vdr  2.2.0
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 3.2 2013/12/29 15:26:33 kls Exp $
8  */
9 
10 #include "thread.h"
11 #include <errno.h>
12 #include <linux/unistd.h>
13 #include <malloc.h>
14 #include <stdarg.h>
15 #include <stdlib.h>
16 #include <sys/resource.h>
17 #include <sys/syscall.h>
18 #include <sys/time.h>
19 #include <sys/wait.h>
20 #include <sys/prctl.h>
21 #include <unistd.h>
22 #include "tools.h"
23 
24 static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
25 {
26  struct timeval now;
27  if (gettimeofday(&now, NULL) == 0) { // get current time
28  now.tv_sec += MillisecondsFromNow / 1000; // add full seconds
29  now.tv_usec += (MillisecondsFromNow % 1000) * 1000; // add microseconds
30  if (now.tv_usec >= 1000000) { // take care of an overflow
31  now.tv_sec++;
32  now.tv_usec -= 1000000;
33  }
34  Abstime->tv_sec = now.tv_sec; // seconds
35  Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
36  return true;
37  }
38  return false;
39 }
40 
41 // --- cCondWait -------------------------------------------------------------
42 
44 {
45  signaled = false;
46  pthread_mutex_init(&mutex, NULL);
47  pthread_cond_init(&cond, NULL);
48 }
49 
51 {
52  pthread_cond_broadcast(&cond); // wake up any sleepers
53  pthread_cond_destroy(&cond);
54  pthread_mutex_destroy(&mutex);
55 }
56 
57 void cCondWait::SleepMs(int TimeoutMs)
58 {
59  cCondWait w;
60  w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
61 }
62 
63 bool cCondWait::Wait(int TimeoutMs)
64 {
65  pthread_mutex_lock(&mutex);
66  if (!signaled) {
67  if (TimeoutMs) {
68  struct timespec abstime;
69  if (GetAbsTime(&abstime, TimeoutMs)) {
70  while (!signaled) {
71  if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
72  break;
73  }
74  }
75  }
76  else
77  pthread_cond_wait(&cond, &mutex);
78  }
79  bool r = signaled;
80  signaled = false;
81  pthread_mutex_unlock(&mutex);
82  return r;
83 }
84 
86 {
87  pthread_mutex_lock(&mutex);
88  signaled = true;
89  pthread_cond_broadcast(&cond);
90  pthread_mutex_unlock(&mutex);
91 }
92 
93 // --- cCondVar --------------------------------------------------------------
94 
96 {
97  pthread_cond_init(&cond, 0);
98 }
99 
101 {
102  pthread_cond_broadcast(&cond); // wake up any sleepers
103  pthread_cond_destroy(&cond);
104 }
105 
106 void cCondVar::Wait(cMutex &Mutex)
107 {
108  if (Mutex.locked) {
109  int locked = Mutex.locked;
110  Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
111  // does an implicit unlock of the mutex
112  pthread_cond_wait(&cond, &Mutex.mutex);
113  Mutex.locked = locked;
114  }
115 }
116 
117 bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
118 {
119  bool r = true; // true = condition signaled, false = timeout
120 
121  if (Mutex.locked) {
122  struct timespec abstime;
123  if (GetAbsTime(&abstime, TimeoutMs)) {
124  int locked = Mutex.locked;
125  Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
126  // does an implicit unlock of the mutex.
127  if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
128  r = false;
129  Mutex.locked = locked;
130  }
131  }
132  return r;
133 }
134 
136 {
137  pthread_cond_broadcast(&cond);
138 }
139 
140 // --- cRwLock ---------------------------------------------------------------
141 
142 cRwLock::cRwLock(bool PreferWriter)
143 {
144  pthread_rwlockattr_t attr;
145  pthread_rwlockattr_init(&attr);
146  pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
147  pthread_rwlock_init(&rwlock, &attr);
148 }
149 
151 {
152  pthread_rwlock_destroy(&rwlock);
153 }
154 
155 bool cRwLock::Lock(bool Write, int TimeoutMs)
156 {
157  int Result = 0;
158  struct timespec abstime;
159  if (TimeoutMs) {
160  if (!GetAbsTime(&abstime, TimeoutMs))
161  TimeoutMs = 0;
162  }
163  if (Write)
164  Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
165  else
166  Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
167  return Result == 0;
168 }
169 
170 void cRwLock::Unlock(void)
171 {
172  pthread_rwlock_unlock(&rwlock);
173 }
174 
175 // --- cMutex ----------------------------------------------------------------
176 
178 {
179  locked = 0;
180  pthread_mutexattr_t attr;
181  pthread_mutexattr_init(&attr);
182  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
183  pthread_mutex_init(&mutex, &attr);
184 }
185 
187 {
188  pthread_mutex_destroy(&mutex);
189 }
190 
191 void cMutex::Lock(void)
192 {
193  pthread_mutex_lock(&mutex);
194  locked++;
195 }
196 
197 void cMutex::Unlock(void)
198 {
199  if (!--locked)
200  pthread_mutex_unlock(&mutex);
201 }
202 
203 // --- cThread ---------------------------------------------------------------
204 
206 
207 cThread::cThread(const char *Description, bool LowPriority)
208 {
209  active = running = false;
210  childTid = 0;
211  childThreadId = 0;
212  description = NULL;
213  if (Description)
214  SetDescription("%s", Description);
215  lowPriority = LowPriority;
216 }
217 
219 {
220  Cancel(); // just in case the derived class didn't call it
221  free(description);
222 }
223 
224 void cThread::SetPriority(int Priority)
225 {
226  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
227  LOG_ERROR;
228 }
229 
230 void cThread::SetIOPriority(int Priority)
231 {
232  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0) // idle class
233  LOG_ERROR;
234 }
235 
236 void cThread::SetDescription(const char *Description, ...)
237 {
238  free(description);
239  description = NULL;
240  if (Description) {
241  va_list ap;
242  va_start(ap, Description);
243  description = strdup(cString::vsprintf(Description, ap));
244  va_end(ap);
245  }
246 }
247 
249 {
250  Thread->childThreadId = ThreadId();
251  if (Thread->description) {
252  dsyslog("%s thread started (pid=%d, tid=%d, prio=%s)", Thread->description, getpid(), Thread->childThreadId, Thread->lowPriority ? "low" : "high");
253 #ifdef PR_SET_NAME
254  if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
255  esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
256 #endif
257  }
258  if (Thread->lowPriority) {
259  Thread->SetPriority(19);
260  Thread->SetIOPriority(7);
261  }
262  Thread->Action();
263  if (Thread->description)
264  dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
265  Thread->running = false;
266  Thread->active = false;
267  return NULL;
268 }
269 
270 #define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
271 #define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
272 
273 bool cThread::Start(void)
274 {
275  if (!running) {
276  if (active) {
277  // Wait until the previous incarnation of this thread has completely ended
278  // before starting it newly:
279  cTimeMs RestartTimeout;
280  while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
282  }
283  if (!active) {
284  active = running = true;
285  if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
286  pthread_detach(childTid); // auto-reap
287  }
288  else {
289  LOG_ERROR;
290  active = running = false;
291  return false;
292  }
293  }
294  }
295  return true;
296 }
297 
298 bool cThread::Active(void)
299 {
300  if (active) {
301  //
302  // Single UNIX Spec v2 says:
303  //
304  // The pthread_kill() function is used to request
305  // that a signal be delivered to the specified thread.
306  //
307  // As in kill(), if sig is zero, error checking is
308  // performed but no signal is actually sent.
309  //
310  int err;
311  if ((err = pthread_kill(childTid, 0)) != 0) {
312  if (err != ESRCH)
313  LOG_ERROR;
314  childTid = 0;
315  active = running = false;
316  }
317  else
318  return true;
319  }
320  return false;
321 }
322 
323 void cThread::Cancel(int WaitSeconds)
324 {
325  running = false;
326  if (active && WaitSeconds > -1) {
327  if (WaitSeconds > 0) {
328  for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
329  if (!Active())
330  return;
331  cCondWait::SleepMs(10);
332  }
333  esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
334  }
335  pthread_cancel(childTid);
336  childTid = 0;
337  active = false;
338  }
339 }
340 
342 {
343  return syscall(__NR_gettid);
344 }
345 
347 {
348  if (mainThreadId == 0)
349  mainThreadId = ThreadId();
350  else
351  esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
352 }
353 
354 // --- cMutexLock ------------------------------------------------------------
355 
357 {
358  mutex = NULL;
359  locked = false;
360  Lock(Mutex);
361 }
362 
364 {
365  if (mutex && locked)
366  mutex->Unlock();
367 }
368 
370 {
371  if (Mutex && !mutex) {
372  mutex = Mutex;
373  Mutex->Lock();
374  locked = true;
375  return true;
376  }
377  return false;
378 }
379 
380 // --- cThreadLock -----------------------------------------------------------
381 
383 {
384  thread = NULL;
385  locked = false;
386  Lock(Thread);
387 }
388 
390 {
391  if (thread && locked)
392  thread->Unlock();
393 }
394 
396 {
397  if (Thread && !thread) {
398  thread = Thread;
399  Thread->Lock();
400  locked = true;
401  return true;
402  }
403  return false;
404 }
405 
406 // --- cIoThrottle -----------------------------------------------------------
407 
409 int cIoThrottle::count = 0;
410 
412 {
413  active = false;
414 }
415 
417 {
418  Release();
419 }
420 
422 {
423  if (!active) {
424  mutex.Lock();
425  count++;
426  active = true;
427  dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
428  mutex.Unlock();
429  }
430 }
431 
433 {
434  if (active) {
435  mutex.Lock();
436  count--;
437  active = false;
438  dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
439  mutex.Unlock();
440  }
441 }
442 
444 {
445  return count > 0;
446 }
447 
448 // --- cPipe -----------------------------------------------------------------
449 
450 // cPipe::Open() and cPipe::Close() are based on code originally received from
451 // Andreas Vitting <Andreas@huji.de>
452 
454 {
455  pid = -1;
456  f = NULL;
457 }
458 
460 {
461  Close();
462 }
463 
464 bool cPipe::Open(const char *Command, const char *Mode)
465 {
466  int fd[2];
467 
468  if (pipe(fd) < 0) {
469  LOG_ERROR;
470  return false;
471  }
472  if ((pid = fork()) < 0) { // fork failed
473  LOG_ERROR;
474  close(fd[0]);
475  close(fd[1]);
476  return false;
477  }
478 
479  const char *mode = "w";
480  int iopipe = 0;
481 
482  if (pid > 0) { // parent process
483  if (strcmp(Mode, "r") == 0) {
484  mode = "r";
485  iopipe = 1;
486  }
487  close(fd[iopipe]);
488  if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
489  LOG_ERROR;
490  close(fd[1 - iopipe]);
491  }
492  return f != NULL;
493  }
494  else { // child process
495  int iofd = STDOUT_FILENO;
496  if (strcmp(Mode, "w") == 0) {
497  iopipe = 1;
498  iofd = STDIN_FILENO;
499  }
500  close(fd[iopipe]);
501  if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
502  LOG_ERROR;
503  close(fd[1 - iopipe]);
504  _exit(-1);
505  }
506  else {
507  int MaxPossibleFileDescriptors = getdtablesize();
508  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
509  close(i); //close all dup'ed filedescriptors
510  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
511  LOG_ERROR_STR(Command);
512  close(fd[1 - iopipe]);
513  _exit(-1);
514  }
515  }
516  _exit(0);
517  }
518 }
519 
520 int cPipe::Close(void)
521 {
522  int ret = -1;
523 
524  if (f) {
525  fclose(f);
526  f = NULL;
527  }
528 
529  if (pid > 0) {
530  int status = 0;
531  int i = 5;
532  while (i > 0) {
533  ret = waitpid(pid, &status, WNOHANG);
534  if (ret < 0) {
535  if (errno != EINTR && errno != ECHILD) {
536  LOG_ERROR;
537  break;
538  }
539  }
540  else if (ret == pid)
541  break;
542  i--;
543  cCondWait::SleepMs(100);
544  }
545  if (!i) {
546  kill(pid, SIGKILL);
547  ret = -1;
548  }
549  else if (ret == -1 || !WIFEXITED(status))
550  ret = -1;
551  pid = -1;
552  }
553 
554  return ret;
555 }
556 
557 // --- SystemExec ------------------------------------------------------------
558 
559 int SystemExec(const char *Command, bool Detached)
560 {
561  pid_t pid;
562 
563  if ((pid = fork()) < 0) { // fork failed
564  LOG_ERROR;
565  return -1;
566  }
567 
568  if (pid > 0) { // parent process
569  int status = 0;
570  if (waitpid(pid, &status, 0) < 0) {
571  LOG_ERROR;
572  return -1;
573  }
574  return status;
575  }
576  else { // child process
577  if (Detached) {
578  // Fork again and let first child die - grandchild stays alive without parent
579  if (fork() > 0)
580  _exit(0);
581  // Start a new session
582  pid_t sid = setsid();
583  if (sid < 0)
584  LOG_ERROR;
585  // close STDIN and re-open as /dev/null
586  int devnull = open("/dev/null", O_RDONLY);
587  if (devnull < 0 || dup2(devnull, 0) < 0)
588  LOG_ERROR;
589  }
590  int MaxPossibleFileDescriptors = getdtablesize();
591  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
592  close(i); //close all dup'ed filedescriptors
593  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
594  LOG_ERROR_STR(Command);
595  _exit(-1);
596  }
597  _exit(0);
598  }
599 }
static cMutex mutex
Definition: thread.h:169
void Lock(void)
Definition: thread.c:191
virtual void Action(void)=0
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
#define dsyslog(a...)
Definition: tools.h:36
void SetDescription(const char *Description,...) __attribute__((format(printf
Definition: thread.c:236
#define LOG_ERROR
Definition: tools.h:38
~cIoThrottle()
Definition: thread.c:416
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
Definition: thread.c:85
~cThreadLock()
Definition: thread.c:389
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
Definition: thread.c:24
bool Lock(cMutex *Mutex)
Definition: thread.c:369
#define esyslog(a...)
Definition: tools.h:34
void SetPriority(int Priority)
Definition: thread.c:224
cThreadLock(cThread *Thread=NULL)
Definition: thread.c:382
virtual ~cThread()
Definition: thread.c:218
#define LOG_ERROR_STR(s)
Definition: tools.h:39
T max(T a, T b)
Definition: tools.h:55
#define THREAD_STOP_TIMEOUT
Definition: thread.c:270
static int count
Definition: thread.h:170
cRwLock(bool PreferWriter=false)
Definition: thread.c:142
cIoThrottle(void)
Definition: thread.c:411
void Unlock(void)
Definition: thread.c:170
~cCondVar()
Definition: thread.c:100
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition: tools.c:1093
cMutexLock(cMutex *Mutex=NULL)
Definition: thread.c:356
~cMutex()
Definition: thread.c:186
pthread_mutex_t mutex
Definition: thread.h:66
~cCondWait()
Definition: thread.c:50
void Broadcast(void)
Definition: thread.c:135
pthread_cond_t cond
Definition: thread.h:20
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:57
static void * StartThread(cThread *Thread)
Definition: thread.c:248
bool active
Definition: thread.h:80
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:273
cCondWait(void)
Definition: thread.c:43
bool Open(const char *Command, const char *Mode)
Definition: thread.c:464
#define THREAD_STOP_SLEEP
Definition: thread.c:271
char * description
Definition: thread.h:85
bool Lock(bool Write, int TimeoutMs=0)
Definition: thread.c:155
pid_t tThreadId
Definition: thread.h:75
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0...
Definition: thread.c:63
Definition: thread.h:63
static tThreadId ThreadId(void)
Definition: thread.c:341
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:117
int SystemExec(const char *Command, bool Detached)
Definition: thread.c:559
void Activate(void)
Activates the global I/O throttling mechanism.
Definition: thread.c:421
int Close(void)
Definition: thread.c:520
~cRwLock()
Definition: thread.c:150
tThreadId childThreadId
Definition: thread.h:83
bool signaled
Definition: thread.h:21
~cPipe()
Definition: thread.c:459
void SetIOPriority(int Priority)
Definition: thread.c:230
static void SetMainThreadId(void)
Definition: thread.c:346
~cMutexLock()
Definition: thread.c:363
bool Active(void)
Checks whether the thread is still alive.
Definition: thread.c:298
int locked
Definition: thread.h:67
Definition: thread.h:77
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
Definition: thread.c:443
void Wait(cMutex &Mutex)
Definition: thread.c:106
cPipe(void)
Definition: thread.c:453
Definition: tools.h:333
bool lowPriority
Definition: thread.h:86
pthread_mutex_t mutex
Definition: thread.h:19
cCondVar(void)
Definition: thread.c:95
bool running
Definition: thread.h:81
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition: thread.c:207
bool Lock(cThread *Thread)
Definition: thread.c:395
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
static tThreadId mainThreadId
Definition: thread.h:87
void Release(void)
Releases the global I/O throttling mechanism.
Definition: thread.c:432
uint64_t Elapsed(void) const
Definition: tools.c:748
void Lock(void)
Definition: thread.h:92
cMutex(void)
Definition: thread.c:177
void Unlock(void)
Definition: thread.c:197