vdr  2.6.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 4.15 2020/09/16 13:48:33 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  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 
72 void 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 
78 bool 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 
121 void cCondVar::Wait(cMutex &Mutex)
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 
132 bool 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 
157 cRwLock::cRwLock(bool PreferWriter)
158 {
159  locked = 0;
160  writeLockThreadId = 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 
172 bool 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 
194 void cRwLock::Unlock(void)
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  }
202  writeLockThreadId = 0;
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 
222 void cMutex::Lock(void)
223 {
224  pthread_mutex_lock(&mutex);
225  locked++;
226 }
227 
228 void cMutex::Unlock(void)
229 {
230  if (!--locked)
231  pthread_mutex_unlock(&mutex);
232 }
233 
234 // --- cThread ---------------------------------------------------------------
235 
237 
238 cThread::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 
255 void cThread::SetPriority(int Priority)
256 {
257  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
258  LOG_ERROR;
259 }
260 
261 void 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 
267 void 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 
304 bool cThread::Start(void)
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 
329 bool cThread::Active(void)
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 
354 void 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;
362  cCondWait::SleepMs(10);
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 
519 void 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 
530 void 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 
542 cString 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 
567 private:
574 #ifdef DEBUG_LOCKCALL
575  char logCaller[SLL_SIZE][SLL_LENGTH];
576 #endif
577  int logIndex;
578  bool dumped;
579  void Dump(const char *Name, tThreadId ThreadId);
580 public:
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 
597 void 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 
641 void 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 
713 cStateLock::cStateLock(const char *Name)
714 :rwLock(true)
715 {
716  name = Name;
717  threadId = 0;
718  state = 0;
720  syncStateKey = NULL;
721 }
722 
723 bool 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  return false;
757 }
758 
759 void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
760 {
761  dbglocking("%5d %-12s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState);
762  if (StateKey.stateLock != this) {
763  esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name);
764  ABORT;
765  return;
766  }
767  if (StateKey.write && threadId != cThread::ThreadId()) {
768  esyslog("ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
769  ABORT;
770  return;
771  }
772  if (StateKey.write && (IncState && explicitModify != emArmed || explicitModify == emEnabled)) {
774  syncStateKey->state++;
775  state++;
776  }
777  StateKey.state = state;
778  StateKey.stateLock = NULL;
779  if (StateKey.write) {
780  StateKey.write = false;
781  threadId = 0;
783  syncStateKey = NULL;
784  }
785  dbglockseq(name, false, false);
786  rwLock.Unlock();
787 }
788 
790 {
791  dbglocking("%5d %-12s %10p SetSyncStateKey\n", cThread::ThreadId(), name, &StateKey);
792  if (threadId != cThread::ThreadId()) {
793  esyslog("ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
794  ABORT;
795  return;
796  }
797  if (StateKey.stateLock == this) {
798  esyslog("ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)", threadId, name);
799  ABORT;
800  return;
801  }
802  if (syncStateKey) {
803  esyslog("ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)", threadId, name);
804  ABORT;
805  return;
806  }
807  syncStateKey = &StateKey;
808 }
809 
811 {
812  if (threadId != cThread::ThreadId()) {
813  esyslog("ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
814  ABORT;
815  return;
816  }
817  if (explicitModify != emDisabled) {
818  esyslog("ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)", threadId, name);
819  ABORT;
820  return;
821  }
823 }
824 
826 {
827  if (threadId != cThread::ThreadId()) {
828  esyslog("ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
829  ABORT;
830  return;
831  }
833 }
834 
835 // --- cStateKey -------------------------------------------------------------
836 
837 cStateKey::cStateKey(bool IgnoreFirst)
838 {
839  stateLock = NULL;
840  write = false;
841  state = 0;
842  if (!IgnoreFirst)
843  Reset();
844 }
845 
847 {
848  if (stateLock) {
849  esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this);
850  ABORT;
851  }
852 }
853 
855 {
856  state = -1; // lock and key are initialized differently, to make the first check return true
857 }
858 
859 void cStateKey::Remove(bool IncState)
860 {
861  if (stateLock)
862  stateLock->Unlock(*this, IncState);
863  else {
864  esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
865  ABORT;
866  }
867 }
868 
870 {
871  if (!stateLock) {
872  esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this);
873  ABORT;
874  }
875  else if (write)
876  return state != stateLock->state;
877  else
878  return true;
879 }
880 
881 // --- cIoThrottle -----------------------------------------------------------
882 
884 int cIoThrottle::count = 0;
885 
887 {
888  active = false;
889 }
890 
892 {
893  Release();
894 }
895 
897 {
898  if (!active) {
899  mutex.Lock();
900  count++;
901  active = true;
902  dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
903  mutex.Unlock();
904  }
905 }
906 
908 {
909  if (active) {
910  mutex.Lock();
911  count--;
912  active = false;
913  dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
914  mutex.Unlock();
915  }
916 }
917 
919 {
920  return count > 0;
921 }
922 
923 // --- cPipe -----------------------------------------------------------------
924 
925 // cPipe::Open() and cPipe::Close() are based on code originally received from
926 // Andreas Vitting <Andreas@huji.de>
927 
929 {
930  pid = -1;
931  f = NULL;
932 }
933 
935 {
936  Close();
937 }
938 
939 bool cPipe::Open(const char *Command, const char *Mode)
940 {
941  int fd[2];
942 
943  if (pipe(fd) < 0) {
944  LOG_ERROR;
945  return false;
946  }
947  if ((pid = fork()) < 0) { // fork failed
948  LOG_ERROR;
949  close(fd[0]);
950  close(fd[1]);
951  return false;
952  }
953 
954  const char *mode = "w";
955  int iopipe = 0;
956 
957  if (pid > 0) { // parent process
958  if (strcmp(Mode, "r") == 0) {
959  mode = "r";
960  iopipe = 1;
961  }
962  close(fd[iopipe]);
963  if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
964  LOG_ERROR;
965  close(fd[1 - iopipe]);
966  }
967  return f != NULL;
968  }
969  else { // child process
970  int iofd = STDOUT_FILENO;
971  if (strcmp(Mode, "w") == 0) {
972  iopipe = 1;
973  iofd = STDIN_FILENO;
974  }
975  close(fd[iopipe]);
976  if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
977  LOG_ERROR;
978  close(fd[1 - iopipe]);
979  _exit(-1);
980  }
981  else {
982  int MaxPossibleFileDescriptors = getdtablesize();
983  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
984  close(i); //close all dup'ed filedescriptors
985  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
986  LOG_ERROR_STR(Command);
987  close(fd[1 - iopipe]);
988  _exit(-1);
989  }
990  }
991  _exit(0);
992  }
993 }
994 
995 int cPipe::Close(void)
996 {
997  int ret = -1;
998 
999  if (f) {
1000  fclose(f);
1001  f = NULL;
1002  }
1003 
1004  if (pid > 0) {
1005  int status = 0;
1006  int i = 5;
1007  while (i > 0) {
1008  ret = waitpid(pid, &status, WNOHANG);
1009  if (ret < 0) {
1010  if (errno != EINTR && errno != ECHILD) {
1011  LOG_ERROR;
1012  break;
1013  }
1014  }
1015  else if (ret == pid)
1016  break;
1017  i--;
1018  cCondWait::SleepMs(100);
1019  }
1020  if (!i) {
1021  kill(pid, SIGKILL);
1022  ret = -1;
1023  }
1024  else if (ret == -1 || !WIFEXITED(status))
1025  ret = -1;
1026  pid = -1;
1027  }
1028 
1029  return ret;
1030 }
1031 
1032 // --- SystemExec ------------------------------------------------------------
1033 
1034 int SystemExec(const char *Command, bool Detached)
1035 {
1036  pid_t pid;
1037 
1038  if ((pid = fork()) < 0) { // fork failed
1039  LOG_ERROR;
1040  return -1;
1041  }
1042 
1043  if (pid > 0) { // parent process
1044  int status = 0;
1045  if (waitpid(pid, &status, 0) < 0) {
1046  LOG_ERROR;
1047  return -1;
1048  }
1049  return status;
1050  }
1051  else { // child process
1052  if (Detached) {
1053  // Fork again and let first child die - grandchild stays alive without parent
1054  if (fork() > 0)
1055  _exit(0);
1056  // Start a new session
1057  pid_t sid = setsid();
1058  if (sid < 0)
1059  LOG_ERROR;
1060  // close STDIN and re-open as /dev/null
1061  int devnull = open("/dev/null", O_RDONLY);
1062  if (devnull < 0 || dup2(devnull, 0) < 0)
1063  LOG_ERROR;
1064  }
1065  int MaxPossibleFileDescriptors = getdtablesize();
1066  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1067  close(i); //close all dup'ed filedescriptors
1068  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
1069  LOG_ERROR_STR(Command);
1070  _exit(-1);
1071  }
1072  _exit(0);
1073  }
1074 }
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:886
static int count
Definition: thread.h:270
void Activate(void)
Activates the global I/O throttling mechanism.
Definition: thread.c:896
~cIoThrottle()
Definition: thread.c:891
void Release(void)
Releases the global I/O throttling mechanism.
Definition: thread.c:907
bool active
Definition: thread.h:271
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition: thread.c:918
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
Definition: thread.h:67
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
Definition: thread.h:292
pid_t pid
Definition: thread.h:294
int Close(void)
Definition: thread.c:995
FILE * f
Definition: thread.h:295
bool Open(const char *Command, const char *Mode)
Definition: thread.c:939
cPipe(void)
Definition: thread.c:928
~cPipe()
Definition: thread.c:934
char * Read(FILE *f)
Definition: tools.c:1481
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:837
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:859
~cStateKey()
Definition: thread.c:846
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:854
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:869
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
int logIndex
Definition: thread.c:577
cMutex mutex
Definition: thread.c:568
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:810
cStateLock(const char *Name=NULL)
Definition: thread.c:713
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:759
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
Definition: thread.c:789
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
Definition: thread.c:825
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
Definition: thread.c:723
Definition: tools.h:178
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition: tools.c:1162
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1149
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
Definition: thread.h:79
virtual ~cThread()
Definition: thread.c:249
void SetIOPriority(int Priority)
Definition: thread.c:261
void Unlock(void)
Definition: thread.h:95
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
Definition: tools.h:401
uint64_t Elapsed(void) const
Definition: tools.c:802
int Size(void) const
Definition: tools.h:764
virtual void Append(T Data)
Definition: tools.h:784
#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:1034
#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
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:1214
#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