vdr 2.7.3
thread.c
Go to the documentation of this file.
1/*
2 * thread.c: A simple thread base class
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: thread.c 5.2 2024/01/18 13:01:07 kls Exp $
8 */
9
10#include "thread.h"
11#include <cxxabi.h>
12#include <dlfcn.h>
13#include <errno.h>
14#include <execinfo.h>
15#include <linux/unistd.h>
16#include <malloc.h>
17#include <stdarg.h>
18#include <stdlib.h>
19#include <sys/prctl.h>
20#include <sys/resource.h>
21#include <sys/syscall.h>
22#include <sys/time.h>
23#include <sys/wait.h>
24#include <unistd.h>
25#include "tools.h"
26
27#define ABORT { dsyslog("ABORT!"); cBackTrace::BackTrace(); abort(); }
28
29//#define DEBUG_LOCKING // uncomment this line to activate debug output for locking
30#define DEBUG_LOCKSEQ // uncomment this line to activate debug output for invalid locking sequence
31//#define DEBUG_LOCKCALL // uncomment this line to activate caller information with DEBUG_LOCKSEQ (WARNING: expensive operation, use only when actually debugging the locking sequence!)
32
33#ifdef DEBUG_LOCKING
34#define dbglocking(a...) fprintf(stderr, a)
35#else
36#define dbglocking(a...)
37#endif
38
39static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
40{
41 struct timeval now;
42 if (gettimeofday(&now, NULL) == 0) { // get current time
43 now.tv_sec += MillisecondsFromNow / 1000; // add full seconds
44 now.tv_usec += (MillisecondsFromNow % 1000) * 1000; // add microseconds
45 if (now.tv_usec >= 1000000) { // take care of an overflow
46 now.tv_sec++;
47 now.tv_usec -= 1000000;
48 }
49 Abstime->tv_sec = now.tv_sec; // seconds
50 Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
51 return true;
52 }
53 return false;
54}
55
56// --- cCondWait -------------------------------------------------------------
57
59{
60 signaled = false;
61 pthread_mutex_init(&mutex, NULL);
62 pthread_cond_init(&cond, NULL);
63}
64
66{
67 pthread_cond_broadcast(&cond); // wake up any sleepers
68 pthread_cond_destroy(&cond);
69 pthread_mutex_destroy(&mutex);
70}
71
72void cCondWait::SleepMs(int TimeoutMs)
73{
74 cCondWait w;
75 w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
76}
77
78bool cCondWait::Wait(int TimeoutMs)
79{
80 pthread_mutex_lock(&mutex);
81 if (!signaled) {
82 if (TimeoutMs) {
83 struct timespec abstime;
84 if (GetAbsTime(&abstime, TimeoutMs)) {
85 while (!signaled) {
86 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
87 break;
88 }
89 }
90 }
91 else
92 pthread_cond_wait(&cond, &mutex);
93 }
94 bool r = signaled;
95 signaled = false;
96 pthread_mutex_unlock(&mutex);
97 return r;
98}
99
101{
102 pthread_mutex_lock(&mutex);
103 signaled = true;
104 pthread_cond_broadcast(&cond);
105 pthread_mutex_unlock(&mutex);
106}
107
108// --- cCondVar --------------------------------------------------------------
109
111{
112 pthread_cond_init(&cond, 0);
113}
114
116{
117 pthread_cond_broadcast(&cond); // wake up any sleepers
118 pthread_cond_destroy(&cond);
119}
120
122{
123 if (Mutex.locked) {
124 int locked = Mutex.locked;
125 Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
126 // does an implicit unlock of the mutex
127 pthread_cond_wait(&cond, &Mutex.mutex);
128 Mutex.locked = locked;
129 }
130}
131
132bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
133{
134 bool r = true; // true = condition signaled, false = timeout
135
136 if (Mutex.locked) {
137 struct timespec abstime;
138 if (GetAbsTime(&abstime, TimeoutMs)) {
139 int locked = Mutex.locked;
140 Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
141 // does an implicit unlock of the mutex.
142 if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
143 r = false;
144 Mutex.locked = locked;
145 }
146 }
147 return r;
148}
149
151{
152 pthread_cond_broadcast(&cond);
153}
154
155// --- cRwLock ---------------------------------------------------------------
156
157cRwLock::cRwLock(bool PreferWriter)
158{
159 locked = 0;
161 pthread_rwlockattr_t attr;
162 pthread_rwlockattr_init(&attr);
163 pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
164 pthread_rwlock_init(&rwlock, &attr);
165}
166
168{
169 pthread_rwlock_destroy(&rwlock);
170}
171
172bool cRwLock::Lock(bool Write, int TimeoutMs)
173{
174 int Result = 0;
175 struct timespec abstime;
176 if (TimeoutMs) {
177 if (!GetAbsTime(&abstime, TimeoutMs))
178 TimeoutMs = 0;
179 }
180 if (Write) {
181 Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
182 if (Result == 0)
184 }
185 else if (writeLockThreadId == cThread::ThreadId()) {
186 locked++; // there can be any number of stacked read locks, so we keep track here
187 Result = 0; // acquiring a read lock while holding a write lock within the same thread is OK
188 }
189 else
190 Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
191 return Result == 0;
192}
193
195{
196 if (writeLockThreadId == cThread::ThreadId()) { // this is the thread that obtained the initial write lock
197 if (locked) { // this is the unlock of a read lock within the write lock
198 locked--;
199 return;
200 }
201 }
203 pthread_rwlock_unlock(&rwlock);
204}
205
206// --- cMutex ----------------------------------------------------------------
207
209{
210 locked = 0;
211 pthread_mutexattr_t attr;
212 pthread_mutexattr_init(&attr);
213 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
214 pthread_mutex_init(&mutex, &attr);
215}
216
218{
219 pthread_mutex_destroy(&mutex);
220}
221
222void cMutex::Lock(void)
223{
224 pthread_mutex_lock(&mutex);
225 locked++;
226}
227
229{
230 if (!--locked)
231 pthread_mutex_unlock(&mutex);
232}
233
234// --- cThread ---------------------------------------------------------------
235
237
238cThread::cThread(const char *Description, bool LowPriority)
239{
240 active = running = false;
241 childTid = 0;
242 childThreadId = 0;
243 description = NULL;
244 if (Description)
245 SetDescription("%s", Description);
246 lowPriority = LowPriority;
247}
248
250{
251 Cancel(); // just in case the derived class didn't call it
252 free(description);
253}
254
255void cThread::SetPriority(int Priority)
256{
257 if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
258 LOG_ERROR;
259}
260
261void cThread::SetIOPriority(int Priority)
262{
263 if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0) // idle class
264 LOG_ERROR;
265}
266
267void cThread::SetDescription(const char *Description, ...)
268{
269 free(description);
270 description = NULL;
271 if (Description) {
272 va_list ap;
273 va_start(ap, Description);
274 description = strdup(cString::vsprintf(Description, ap));
275 va_end(ap);
276 }
277}
278
280{
281 Thread->childThreadId = ThreadId();
282 if (Thread->description) {
283 dsyslog("%s thread started (pid=%d, tid=%d, prio=%s)", Thread->description, getpid(), Thread->childThreadId, Thread->lowPriority ? "low" : "high");
284#ifdef PR_SET_NAME
285 if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
286 esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
287#endif
288 }
289 if (Thread->lowPriority) {
290 Thread->SetPriority(19);
291 Thread->SetIOPriority(7);
292 }
293 Thread->Action();
294 if (Thread->description)
295 dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
296 Thread->running = false;
297 Thread->active = false;
298 return NULL;
299}
300
301#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
302#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
303
305{
306 if (!running) {
307 if (active) {
308 // Wait until the previous incarnation of this thread has completely ended
309 // before starting it newly:
310 cTimeMs RestartTimeout;
311 while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
313 }
314 if (!active) {
315 active = running = true;
316 if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
317 pthread_detach(childTid); // auto-reap
318 }
319 else {
320 LOG_ERROR;
321 active = running = false;
322 return false;
323 }
324 }
325 }
326 return true;
327}
328
330{
331 if (active) {
332 //
333 // Single UNIX Spec v2 says:
334 //
335 // The pthread_kill() function is used to request
336 // that a signal be delivered to the specified thread.
337 //
338 // As in kill(), if sig is zero, error checking is
339 // performed but no signal is actually sent.
340 //
341 int err;
342 if ((err = pthread_kill(childTid, 0)) != 0) {
343 if (err != ESRCH)
344 LOG_ERROR;
345 childTid = 0;
346 active = running = false;
347 }
348 else
349 return true;
350 }
351 return false;
352}
353
354void cThread::Cancel(int WaitSeconds)
355{
356 running = false;
357 if (active && WaitSeconds > -1) {
358 if (WaitSeconds > 0) {
359 for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
360 if (!Active())
361 return;
363 }
364 esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
365 }
366 pthread_cancel(childTid);
367 childTid = 0;
368 active = false;
369 }
370}
371
373{
374 return syscall(__NR_gettid);
375}
376
378{
379 if (mainThreadId == 0)
381 else
382 esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
383}
384
385// --- cMutexLock ------------------------------------------------------------
386
388{
389 mutex = NULL;
390 locked = false;
391 Lock(Mutex);
392}
393
395{
396 if (mutex && locked)
397 mutex->Unlock();
398}
399
401{
402 if (Mutex && !mutex) {
403 mutex = Mutex;
404 Mutex->Lock();
405 locked = true;
406 return true;
407 }
408 return false;
409}
410
411// --- cThreadLock -----------------------------------------------------------
412
414{
415 thread = NULL;
416 locked = false;
417 Lock(Thread);
418}
419
421{
422 if (thread && locked)
423 thread->Unlock();
424}
425
427{
428 if (Thread && !thread) {
429 thread = Thread;
430 Thread->Lock();
431 locked = true;
432 return true;
433 }
434 return false;
435}
436
437// --- cBackTrace ------------------------------------------------------------
438
439#define BT_BUF_SIZE 100
440
442{
443 char *Module = s;
444 char *Function = NULL;
445 char *Offset = NULL;
446 char *Address = NULL;
447 // separate the string:
448 for (char *q = Module; *q; q++) {
449 if (*q == '(') {
450 *q = 0;
451 Function = q + 1;
452 }
453 else if (*q == '+') {
454 *q = 0;
455 Offset = q + 1;
456 }
457 else if (*q == ')')
458 *q = 0;
459 else if (*q == '[')
460 Address = q + 1;
461 else if (*q == ']') {
462 *q = 0;
463 break;
464 }
465 }
466 // demangle the function name:
467 char *DemangledFunction = NULL;
468 if (Function) {
469 int status;
470 DemangledFunction = abi::__cxa_demangle(Function, NULL, 0, &status);
471 if (DemangledFunction)
472 Function = DemangledFunction;
473 if (!*Function)
474 Function = NULL;
475 }
476 cString d = cString::sprintf("%s%s%s", Module, Function ? " " : "", Function ? Function : "");
477 // convert string address to numbers:
478 unsigned long long addr = Address ? strtoull(Address, NULL, 0) : 0;
479 unsigned long long offs = Offset ? strtoull(Offset, NULL, 0) : 0;
480 // for shared libraries we need get the offset inside the library:
481 if (Function) {
482 // check whether the module name ends with ".so*":
483 char *e = Module;
484 char *p = NULL;
485 while (e = strstr(e, ".so"))
486 p = e++;
487 if (p && !strchr(p, '/')) {
488 Dl_info dlinfo;
489 if (dladdr(reinterpret_cast<void*>(addr), &dlinfo)) {
490 if ((strcmp(Module, dlinfo.dli_fname) == 0) && dlinfo.dli_fbase) {
491 unsigned long long base = reinterpret_cast<unsigned long long>(dlinfo.dli_fbase);
492 addr -= base;
493 addr &= 0x0FFFFFFFF; // to make it work on both 32 and 64 bit systems
494 }
495 }
496 }
497 }
498 // determine the file name and line number:
499 cString cmd = cString::sprintf("addr2line --functions --demangle --inlines --basename --exe=%s 0x%llx", Module, Function ? addr : offs);
500 cPipe p;
501 if (p.Open(cmd, "r")) {
502 int n = 0;
503 cReadLine rl;
504 while (char *l = rl.Read(p)) {
505 if (n == 0) {
506 if (Function && strcmp(l, Function))
507 d = cString::sprintf("%s calling %s", *d, l);
508 }
509 else
510 d = cString::sprintf("%s at %s", *d, l);
511 n++;
512 }
513 p.Close();
514 }
515 free(DemangledFunction);
516 return d;
517}
518
519void cBackTrace::BackTrace(cStringList &StringList, int Level, bool Mangled)
520{
521 void *b[BT_BUF_SIZE];
522 int n = backtrace(b, BT_BUF_SIZE);
523 if (char **s = backtrace_symbols(b, n)) {
524 for (int i = max(Level, 0) + 1; i < n; i++) // 1 is the call to this function itself
525 StringList.Append(strdup(Mangled ? s[i] : *Demangle(s[i])));
526 free(s);
527 }
528}
529
530void cBackTrace::BackTrace(FILE *f, int Level, bool Mangled)
531{
532 cStringList sl;
533 BackTrace(sl, Level + 1, Mangled); // 1 is the call to this function itself
534 for (int i = 0; i < sl.Size(); i++) {
535 if (f)
536 fprintf(f, "%s\n", sl[i]);
537 else
538 dsyslog("%s", sl[i]);
539 }
540}
541
542cString cBackTrace::GetCaller(int Level, bool Mangled)
543{
544 cString Caller;
545 Level = max(Level, 0) + 1; // 1 is the call to this function itself
546 void *b[BT_BUF_SIZE];
547 int n = backtrace(b, BT_BUF_SIZE);
548 if (char **s = backtrace_symbols(b, n)) {
549 if (Level < n)
550 Caller = Mangled ? s[Level] : *Demangle(s[Level]);
551 free(s);
552 }
553 return Caller;
554}
555
556// --- cStateLockLog ---------------------------------------------------------
557
558#ifdef DEBUG_LOCKSEQ
559#define SLL_SIZE 20 // the number of log entries
560#define SLL_LENGTH 512 // the maximum length of log entries
561#define SLL_THREADS 20 // the maximum number of threads holding locks at the same time (typically well below 10)
562#define SLL_MAX_LIST 9 // max. number of lists to log
563#define SLL_WRITE_FLAG 0x80000000
564#define SLL_LOCK_FLAG 0x40000000
565
567private:
574#ifdef DEBUG_LOCKCALL
575 char logCaller[SLL_SIZE][SLL_LENGTH];
576#endif
578 bool dumped;
579 void Dump(const char *Name, tThreadId ThreadId);
580public:
581 cStateLockLog(void);
582 void Check(const char *Name, bool Lock, bool Write = false);
583 };
584
586{
587 memset(logThreadIds, 0, sizeof(logThreadIds));
588 memset(logFlags, 0, sizeof(logFlags));
589 memset(logCounter, 0, sizeof(logCounter));
590#ifdef DEBUG_LOCKCALL
591 memset(logCaller, 0, sizeof(logCaller));
592#endif
593 logIndex = 0;
594 dumped = false;
595}
596
597void cStateLockLog::Dump(const char *Name, tThreadId ThreadId)
598{
599 dsyslog("--- begin invalid lock sequence report");
600 int LastFlags = 0;
601 for (int i = 0; i < SLL_SIZE; i++) {
602 if (tThreadId tid = logThreadIds[logIndex]) {
603 char msg[SLL_LENGTH];
604 char *q = msg;
605 q += sprintf(q, "%5d", tid);
606 int Flags = logFlags[logIndex];
607 bool Write = Flags & SLL_WRITE_FLAG;
608 bool Lock = Flags & SLL_LOCK_FLAG;
609 Flags &= ~(SLL_WRITE_FLAG | SLL_LOCK_FLAG);
610 int Changed = LastFlags ^ Flags;
611 LastFlags = Flags;
612 for (int i = 0; i <= SLL_MAX_LIST; i++) {
613 char c = '-';
614 int b = 1 << i;
615 if ((Flags & b) != 0)
616 c = '*';
617 if ((Changed & b) != 0)
618 c = Lock ? Write ? 'W' : 'R' : 'U';
619 q += sprintf(q, " %c", c);
620 }
621 q += sprintf(q, " %c", Lock ? 'L' : 'U');
622#ifdef DEBUG_LOCKCALL
623 if (*logCaller[logIndex]) {
624 *q++ = ' ';
625 strn0cpy(q, *cBackTrace::Demangle(logCaller[logIndex]), sizeof(msg) - (q - msg));
626 }
627#endif
628 dsyslog("%s", msg);
629 }
630 if (++logIndex >= SLL_SIZE)
631 logIndex = 0;
632 }
633 dsyslog("%5d invalid lock sequence: %s", ThreadId, Name);
634 dsyslog("full backtrace:");
635 cBackTrace::BackTrace(NULL, 2);
636 dsyslog("--- end invalid lock sequence report");
637 dsyslog("--- THERE WILL BE NO FURTHER REPORTS UNTIL VDR IS RESTARTED!");
638 fprintf(stderr, "invalid lock sequence at %s\n", *DayDateTime(time(NULL)));
639}
640
641void cStateLockLog::Check(const char *Name, bool Lock, bool Write)
642{
643 if (!dumped && Name) {
644 int n = *Name - '0' - 1;
645 if (0 <= n && n < SLL_MAX_LIST) {
646 int b = 1 << n;
647 cMutexLock MutexLock(&mutex);
648 tThreadId ThreadId = cThread::ThreadId();
649 int Index = -1;
650 int AvailableIndex = -1;
651 for (int i = 0; i < threadIds.Size(); i++) {
652 if (ThreadId == threadIds[i]) {
653 Index = i;
654 break;
655 }
656 if (threadIds[i] == 0)
657 AvailableIndex = i;
658 }
659 if (Index < 0) {
660 if (AvailableIndex < 0) {
661 Index = threadIds.Size();
662 threadIds.Append(ThreadId);
663 flags.Append(0);
664 }
665 else {
666 Index = AvailableIndex;
667 threadIds[Index] = ThreadId;
668 }
669 }
670 if (Index >= SLL_THREADS) {
671 // should never happen!
672 esyslog("ERROR: too many threads holding list locks at the same time - stopped logging locks!");
673 dumped = true;
674 return;
675 }
676 bool DoDump = false;
677 if (Lock) {
678 if ((flags[Index] & ~b) < b) // thread holds only "smaller" locks -> OK
679 ;
680 else if ((flags[Index] & b) == 0) // thread already holds "bigger" locks, so it may only re-lock one that it already has!
681 DoDump = true;
682 logCounter[Index][n]++;
683 flags[Index] |= b;
684 }
685 else if (--logCounter[Index][n] == 0)
686 flags[Index] &= ~b;
687 logThreadIds[logIndex] = ThreadId;
688 logFlags[logIndex] = flags[Index] | (Write ? SLL_WRITE_FLAG : 0) | (Lock ? SLL_LOCK_FLAG : 0);
689 if (flags[Index] == 0)
690 threadIds[Index] = 0;
691#ifdef DEBUG_LOCKCALL
692 strn0cpy(logCaller[logIndex], cBackTrace::GetCaller(Lock ? 3 : 5, true), SLL_LENGTH);
693#endif
694 if (++logIndex >= SLL_SIZE)
695 logIndex = 0;
696 if (DoDump) {
697 Dump(Name, ThreadId);
698 dumped = true;
699 }
700 }
701 }
702}
703
705
706#define dbglockseq(n, l, w) StateLockLog.Check(n, l, w)
707#else
708#define dbglockseq(n, l, w)
709#endif // DEBUG_LOCKSEQ
710
711// --- cStateLock ------------------------------------------------------------
712
713cStateLock::cStateLock(const char *Name)
714:rwLock(true)
715{
716 name = Name;
717 threadId = 0;
718 state = 0;
720 syncStateKey = NULL;
721}
722
723bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs)
724{
725 dbglocking("%5d %-12s %10p lock state = %d/%d write = %d timeout = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, Write, TimeoutMs);
726 StateKey.timedOut = false;
727 if (StateKey.stateLock) {
728 esyslog("ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s)", StateKey.stateLock->threadId, name);
729 ABORT;
730 return false;
731 }
732 if (rwLock.Lock(Write, TimeoutMs)) {
733 dbglockseq(name, true, Write);
734 StateKey.stateLock = this;
735 if (Write) {
736 dbglocking("%5d %-12s %10p locked write\n", cThread::ThreadId(), name, &StateKey);
738 StateKey.write = true;
739 return true;
740 }
741 else if (state != StateKey.state) {
742 dbglocking("%5d %-12s %10p locked read\n", cThread::ThreadId(), name, &StateKey);
743 return true;
744 }
745 else {
746 dbglocking("%5d %-12s %10p state unchanged\n", cThread::ThreadId(), name, &StateKey);
747 StateKey.stateLock = NULL;
748 dbglockseq(name, false, false);
749 rwLock.Unlock();
750 }
751 }
752 else if (TimeoutMs) {
753 dbglocking("%5d %-12s %10p timeout\n", cThread::ThreadId(), name, &StateKey);
754 StateKey.timedOut = true;
755 }
756 else if (threadId == cThread::ThreadId()) {
757 static bool DoubleWriteLockReported = false;
758 if (!DoubleWriteLockReported) {
759 dsyslog("WARNING: attempt to acquire write lock while already holding a write lock in the same thread - this may crash! (backtrace follows)");
761 DoubleWriteLockReported = true;
762 }
763 }
764 return false;
765}
766
767void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
768{
769 dbglocking("%5d %-12s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState);
770 if (StateKey.stateLock != this) {
771 esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name);
772 ABORT;
773 return;
774 }
775 if (StateKey.write && threadId != cThread::ThreadId()) {
776 esyslog("ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
777 ABORT;
778 return;
779 }
780 if (StateKey.write && (IncState && explicitModify != emArmed || explicitModify == emEnabled)) {
781 if (syncStateKey && syncStateKey->state == state)
782 syncStateKey->state++;
783 state++;
784 }
785 StateKey.state = state;
786 StateKey.stateLock = NULL;
787 if (StateKey.write) {
788 StateKey.write = false;
789 threadId = 0;
791 syncStateKey = NULL;
792 }
793 dbglockseq(name, false, false);
794 rwLock.Unlock();
795}
796
798{
799 dbglocking("%5d %-12s %10p SetSyncStateKey\n", cThread::ThreadId(), name, &StateKey);
800 if (threadId != cThread::ThreadId()) {
801 esyslog("ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
802 ABORT;
803 return;
804 }
805 if (StateKey.stateLock == this) {
806 esyslog("ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)", threadId, name);
807 ABORT;
808 return;
809 }
810 if (syncStateKey) {
811 esyslog("ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)", threadId, name);
812 ABORT;
813 return;
814 }
815 syncStateKey = &StateKey;
816}
817
819{
820 if (threadId != cThread::ThreadId()) {
821 esyslog("ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
822 ABORT;
823 return;
824 }
825 if (explicitModify != emDisabled) {
826 esyslog("ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)", threadId, name);
827 ABORT;
828 return;
829 }
831}
832
834{
835 if (threadId != cThread::ThreadId()) {
836 esyslog("ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
837 ABORT;
838 return;
839 }
841}
842
843// --- cStateKey -------------------------------------------------------------
844
845cStateKey::cStateKey(bool IgnoreFirst)
846{
847 stateLock = NULL;
848 write = false;
849 state = 0;
850 if (!IgnoreFirst)
851 Reset();
852}
853
855{
856 if (stateLock) {
857 esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this);
858 ABORT;
859 }
860}
861
863{
864 state = -1; // lock and key are initialized differently, to make the first check return true
865}
866
867void cStateKey::Remove(bool IncState)
868{
869 if (stateLock)
870 stateLock->Unlock(*this, IncState);
871 else {
872 esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
873 ABORT;
874 }
875}
876
878{
879 if (!stateLock) {
880 esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this);
881 ABORT;
882 }
883 else if (write)
884 return state != stateLock->state;
885 else
886 return true;
887}
888
889// --- cIoThrottle -----------------------------------------------------------
890
892int cIoThrottle::count = 0;
893
895{
896 active = false;
897}
898
903
905{
906 if (!active) {
907 mutex.Lock();
908 count++;
909 active = true;
910 dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
911 mutex.Unlock();
912 }
913}
914
916{
917 if (active) {
918 mutex.Lock();
919 count--;
920 active = false;
921 dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
922 mutex.Unlock();
923 }
924}
925
927{
928 return count > 0;
929}
930
931// --- cPipe -----------------------------------------------------------------
932
933// cPipe::Open() and cPipe::Close() are based on code originally received from
934// Andreas Vitting <Andreas@huji.de>
935
937{
938 pid = -1;
939 f = NULL;
940}
941
943{
944 Close();
945}
946
947bool cPipe::Open(const char *Command, const char *Mode)
948{
949 int fd[2];
950
951 if (pipe(fd) < 0) {
952 LOG_ERROR_STR(Command);
953 return false;
954 }
955 if ((pid = fork()) < 0) { // fork failed
956 LOG_ERROR_STR(Command);
957 close(fd[0]);
958 close(fd[1]);
959 return false;
960 }
961
962 const char *mode = "w";
963 int iopipe = 0;
964
965 if (pid > 0) { // parent process
966 if (strcmp(Mode, "r") == 0) {
967 mode = "r";
968 iopipe = 1;
969 }
970 close(fd[iopipe]);
971 if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
972 LOG_ERROR_STR(Command);
973 close(fd[1 - iopipe]);
974 }
975 return f != NULL;
976 }
977 else { // child process
978 int iofd = STDOUT_FILENO;
979 if (strcmp(Mode, "w") == 0) {
980 iopipe = 1;
981 iofd = STDIN_FILENO;
982 }
983 close(fd[iopipe]);
984 if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
985 close(fd[1 - iopipe]);
986 _exit(-1);
987 }
988 else {
989 int MaxPossibleFileDescriptors = getdtablesize();
990 for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
991 close(i); //close all dup'ed filedescriptors
992 if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
993 close(fd[1 - iopipe]);
994 _exit(-1);
995 }
996 }
997 _exit(0);
998 }
999}
1000
1002{
1003 int ret = -1;
1004
1005 if (f) {
1006 fclose(f);
1007 f = NULL;
1008 }
1009
1010 if (pid > 0) {
1011 int status = 0;
1012 int i = 5;
1013 while (i > 0) {
1014 ret = waitpid(pid, &status, WNOHANG);
1015 if (ret < 0) {
1016 if (errno != EINTR && errno != ECHILD) {
1017 LOG_ERROR;
1018 break;
1019 }
1020 }
1021 else if (ret == pid)
1022 break;
1023 i--;
1024 cCondWait::SleepMs(100);
1025 }
1026 if (!i) {
1027 kill(pid, SIGKILL);
1028 ret = -1;
1029 }
1030 else if (ret == -1 || !WIFEXITED(status))
1031 ret = -1;
1032 pid = -1;
1033 }
1034
1035 return ret;
1036}
1037
1038// --- SystemExec ------------------------------------------------------------
1039
1040int SystemExec(const char *Command, bool Detached)
1041{
1042 pid_t pid;
1043
1044 if ((pid = fork()) < 0) { // fork failed
1045 LOG_ERROR_STR(Command);
1046 return -1;
1047 }
1048
1049 if (pid > 0) { // parent process
1050 int status = 0;
1051 if (waitpid(pid, &status, 0) < 0) {
1052 LOG_ERROR_STR(Command);
1053 return -1;
1054 }
1055 return status;
1056 }
1057 else { // child process
1058 if (Detached) {
1059 // Fork again and let first child die - grandchild stays alive without parent
1060 if (fork() > 0)
1061 _exit(0);
1062 // Start a new session
1063 pid_t sid = setsid();
1064 if (sid < 0)
1065 _exit(-1);
1066 // close STDIN and re-open as /dev/null
1067 int devnull = open("/dev/null", O_RDONLY);
1068 if (devnull < 0 || dup2(devnull, 0) < 0)
1069 _exit(-1);
1070 }
1071 int MaxPossibleFileDescriptors = getdtablesize();
1072 for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1073 close(i); //close all dup'ed filedescriptors
1074 if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1)
1075 _exit(-1);
1076 _exit(0);
1077 }
1078}
static void BackTrace(cStringList &StringList, int Level=0, bool Mangled=false)
Produces a backtrace and stores it in the given StringList.
Definition thread.c:519
static cString GetCaller(int Level=0, bool Mangled=false)
Returns the caller at the given Level (or the immediate caller, if Level is 0).
Definition thread.c:542
static cString Demangle(char *s)
Demangles the function name in the given string and returns the converted version of s.
Definition thread.c:441
void Wait(cMutex &Mutex)
Definition thread.c:121
cCondVar(void)
Definition thread.c:110
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition thread.c:132
void Broadcast(void)
Definition thread.c:150
pthread_cond_t cond
Definition thread.h:46
~cCondVar()
Definition thread.c:115
pthread_cond_t cond
Definition thread.h:22
bool signaled
Definition thread.h:23
cCondWait(void)
Definition thread.c:58
~cCondWait()
Definition thread.c:65
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0.
Definition thread.c:78
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
Definition thread.c:100
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:72
pthread_mutex_t mutex
Definition thread.h:21
cIoThrottle(void)
Definition thread.c:894
static int count
Definition thread.h:270
void Activate(void)
Activates the global I/O throttling mechanism.
Definition thread.c:904
~cIoThrottle()
Definition thread.c:899
void Release(void)
Releases the global I/O throttling mechanism.
Definition thread.c:915
bool active
Definition thread.h:271
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition thread.c:926
static cMutex mutex
Definition thread.h:269
cMutexLock(cMutex *Mutex=NULL)
Definition thread.c:387
bool Lock(cMutex *Mutex)
Definition thread.c:400
~cMutexLock()
Definition thread.c:394
cMutex * mutex
Definition thread.h:143
bool locked
Definition thread.h:144
void Lock(void)
Definition thread.c:222
pthread_mutex_t mutex
Definition thread.h:70
cMutex(void)
Definition thread.c:208
~cMutex()
Definition thread.c:217
int locked
Definition thread.h:71
void Unlock(void)
Definition thread.c:228
pid_t pid
Definition thread.h:294
int Close(void)
Definition thread.c:1001
FILE * f
Definition thread.h:295
bool Open(const char *Command, const char *Mode)
Definition thread.c:947
cPipe(void)
Definition thread.c:936
~cPipe()
Definition thread.c:942
char * Read(FILE *f)
Definition tools.c:1520
int locked
Definition thread.h:58
tThreadId writeLockThreadId
Definition thread.h:59
pthread_rwlock_t rwlock
Definition thread.h:57
cRwLock(bool PreferWriter=false)
Definition thread.c:157
bool Lock(bool Write, int TimeoutMs=0)
Definition thread.c:172
void Unlock(void)
Definition thread.c:194
~cRwLock()
Definition thread.c:167
cStateLock * stateLock
Definition thread.h:236
cStateKey(bool IgnoreFirst=false)
Sets up a new state key.
Definition thread.c:845
int state
Definition thread.h:238
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition thread.c:867
~cStateKey()
Definition thread.c:854
void Reset(void)
Resets the state of this key, so that the next call to a lock's Lock() function with this key will re...
Definition thread.c:862
bool timedOut
Definition thread.h:239
bool write
Definition thread.h:237
bool StateChanged(void)
Returns true if this key is used for obtaining a write lock, and the lock's state differs from that o...
Definition thread.c:877
cVector< int > flags
Definition thread.c:570
cVector< tThreadId > threadIds
Definition thread.c:569
cStateLockLog(void)
Definition thread.c:585
uint8_t logCounter[SLL_THREADS][SLL_MAX_LIST]
Definition thread.c:573
int logFlags[SLL_SIZE]
Definition thread.c:572
void Check(const char *Name, bool Lock, bool Write=false)
Definition thread.c:641
tThreadId logThreadIds[SLL_SIZE]
Definition thread.c:571
bool dumped
Definition thread.c:578
void Dump(const char *Name, tThreadId ThreadId)
Definition thread.c:597
cMutex mutex
Definition thread.c:568
tThreadId threadId
Definition thread.h:176
const char * name
Definition thread.h:175
cRwLock rwLock
Definition thread.h:177
int state
Definition thread.h:178
void SetExplicitModify(void)
If you have obtained a write lock on this lock, and you don't want its state to be automatically incr...
Definition thread.c:818
cStateLock(const char *Name=NULL)
Definition thread.c:713
friend class cStateKey
Definition thread.h:172
cStateKey * syncStateKey
Definition thread.h:180
int explicitModify
Definition thread.h:179
void Unlock(cStateKey &StateKey, bool IncState=true)
Releases a lock that has been obtained by a previous call to Lock() with the given StateKey.
Definition thread.c:767
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
Definition thread.c:797
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
Definition thread.c:833
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
Definition thread.c:723
@ emDisabled
Definition thread.h:174
@ emEnabled
Definition thread.h:174
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition tools.c:1201
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1188
cThreadLock(cThread *Thread=NULL)
Definition thread.c:413
bool Lock(cThread *Thread)
Definition thread.c:426
bool locked
Definition thread.h:160
~cThreadLock()
Definition thread.c:420
cThread * thread
Definition thread.h:159
virtual ~cThread()
Definition thread.c:249
void SetIOPriority(int Priority)
Definition thread.c:261
static void SetMainThreadId(void)
Definition thread.c:377
virtual void Action(void)=0
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition thread.c:304
void SetDescription(const char *Description,...) __attribute__((format(printf
Definition thread.c:267
void SetPriority(int Priority)
Definition thread.c:255
bool active
Definition thread.h:82
void Lock(void)
Definition thread.h:94
tThreadId childThreadId
Definition thread.h:85
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:238
bool lowPriority
Definition thread.h:88
bool running
Definition thread.h:83
static void * StartThread(cThread *Thread)
Definition thread.c:279
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition thread.c:354
static tThreadId mainThreadId
Definition thread.h:89
pthread_t childTid
Definition thread.h:84
bool Active(void)
Checks whether the thread is still alive.
Definition thread.c:329
static tThreadId ThreadId(void)
Definition thread.c:372
char * description
Definition thread.h:87
uint64_t Elapsed(void) const
Definition tools.c:815
int Size(void) const
Definition tools.h:754
virtual void Append(T Data)
Definition tools.h:774
#define BT_BUF_SIZE
Definition thread.c:439
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
Definition thread.c:39
#define SLL_LENGTH
Definition thread.c:560
#define dbglockseq(n, l, w)
Definition thread.c:706
#define ABORT
Definition thread.c:27
#define SLL_MAX_LIST
Definition thread.c:562
#define THREAD_STOP_SLEEP
Definition thread.c:302
#define SLL_SIZE
Definition thread.c:559
int SystemExec(const char *Command, bool Detached)
Definition thread.c:1040
#define SLL_THREADS
Definition thread.c:561
#define SLL_LOCK_FLAG
Definition thread.c:564
#define SLL_WRITE_FLAG
Definition thread.c:563
#define THREAD_STOP_TIMEOUT
Definition thread.c:301
static cStateLockLog StateLockLog
Definition thread.c:704
#define dbglocking(a...)
Definition thread.c:36
pid_t tThreadId
Definition thread.h:17
cString DayDateTime(time_t t)
Converts the given time to a string of the form "www dd.mm. hh:mm".
Definition tools.c:1253
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:131
#define LOG_ERROR_STR(s)
Definition tools.h:40
#define dsyslog(a...)
Definition tools.h:37
T max(T a, T b)
Definition tools.h:64
#define esyslog(a...)
Definition tools.h:35
#define LOG_ERROR
Definition tools.h:39