vdr  2.7.6
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.4 2025/06/17 20:32:06 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 
39 static 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 
73 void cCondWait::SleepMs(int TimeoutMs)
74 {
75  cCondWait w;
76  w.Wait(TimeoutMs);
77 }
78 
79 bool 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 
133 bool 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 
158 cRwLock::cRwLock(bool PreferWriter)
159 {
160  locked = 0;
161  writeLockThreadId = 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 
173 bool 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 
195 void cRwLock::Unlock(void)
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  }
203  writeLockThreadId = 0;
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 
223 void cMutex::Lock(void)
224 {
225  pthread_mutex_lock(&mutex);
226  locked++;
227 }
228 
229 void cMutex::Unlock(void)
230 {
231  if (!--locked)
232  pthread_mutex_unlock(&mutex);
233 }
234 
235 // --- cThread ---------------------------------------------------------------
236 
238 
239 cThread::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 
256 void cThread::SetPriority(int Priority)
257 {
258  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
259  LOG_ERROR;
260 }
261 
262 void 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 
268 void 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 
305 bool cThread::Start(void)
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 
330 bool cThread::Active(void)
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 
355 void cThread::Cancel(int WaitSeconds)
356 {
357  running = false;
358  if (active && WaitSeconds > -1) {
359  if (WaitSeconds > 0) {
360  for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
361  if (!Active())
362  return;
363  cCondWait::SleepMs(10);
364  }
365  esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
366  }
367  pthread_cancel(childTid);
368  childTid = 0;
369  active = false;
370  }
371 }
372 
374 {
375  return syscall(__NR_gettid);
376 }
377 
379 {
380  if (mainThreadId == 0)
382  else
383  esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
384 }
385 
386 // --- cMutexLock ------------------------------------------------------------
387 
389 {
390  mutex = NULL;
391  locked = false;
392  Lock(Mutex);
393 }
394 
396 {
397  if (mutex && locked)
398  mutex->Unlock();
399 }
400 
402 {
403  if (Mutex && !mutex) {
404  mutex = Mutex;
405  Mutex->Lock();
406  locked = true;
407  return true;
408  }
409  return false;
410 }
411 
412 // --- cThreadLock -----------------------------------------------------------
413 
415 {
416  thread = NULL;
417  locked = false;
418  Lock(Thread);
419 }
420 
422 {
423  if (thread && locked)
424  thread->Unlock();
425 }
426 
428 {
429  if (Thread && !thread) {
430  thread = Thread;
431  Thread->Lock();
432  locked = true;
433  return true;
434  }
435  return false;
436 }
437 
438 // --- cBackTrace ------------------------------------------------------------
439 
440 #define BT_BUF_SIZE 100
441 
443 {
444  char *Module = s;
445  char *Function = NULL;
446  char *Offset = NULL;
447  char *Address = NULL;
448  // separate the string:
449  for (char *q = Module; *q; q++) {
450  if (*q == '(') {
451  *q = 0;
452  Function = q + 1;
453  }
454  else if (*q == '+') {
455  *q = 0;
456  Offset = q + 1;
457  }
458  else if (*q == ')')
459  *q = 0;
460  else if (*q == '[')
461  Address = q + 1;
462  else if (*q == ']') {
463  *q = 0;
464  break;
465  }
466  }
467  // demangle the function name:
468  char *DemangledFunction = NULL;
469  if (Function) {
470  int status;
471  DemangledFunction = abi::__cxa_demangle(Function, NULL, 0, &status);
472  if (DemangledFunction)
473  Function = DemangledFunction;
474  if (!*Function)
475  Function = NULL;
476  }
477  cString d = cString::sprintf("%s%s%s", Module, Function ? " " : "", Function ? Function : "");
478  // convert string address to numbers:
479  unsigned long long addr = Address ? strtoull(Address, NULL, 0) : 0;
480  unsigned long long offs = Offset ? strtoull(Offset, NULL, 0) : 0;
481  // for shared libraries we need get the offset inside the library:
482  if (Function) {
483  // check whether the module name ends with ".so*":
484  char *e = Module;
485  char *p = NULL;
486  while (e = strstr(e, ".so"))
487  p = e++;
488  if (p && !strchr(p, '/')) {
489  Dl_info dlinfo;
490  if (dladdr(reinterpret_cast<void*>(addr), &dlinfo)) {
491  if ((strcmp(Module, dlinfo.dli_fname) == 0) && dlinfo.dli_fbase) {
492  unsigned long long base = reinterpret_cast<unsigned long long>(dlinfo.dli_fbase);
493  addr -= base;
494  addr &= 0x0FFFFFFFF; // to make it work on both 32 and 64 bit systems
495  }
496  }
497  }
498  }
499  // determine the file name and line number:
500  cString cmd = cString::sprintf("addr2line --functions --demangle --inlines --basename --exe=%s 0x%llx", Module, Function ? addr : offs);
501  cPipe p;
502  if (p.Open(cmd, "r")) {
503  int n = 0;
504  cReadLine rl;
505  while (char *l = rl.Read(p)) {
506  if (n == 0) {
507  if (Function && strcmp(l, Function))
508  d = cString::sprintf("%s calling %s", *d, l);
509  }
510  else
511  d = cString::sprintf("%s at %s", *d, l);
512  n++;
513  }
514  p.Close();
515  }
516  free(DemangledFunction);
517  return d;
518 }
519 
520 void cBackTrace::BackTrace(cStringList &StringList, int Level, bool Mangled)
521 {
522  void *b[BT_BUF_SIZE];
523  int n = backtrace(b, BT_BUF_SIZE);
524  if (char **s = backtrace_symbols(b, n)) {
525  for (int i = max(Level, 0) + 1; i < n; i++) // 1 is the call to this function itself
526  StringList.Append(strdup(Mangled ? s[i] : *Demangle(s[i])));
527  free(s);
528  }
529 }
530 
531 void cBackTrace::BackTrace(FILE *f, int Level, bool Mangled)
532 {
533  cStringList sl;
534  BackTrace(sl, Level + 1, Mangled); // 1 is the call to this function itself
535  for (int i = 0; i < sl.Size(); i++) {
536  if (f)
537  fprintf(f, "%s\n", sl[i]);
538  else
539  dsyslog("%s", sl[i]);
540  }
541 }
542 
543 cString cBackTrace::GetCaller(int Level, bool Mangled)
544 {
545  cString Caller;
546  Level = max(Level, 0) + 1; // 1 is the call to this function itself
547  void *b[BT_BUF_SIZE];
548  int n = backtrace(b, BT_BUF_SIZE);
549  if (char **s = backtrace_symbols(b, n)) {
550  if (Level < n)
551  Caller = Mangled ? s[Level] : *Demangle(s[Level]);
552  free(s);
553  }
554  return Caller;
555 }
556 
557 // --- cStateLockLog ---------------------------------------------------------
558 
559 #ifdef DEBUG_LOCKSEQ
560 #define SLL_SIZE 20 // the number of log entries
561 #define SLL_LENGTH 512 // the maximum length of log entries
562 #define SLL_THREADS 20 // the maximum number of threads holding locks at the same time (typically well below 10)
563 #define SLL_MAX_LIST 9 // max. number of lists to log
564 #define SLL_WRITE_FLAG 0x80000000
565 #define SLL_LOCK_FLAG 0x40000000
566 
568 private:
575 #ifdef DEBUG_LOCKCALL
576  char logCaller[SLL_SIZE][SLL_LENGTH];
577 #endif
578  int logIndex;
579  bool dumped;
580  void Dump(const char *Name, tThreadId ThreadId);
581 public:
582  cStateLockLog(void);
583  void Check(const char *Name, bool Lock, bool Write = false);
584  };
585 
587 {
588  memset(logThreadIds, 0, sizeof(logThreadIds));
589  memset(logFlags, 0, sizeof(logFlags));
590  memset(logCounter, 0, sizeof(logCounter));
591 #ifdef DEBUG_LOCKCALL
592  memset(logCaller, 0, sizeof(logCaller));
593 #endif
594  logIndex = 0;
595  dumped = false;
596 }
597 
598 void cStateLockLog::Dump(const char *Name, tThreadId ThreadId)
599 {
600  dsyslog("--- begin invalid lock sequence report");
601  dsyslog("TID T C R DR S ST");
602  int LastFlags = 0;
603  for (int i = 0; i < SLL_SIZE; i++) {
604  if (tThreadId tid = logThreadIds[logIndex]) {
605  char msg[SLL_LENGTH];
606  char *q = msg;
607  q += sprintf(q, "%5d", tid);
608  int Flags = logFlags[logIndex];
609  bool Write = Flags & SLL_WRITE_FLAG;
610  bool Lock = Flags & SLL_LOCK_FLAG;
611  Flags &= ~(SLL_WRITE_FLAG | SLL_LOCK_FLAG);
612  int Changed = LastFlags ^ Flags;
613  LastFlags = Flags;
614  for (int i = 0; i <= SLL_MAX_LIST; i++) {
615  char c = '-';
616  int b = 1 << i;
617  if ((Flags & b) != 0)
618  c = '*';
619  if ((Changed & b) != 0)
620  c = Lock ? Write ? 'W' : 'R' : 'U';
621  q += sprintf(q, " %c", c);
622  }
623  q += sprintf(q, " %c", Lock ? 'L' : 'U');
624 #ifdef DEBUG_LOCKCALL
625  if (*logCaller[logIndex]) {
626  *q++ = ' ';
627  strn0cpy(q, *cBackTrace::Demangle(logCaller[logIndex]), sizeof(msg) - (q - msg));
628  }
629 #endif
630  dsyslog("%s", msg);
631  }
632  if (++logIndex >= SLL_SIZE)
633  logIndex = 0;
634  }
635  dsyslog("%5d invalid lock sequence: %s", ThreadId, Name);
636  dsyslog("full backtrace:");
637  cBackTrace::BackTrace(NULL, 2);
638  dsyslog("--- end invalid lock sequence report");
639  dsyslog("--- THERE WILL BE NO FURTHER REPORTS UNTIL VDR IS RESTARTED!");
640  fprintf(stderr, "invalid lock sequence at %s\n", *DayDateTime(time(NULL)));
641 }
642 
643 void cStateLockLog::Check(const char *Name, bool Lock, bool Write)
644 {
645  if (!dumped && Name) {
646  int n = *Name - '0' - 1;
647  if (0 <= n && n < SLL_MAX_LIST) {
648  int b = 1 << n;
649  cMutexLock MutexLock(&mutex);
650  tThreadId ThreadId = cThread::ThreadId();
651  int Index = -1;
652  int AvailableIndex = -1;
653  for (int i = 0; i < threadIds.Size(); i++) {
654  if (ThreadId == threadIds[i]) {
655  Index = i;
656  break;
657  }
658  if (threadIds[i] == 0)
659  AvailableIndex = i;
660  }
661  if (Index < 0) {
662  if (AvailableIndex < 0) {
663  Index = threadIds.Size();
664  threadIds.Append(ThreadId);
665  flags.Append(0);
666  }
667  else {
668  Index = AvailableIndex;
669  threadIds[Index] = ThreadId;
670  }
671  }
672  if (Index >= SLL_THREADS) {
673  // should never happen!
674  esyslog("ERROR: too many threads holding list locks at the same time - stopped logging locks!");
675  dumped = true;
676  return;
677  }
678  bool DoDump = false;
679  if (Lock) {
680  if ((flags[Index] & ~b) < b) // thread holds only "smaller" locks -> OK
681  ;
682  else if ((flags[Index] & b) == 0) // thread already holds "bigger" locks, so it may only re-lock one that it already has!
683  DoDump = true;
684  logCounter[Index][n]++;
685  flags[Index] |= b;
686  }
687  else if (--logCounter[Index][n] == 0)
688  flags[Index] &= ~b;
689  logThreadIds[logIndex] = ThreadId;
690  logFlags[logIndex] = flags[Index] | (Write ? SLL_WRITE_FLAG : 0) | (Lock ? SLL_LOCK_FLAG : 0);
691  if (flags[Index] == 0)
692  threadIds[Index] = 0;
693 #ifdef DEBUG_LOCKCALL
694  strn0cpy(logCaller[logIndex], cBackTrace::GetCaller(Lock ? 3 : 5, true), SLL_LENGTH);
695 #endif
696  if (++logIndex >= SLL_SIZE)
697  logIndex = 0;
698  if (DoDump) {
699  Dump(Name, ThreadId);
700  dumped = true;
701  }
702  }
703  }
704 }
705 
707 
708 #define dbglockseq(n, l, w) StateLockLog.Check(n, l, w)
709 #else
710 #define dbglockseq(n, l, w)
711 #endif // DEBUG_LOCKSEQ
712 
713 // --- cStateLock ------------------------------------------------------------
714 
715 cStateLock::cStateLock(const char *Name)
716 :rwLock(true)
717 {
718  name = Name;
719  threadId = 0;
720  state = 0;
722  syncStateKey = NULL;
723 }
724 
725 bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs)
726 {
727  dbglocking("%5d %-12s %10p lock state = %d/%d write = %d timeout = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, Write, TimeoutMs);
728  StateKey.timedOut = false;
729  if (StateKey.stateLock) {
730  esyslog("ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s)", StateKey.stateLock->threadId, name);
731  ABORT;
732  return false;
733  }
734  if (rwLock.Lock(Write, TimeoutMs)) {
735  dbglockseq(name, true, Write);
736  StateKey.stateLock = this;
737  if (Write) {
738  dbglocking("%5d %-12s %10p locked write\n", cThread::ThreadId(), name, &StateKey);
740  StateKey.write = true;
741  return true;
742  }
743  else if (state != StateKey.state) {
744  dbglocking("%5d %-12s %10p locked read\n", cThread::ThreadId(), name, &StateKey);
745  return true;
746  }
747  else {
748  dbglocking("%5d %-12s %10p state unchanged\n", cThread::ThreadId(), name, &StateKey);
749  StateKey.stateLock = NULL;
750  dbglockseq(name, false, false);
751  rwLock.Unlock();
752  }
753  }
754  else if (TimeoutMs) {
755  dbglocking("%5d %-12s %10p timeout\n", cThread::ThreadId(), name, &StateKey);
756  StateKey.timedOut = true;
757  }
758  else if (threadId == cThread::ThreadId()) {
759  static bool DoubleWriteLockReported = false;
760  if (!DoubleWriteLockReported) {
761  dsyslog("WARNING: attempt to acquire write lock while already holding a write lock in the same thread - this may crash! (backtrace follows)");
763  DoubleWriteLockReported = true;
764  }
765  }
766  return false;
767 }
768 
769 void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
770 {
771  dbglocking("%5d %-12s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState);
772  if (StateKey.stateLock != this) {
773  esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name);
774  ABORT;
775  return;
776  }
777  if (StateKey.write && threadId != cThread::ThreadId()) {
778  esyslog("ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
779  ABORT;
780  return;
781  }
782  if (StateKey.write && (IncState && explicitModify != emArmed || explicitModify == emEnabled)) {
784  syncStateKey->state++;
785  state++;
786  }
787  StateKey.state = state;
788  StateKey.stateLock = NULL;
789  if (StateKey.write) {
790  StateKey.write = false;
791  threadId = 0;
793  syncStateKey = NULL;
794  }
795  dbglockseq(name, false, false);
796  rwLock.Unlock();
797 }
798 
800 {
801  dbglocking("%5d %-12s %10p SetSyncStateKey\n", cThread::ThreadId(), name, &StateKey);
802  if (threadId != cThread::ThreadId()) {
803  esyslog("ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
804  ABORT;
805  return;
806  }
807  if (StateKey.stateLock == this) {
808  esyslog("ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)", threadId, name);
809  ABORT;
810  return;
811  }
812  if (syncStateKey) {
813  esyslog("ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)", threadId, name);
814  ABORT;
815  return;
816  }
817  syncStateKey = &StateKey;
818 }
819 
821 {
822  if (threadId != cThread::ThreadId()) {
823  esyslog("ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
824  ABORT;
825  return;
826  }
827  if (explicitModify != emDisabled) {
828  esyslog("ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)", threadId, name);
829  ABORT;
830  return;
831  }
833 }
834 
836 {
837  if (threadId != cThread::ThreadId()) {
838  esyslog("ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
839  ABORT;
840  return;
841  }
843 }
844 
845 // --- cStateKey -------------------------------------------------------------
846 
847 cStateKey::cStateKey(bool IgnoreFirst)
848 {
849  stateLock = NULL;
850  write = false;
851  state = 0;
852  if (!IgnoreFirst)
853  Reset();
854 }
855 
857 {
858  if (stateLock) {
859  esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this);
860  ABORT;
861  }
862 }
863 
865 {
866  state = -1; // lock and key are initialized differently, to make the first check return true
867 }
868 
869 void cStateKey::Remove(bool IncState)
870 {
871  if (stateLock)
872  stateLock->Unlock(*this, IncState);
873  else {
874  esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
875  ABORT;
876  }
877 }
878 
880 {
881  if (!stateLock) {
882  esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this);
883  ABORT;
884  }
885  else if (write)
886  return state != stateLock->state;
887  else
888  return true;
889 }
890 
891 // --- cIoThrottle -----------------------------------------------------------
892 
894 int cIoThrottle::count = 0;
895 
897 {
898  active = false;
899 }
900 
902 {
903  Release();
904 }
905 
907 {
908  if (!active) {
909  mutex.Lock();
910  count++;
911  active = true;
912  dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
913  mutex.Unlock();
914  }
915 }
916 
918 {
919  if (active) {
920  mutex.Lock();
921  count--;
922  active = false;
923  dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
924  mutex.Unlock();
925  }
926 }
927 
929 {
930  return count > 0;
931 }
932 
933 // --- cPipe -----------------------------------------------------------------
934 
935 // cPipe::Open() and cPipe::Close() are based on code originally received from
936 // Andreas Vitting <Andreas@huji.de>
937 
939 {
940  pid = -1;
941  f = NULL;
942 }
943 
945 {
946  Close();
947 }
948 
949 bool cPipe::Open(const char *Command, const char *Mode)
950 {
951  int fd[2];
952 
953  if (pipe(fd) < 0) {
954  LOG_ERROR_STR(Command);
955  return false;
956  }
957  if ((pid = fork()) < 0) { // fork failed
958  LOG_ERROR_STR(Command);
959  close(fd[0]);
960  close(fd[1]);
961  return false;
962  }
963 
964  const char *mode = "w";
965  int iopipe = 0;
966 
967  if (pid > 0) { // parent process
968  if (strcmp(Mode, "r") == 0) {
969  mode = "r";
970  iopipe = 1;
971  }
972  close(fd[iopipe]);
973  if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
974  LOG_ERROR_STR(Command);
975  close(fd[1 - iopipe]);
976  }
977  return f != NULL;
978  }
979  else { // child process
980  int iofd = STDOUT_FILENO;
981  if (strcmp(Mode, "w") == 0) {
982  iopipe = 1;
983  iofd = STDIN_FILENO;
984  }
985  close(fd[iopipe]);
986  if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
987  close(fd[1 - iopipe]);
988  _exit(-1);
989  }
990  else {
991  int MaxPossibleFileDescriptors = getdtablesize();
992  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
993  close(i); //close all dup'ed filedescriptors
994  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
995  close(fd[1 - iopipe]);
996  _exit(-1);
997  }
998  }
999  _exit(0);
1000  }
1001 }
1002 
1003 int cPipe::Close(void)
1004 {
1005  int ret = -1;
1006 
1007  if (f) {
1008  fclose(f);
1009  f = NULL;
1010  }
1011 
1012  if (pid > 0) {
1013  int status = 0;
1014  int i = 5;
1015  while (i > 0) {
1016  ret = waitpid(pid, &status, WNOHANG);
1017  if (ret < 0) {
1018  if (errno != EINTR && errno != ECHILD) {
1019  LOG_ERROR;
1020  break;
1021  }
1022  }
1023  else if (ret == pid)
1024  break;
1025  i--;
1026  cCondWait::SleepMs(100);
1027  }
1028  if (!i) {
1029  kill(pid, SIGKILL);
1030  ret = -1;
1031  }
1032  else if (ret == -1 || !WIFEXITED(status))
1033  ret = -1;
1034  pid = -1;
1035  }
1036 
1037  return ret;
1038 }
1039 
1040 // --- SystemExec ------------------------------------------------------------
1041 
1042 int SystemExec(const char *Command, bool Detached)
1043 {
1044  pid_t pid;
1045 
1046  if ((pid = fork()) < 0) { // fork failed
1047  LOG_ERROR_STR(Command);
1048  return -1;
1049  }
1050 
1051  if (pid > 0) { // parent process
1052  int status = 0;
1053  if (waitpid(pid, &status, 0) < 0) {
1054  LOG_ERROR_STR(Command);
1055  return -1;
1056  }
1057  return status;
1058  }
1059  else { // child process
1060  if (Detached) {
1061  // Fork again and let first child die - grandchild stays alive without parent
1062  if (fork() > 0)
1063  _exit(0);
1064  // Start a new session
1065  pid_t sid = setsid();
1066  if (sid < 0)
1067  _exit(-1);
1068  // close STDIN and re-open as /dev/null
1069  int devnull = open("/dev/null", O_RDONLY);
1070  if (devnull < 0 || dup2(devnull, 0) < 0)
1071  _exit(-1);
1072  }
1073  int MaxPossibleFileDescriptors = getdtablesize();
1074  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1075  close(i); //close all dup'ed filedescriptors
1076  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1)
1077  _exit(-1);
1078  _exit(0);
1079  }
1080 }
static void BackTrace(cStringList &StringList, int Level=0, bool Mangled=false)
Produces a backtrace and stores it in the given StringList.
Definition: thread.c:520
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:543
static cString Demangle(char *s)
Demangles the function name in the given string and returns the converted version of s.
Definition: thread.c:442
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:896
static int count
Definition: thread.h:270
void Activate(void)
Activates the global I/O throttling mechanism.
Definition: thread.c:906
~cIoThrottle()
Definition: thread.c:901
void Release(void)
Releases the global I/O throttling mechanism.
Definition: thread.c:917
bool active
Definition: thread.h:271
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition: thread.c:928
static cMutex mutex
Definition: thread.h:269
cMutexLock(cMutex *Mutex=NULL)
Definition: thread.c:388
bool Lock(cMutex *Mutex)
Definition: thread.c:401
~cMutexLock()
Definition: thread.c:395
cMutex * mutex
Definition: thread.h:143
bool locked
Definition: thread.h:144
Definition: thread.h:67
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
Definition: thread.h:292
pid_t pid
Definition: thread.h:294
int Close(void)
Definition: thread.c:1003
FILE * f
Definition: thread.h:295
bool Open(const char *Command, const char *Mode)
Definition: thread.c:949
cPipe(void)
Definition: thread.c:938
~cPipe()
Definition: thread.c:944
char * Read(FILE *f)
Definition: tools.c:1527
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:847
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:869
~cStateKey()
Definition: thread.c:856
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:864
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:879
cVector< int > flags
Definition: thread.c:571
cVector< tThreadId > threadIds
Definition: thread.c:570
cStateLockLog(void)
Definition: thread.c:586
uint8_t logCounter[SLL_THREADS][SLL_MAX_LIST]
Definition: thread.c:574
int logFlags[SLL_SIZE]
Definition: thread.c:573
void Check(const char *Name, bool Lock, bool Write=false)
Definition: thread.c:643
tThreadId logThreadIds[SLL_SIZE]
Definition: thread.c:572
bool dumped
Definition: thread.c:579
void Dump(const char *Name, tThreadId ThreadId)
Definition: thread.c:598
int logIndex
Definition: thread.c:578
cMutex mutex
Definition: thread.c:569
tThreadId threadId
Definition: thread.h:176
const char * name
Definition: thread.h:175
@ emDisabled
Definition: thread.h:174
@ emArmed
Definition: thread.h:174
@ emEnabled
Definition: thread.h:174
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:820
cStateLock(const char *Name=NULL)
Definition: thread.c:715
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:769
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
Definition: thread.c:799
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
Definition: thread.c:835
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
Definition: thread.c:725
Definition: tools.h:178
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition: tools.c:1208
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1195
cThreadLock(cThread *Thread=NULL)
Definition: thread.c:414
bool Lock(cThread *Thread)
Definition: thread.c:427
bool locked
Definition: thread.h:160
~cThreadLock()
Definition: thread.c:421
cThread * thread
Definition: thread.h:159
Definition: thread.h:79
virtual ~cThread()
Definition: thread.c:250
void SetIOPriority(int Priority)
Definition: thread.c:262
void Unlock(void)
Definition: thread.h:95
static void SetMainThreadId(void)
Definition: thread.c:378
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:373
char * description
Definition: thread.h:87
Definition: tools.h:404
uint64_t Elapsed(void) const
Definition: tools.c:818
int Size(void) const
Definition: tools.h:754
virtual void Append(T Data)
Definition: tools.h:774
static cMutex Mutex
Definition: epg.c:1424
#define BT_BUF_SIZE
Definition: thread.c:440
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
Definition: thread.c:39
#define SLL_LENGTH
Definition: thread.c:561
#define dbglockseq(n, l, w)
Definition: thread.c:708
#define ABORT
Definition: thread.c:27
#define SLL_MAX_LIST
Definition: thread.c:563
#define THREAD_STOP_SLEEP
Definition: thread.c:303
#define SLL_SIZE
Definition: thread.c:560
int SystemExec(const char *Command, bool Detached)
Definition: thread.c:1042
#define SLL_THREADS
Definition: thread.c:562
#define SLL_LOCK_FLAG
Definition: thread.c:565
#define SLL_WRITE_FLAG
Definition: thread.c:564
#define THREAD_STOP_TIMEOUT
Definition: thread.c:302
static cStateLockLog StateLockLog
Definition: thread.c:706
#define dbglocking(a...)
Definition: thread.c:36
pid_t tThreadId
Definition: thread.h:17
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
cString DayDateTime(time_t t)
Converts the given time to a string of the form "www dd.mm. hh:mm".
Definition: tools.c:1260
#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