vdr  1.7.31
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 2.5 2012/09/20 09:05:50 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)
208 {
209  active = running = false;
210  childTid = 0;
211  childThreadId = 0;
212  description = NULL;
213  if (Description)
214  SetDescription("%s", Description);
215 }
216 
218 {
219  Cancel(); // just in case the derived class didn't call it
220  free(description);
221 }
222 
223 void cThread::SetPriority(int Priority)
224 {
225  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
226  LOG_ERROR;
227 }
228 
229 void cThread::SetIOPriority(int Priority)
230 {
231  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
232  LOG_ERROR;
233 }
234 
235 void cThread::SetDescription(const char *Description, ...)
236 {
237  free(description);
238  description = NULL;
239  if (Description) {
240  va_list ap;
241  va_start(ap, Description);
242  description = strdup(cString::vsprintf(Description, ap));
243  va_end(ap);
244  }
245 }
246 
248 {
249  Thread->childThreadId = ThreadId();
250  if (Thread->description) {
251  dsyslog("%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
252 #ifdef PR_SET_NAME
253  if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
254  esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
255 #endif
256  }
257  Thread->Action();
258  if (Thread->description)
259  dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
260  Thread->running = false;
261  Thread->active = false;
262  return NULL;
263 }
264 
265 #define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
266 #define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
267 
268 bool cThread::Start(void)
269 {
270  if (!running) {
271  if (active) {
272  // Wait until the previous incarnation of this thread has completely ended
273  // before starting it newly:
274  cTimeMs RestartTimeout;
275  while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
277  }
278  if (!active) {
279  active = running = true;
280  if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
281  pthread_detach(childTid); // auto-reap
282  }
283  else {
284  LOG_ERROR;
285  active = running = false;
286  return false;
287  }
288  }
289  }
290  return true;
291 }
292 
293 bool cThread::Active(void)
294 {
295  if (active) {
296  //
297  // Single UNIX Spec v2 says:
298  //
299  // The pthread_kill() function is used to request
300  // that a signal be delivered to the specified thread.
301  //
302  // As in kill(), if sig is zero, error checking is
303  // performed but no signal is actually sent.
304  //
305  int err;
306  if ((err = pthread_kill(childTid, 0)) != 0) {
307  if (err != ESRCH)
308  LOG_ERROR;
309  childTid = 0;
310  active = running = false;
311  }
312  else
313  return true;
314  }
315  return false;
316 }
317 
318 void cThread::Cancel(int WaitSeconds)
319 {
320  running = false;
321  if (active && WaitSeconds > -1) {
322  if (WaitSeconds > 0) {
323  for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
324  if (!Active())
325  return;
326  cCondWait::SleepMs(10);
327  }
328  esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
329  }
330  pthread_cancel(childTid);
331  childTid = 0;
332  active = false;
333  }
334 }
335 
337 {
338  return syscall(__NR_gettid);
339 }
340 
342 {
343  if (mainThreadId == 0)
345  else
346  esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
347 }
348 
349 // --- cMutexLock ------------------------------------------------------------
350 
352 {
353  mutex = NULL;
354  locked = false;
355  Lock(Mutex);
356 }
357 
359 {
360  if (mutex && locked)
361  mutex->Unlock();
362 }
363 
365 {
366  if (Mutex && !mutex) {
367  mutex = Mutex;
368  Mutex->Lock();
369  locked = true;
370  return true;
371  }
372  return false;
373 }
374 
375 // --- cThreadLock -----------------------------------------------------------
376 
378 {
379  thread = NULL;
380  locked = false;
381  Lock(Thread);
382 }
383 
385 {
386  if (thread && locked)
387  thread->Unlock();
388 }
389 
391 {
392  if (Thread && !thread) {
393  thread = Thread;
394  Thread->Lock();
395  locked = true;
396  return true;
397  }
398  return false;
399 }
400 
401 // --- cIoThrottle -----------------------------------------------------------
402 
404 int cIoThrottle::count = 0;
405 
407 {
408  active = false;
409 }
410 
412 {
413  Release();
414 }
415 
417 {
418  if (!active) {
419  mutex.Lock();
420  count++;
421  active = true;
422  dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
423  mutex.Unlock();
424  }
425 }
426 
428 {
429  if (active) {
430  mutex.Lock();
431  count--;
432  active = false;
433  dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
434  mutex.Unlock();
435  }
436 }
437 
439 {
440  return count > 0;
441 }
442 
443 // --- cPipe -----------------------------------------------------------------
444 
445 // cPipe::Open() and cPipe::Close() are based on code originally received from
446 // Andreas Vitting <Andreas@huji.de>
447 
449 {
450  pid = -1;
451  f = NULL;
452 }
453 
455 {
456  Close();
457 }
458 
459 bool cPipe::Open(const char *Command, const char *Mode)
460 {
461  int fd[2];
462 
463  if (pipe(fd) < 0) {
464  LOG_ERROR;
465  return false;
466  }
467  if ((pid = fork()) < 0) { // fork failed
468  LOG_ERROR;
469  close(fd[0]);
470  close(fd[1]);
471  return false;
472  }
473 
474  const char *mode = "w";
475  int iopipe = 0;
476 
477  if (pid > 0) { // parent process
478  if (strcmp(Mode, "r") == 0) {
479  mode = "r";
480  iopipe = 1;
481  }
482  close(fd[iopipe]);
483  if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
484  LOG_ERROR;
485  close(fd[1 - iopipe]);
486  }
487  return f != NULL;
488  }
489  else { // child process
490  int iofd = STDOUT_FILENO;
491  if (strcmp(Mode, "w") == 0) {
492  mode = "r";
493  iopipe = 1;
494  iofd = STDIN_FILENO;
495  }
496  close(fd[iopipe]);
497  if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
498  LOG_ERROR;
499  close(fd[1 - iopipe]);
500  _exit(-1);
501  }
502  else {
503  int MaxPossibleFileDescriptors = getdtablesize();
504  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
505  close(i); //close all dup'ed filedescriptors
506  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
507  LOG_ERROR_STR(Command);
508  close(fd[1 - iopipe]);
509  _exit(-1);
510  }
511  }
512  _exit(0);
513  }
514 }
515 
516 int cPipe::Close(void)
517 {
518  int ret = -1;
519 
520  if (f) {
521  fclose(f);
522  f = NULL;
523  }
524 
525  if (pid > 0) {
526  int status = 0;
527  int i = 5;
528  while (i > 0) {
529  ret = waitpid(pid, &status, WNOHANG);
530  if (ret < 0) {
531  if (errno != EINTR && errno != ECHILD) {
532  LOG_ERROR;
533  break;
534  }
535  }
536  else if (ret == pid)
537  break;
538  i--;
539  cCondWait::SleepMs(100);
540  }
541  if (!i) {
542  kill(pid, SIGKILL);
543  ret = -1;
544  }
545  else if (ret == -1 || !WIFEXITED(status))
546  ret = -1;
547  pid = -1;
548  }
549 
550  return ret;
551 }
552 
553 // --- SystemExec ------------------------------------------------------------
554 
555 int SystemExec(const char *Command, bool Detached)
556 {
557  pid_t pid;
558 
559  if ((pid = fork()) < 0) { // fork failed
560  LOG_ERROR;
561  return -1;
562  }
563 
564  if (pid > 0) { // parent process
565  int status = 0;
566  if (waitpid(pid, &status, 0) < 0) {
567  LOG_ERROR;
568  return -1;
569  }
570  return status;
571  }
572  else { // child process
573  if (Detached) {
574  // Fork again and let first child die - grandchild stays alive without parent
575  if (fork() > 0)
576  _exit(0);
577  // Start a new session
578  pid_t sid = setsid();
579  if (sid < 0)
580  LOG_ERROR;
581  // close STDIN and re-open as /dev/null
582  int devnull = open("/dev/null", O_RDONLY);
583  if (devnull < 0 || dup2(devnull, 0) < 0)
584  LOG_ERROR;
585  }
586  int MaxPossibleFileDescriptors = getdtablesize();
587  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
588  close(i); //close all dup'ed filedescriptors
589  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
590  LOG_ERROR_STR(Command);
591  _exit(-1);
592  }
593  _exit(0);
594  }
595 }
596