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