vdr  1.7.27
thread.c
Go to the documentation of this file.
00001 /*
00002  * thread.c: A simple thread base class
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: thread.c 2.3 2009/04/13 13:50:39 kls Exp $
00008  */
00009 
00010 #include "thread.h"
00011 #include <errno.h>
00012 #include <linux/unistd.h>
00013 #include <malloc.h>
00014 #include <stdarg.h>
00015 #include <stdlib.h>
00016 #include <sys/resource.h>
00017 #include <sys/syscall.h>
00018 #include <sys/time.h>
00019 #include <sys/wait.h>
00020 #include <sys/prctl.h>
00021 #include <unistd.h>
00022 #include "tools.h"
00023 
00024 static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
00025 {
00026   struct timeval now;
00027   if (gettimeofday(&now, NULL) == 0) {           // get current time
00028      now.tv_sec  += MillisecondsFromNow / 1000;  // add full seconds
00029      now.tv_usec += (MillisecondsFromNow % 1000) * 1000;  // add microseconds
00030      if (now.tv_usec >= 1000000) {               // take care of an overflow
00031         now.tv_sec++;
00032         now.tv_usec -= 1000000;
00033         }
00034      Abstime->tv_sec = now.tv_sec;          // seconds
00035      Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
00036      return true;
00037      }
00038   return false;
00039 }
00040 
00041 // --- cCondWait -------------------------------------------------------------
00042 
00043 cCondWait::cCondWait(void)
00044 {
00045   signaled = false;
00046   pthread_mutex_init(&mutex, NULL);
00047   pthread_cond_init(&cond, NULL);
00048 }
00049 
00050 cCondWait::~cCondWait()
00051 {
00052   pthread_cond_broadcast(&cond); // wake up any sleepers
00053   pthread_cond_destroy(&cond);
00054   pthread_mutex_destroy(&mutex);
00055 }
00056 
00057 void cCondWait::SleepMs(int TimeoutMs)
00058 {
00059   cCondWait w;
00060   w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
00061 }
00062 
00063 bool cCondWait::Wait(int TimeoutMs)
00064 {
00065   pthread_mutex_lock(&mutex);
00066   if (!signaled) {
00067      if (TimeoutMs) {
00068         struct timespec abstime;
00069         if (GetAbsTime(&abstime, TimeoutMs)) {
00070            while (!signaled) {
00071                  if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
00072                     break;
00073                  }
00074            }
00075         }
00076      else
00077         pthread_cond_wait(&cond, &mutex);
00078      }
00079   bool r = signaled;
00080   signaled = false;
00081   pthread_mutex_unlock(&mutex);
00082   return r;
00083 }
00084 
00085 void cCondWait::Signal(void)
00086 {
00087   pthread_mutex_lock(&mutex);
00088   signaled = true;
00089   pthread_cond_broadcast(&cond);
00090   pthread_mutex_unlock(&mutex);
00091 }
00092 
00093 // --- cCondVar --------------------------------------------------------------
00094 
00095 cCondVar::cCondVar(void)
00096 {
00097   pthread_cond_init(&cond, 0);
00098 }
00099 
00100 cCondVar::~cCondVar()
00101 {
00102   pthread_cond_broadcast(&cond); // wake up any sleepers
00103   pthread_cond_destroy(&cond);
00104 }
00105 
00106 void cCondVar::Wait(cMutex &Mutex)
00107 {
00108   if (Mutex.locked) {
00109      int locked = Mutex.locked;
00110      Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
00111                        // does an implicit unlock of the mutex
00112      pthread_cond_wait(&cond, &Mutex.mutex);
00113      Mutex.locked = locked;
00114      }
00115 }
00116 
00117 bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
00118 {
00119   bool r = true; // true = condition signaled, false = timeout
00120 
00121   if (Mutex.locked) {
00122      struct timespec abstime;
00123      if (GetAbsTime(&abstime, TimeoutMs)) {
00124         int locked = Mutex.locked;
00125         Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
00126                           // does an implicit unlock of the mutex.
00127         if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
00128            r = false;
00129         Mutex.locked = locked;
00130         }
00131      }
00132   return r;
00133 }
00134 
00135 void cCondVar::Broadcast(void)
00136 {
00137   pthread_cond_broadcast(&cond);
00138 }
00139 
00140 // --- cRwLock ---------------------------------------------------------------
00141 
00142 cRwLock::cRwLock(bool PreferWriter)
00143 {
00144   pthread_rwlockattr_t attr;
00145   pthread_rwlockattr_init(&attr);
00146   pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
00147   pthread_rwlock_init(&rwlock, &attr);
00148 }
00149 
00150 cRwLock::~cRwLock()
00151 {
00152   pthread_rwlock_destroy(&rwlock);
00153 }
00154 
00155 bool cRwLock::Lock(bool Write, int TimeoutMs)
00156 {
00157   int Result = 0;
00158   struct timespec abstime;
00159   if (TimeoutMs) {
00160      if (!GetAbsTime(&abstime, TimeoutMs))
00161         TimeoutMs = 0;
00162      }
00163   if (Write)
00164      Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
00165   else
00166      Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
00167   return Result == 0;
00168 }
00169 
00170 void cRwLock::Unlock(void)
00171 {
00172   pthread_rwlock_unlock(&rwlock);
00173 }
00174 
00175 // --- cMutex ----------------------------------------------------------------
00176 
00177 cMutex::cMutex(void)
00178 {
00179   locked = 0;
00180   pthread_mutexattr_t attr;
00181   pthread_mutexattr_init(&attr);
00182   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
00183   pthread_mutex_init(&mutex, &attr);
00184 }
00185 
00186 cMutex::~cMutex()
00187 {
00188   pthread_mutex_destroy(&mutex);
00189 }
00190 
00191 void cMutex::Lock(void)
00192 {
00193   pthread_mutex_lock(&mutex);
00194   locked++;
00195 }
00196 
00197 void cMutex::Unlock(void)
00198 {
00199  if (!--locked)
00200     pthread_mutex_unlock(&mutex);
00201 }
00202 
00203 // --- cThread ---------------------------------------------------------------
00204 
00205 tThreadId cThread::mainThreadId = 0;
00206 
00207 cThread::cThread(const char *Description)
00208 {
00209   active = running = false;
00210   childTid = 0;
00211   childThreadId = 0;
00212   description = NULL;
00213   if (Description)
00214      SetDescription("%s", Description);
00215 }
00216 
00217 cThread::~cThread()
00218 {
00219   Cancel(); // just in case the derived class didn't call it
00220   free(description);
00221 }
00222 
00223 void cThread::SetPriority(int Priority)
00224 {
00225   if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
00226      LOG_ERROR;
00227 }
00228 
00229 void cThread::SetIOPriority(int Priority)
00230 {
00231   if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
00232      LOG_ERROR;
00233 }
00234 
00235 void cThread::SetDescription(const char *Description, ...)
00236 {
00237   free(description);
00238   description = NULL;
00239   if (Description) {
00240      va_list ap;
00241      va_start(ap, Description);
00242      description = strdup(cString::sprintf(Description, ap));
00243      va_end(ap);
00244      }
00245 }
00246 
00247 void *cThread::StartThread(cThread *Thread)
00248 {
00249   Thread->childThreadId = ThreadId();
00250   if (Thread->description) {
00251      dsyslog("%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
00252 #ifdef PR_SET_NAME
00253      if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
00254         esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
00255 #endif
00256      }
00257   Thread->Action();
00258   if (Thread->description)
00259      dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
00260   Thread->running = false;
00261   Thread->active = false;
00262   return NULL;
00263 }
00264 
00265 #define THREAD_STOP_TIMEOUT  3000 // ms to wait for a thread to stop before newly starting it
00266 #define THREAD_STOP_SLEEP      30 // ms to sleep while waiting for a thread to stop
00267 
00268 bool cThread::Start(void)
00269 {
00270   if (!running) {
00271      if (active) {
00272         // Wait until the previous incarnation of this thread has completely ended
00273         // before starting it newly:
00274         cTimeMs RestartTimeout;
00275         while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
00276               cCondWait::SleepMs(THREAD_STOP_SLEEP);
00277         }
00278      if (!active) {
00279         active = running = true;
00280         if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
00281            pthread_detach(childTid); // auto-reap
00282            }
00283         else {
00284            LOG_ERROR;
00285            active = running = false;
00286            return false;
00287            }
00288         }
00289      }
00290   return true;
00291 }
00292 
00293 bool cThread::Active(void)
00294 {
00295   if (active) {
00296      //
00297      // Single UNIX Spec v2 says:
00298      //
00299      // The pthread_kill() function is used to request
00300      // that a signal be delivered to the specified thread.
00301      //
00302      // As in kill(), if sig is zero, error checking is
00303      // performed but no signal is actually sent.
00304      //
00305      int err;
00306      if ((err = pthread_kill(childTid, 0)) != 0) {
00307         if (err != ESRCH)
00308            LOG_ERROR;
00309         childTid = 0;
00310         active = running = false;
00311         }
00312      else
00313         return true;
00314      }
00315   return false;
00316 }
00317 
00318 void cThread::Cancel(int WaitSeconds)
00319 {
00320   running = false;
00321   if (active && WaitSeconds > -1) {
00322      if (WaitSeconds > 0) {
00323         for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
00324             if (!Active())
00325                return;
00326             cCondWait::SleepMs(10);
00327             }
00328         esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
00329         }
00330      pthread_cancel(childTid);
00331      childTid = 0;
00332      active = false;
00333      }
00334 }
00335 
00336 tThreadId cThread::ThreadId(void)
00337 {
00338   return syscall(__NR_gettid);
00339 }
00340 
00341 void cThread::SetMainThreadId(void)
00342 {
00343   if (mainThreadId == 0)
00344      mainThreadId = ThreadId();
00345   else
00346      esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
00347 }
00348 
00349 // --- cMutexLock ------------------------------------------------------------
00350 
00351 cMutexLock::cMutexLock(cMutex *Mutex)
00352 {
00353   mutex = NULL;
00354   locked = false;
00355   Lock(Mutex);
00356 }
00357 
00358 cMutexLock::~cMutexLock()
00359 {
00360   if (mutex && locked)
00361      mutex->Unlock();
00362 }
00363 
00364 bool cMutexLock::Lock(cMutex *Mutex)
00365 {
00366   if (Mutex && !mutex) {
00367      mutex = Mutex;
00368      Mutex->Lock();
00369      locked = true;
00370      return true;
00371      }
00372   return false;
00373 }
00374 
00375 // --- cThreadLock -----------------------------------------------------------
00376 
00377 cThreadLock::cThreadLock(cThread *Thread)
00378 {
00379   thread = NULL;
00380   locked = false;
00381   Lock(Thread);
00382 }
00383 
00384 cThreadLock::~cThreadLock()
00385 {
00386   if (thread && locked)
00387      thread->Unlock();
00388 }
00389 
00390 bool cThreadLock::Lock(cThread *Thread)
00391 {
00392   if (Thread && !thread) {
00393      thread = Thread;
00394      Thread->Lock();
00395      locked = true;
00396      return true;
00397      }
00398   return false;
00399 }
00400 
00401 // --- cPipe -----------------------------------------------------------------
00402 
00403 // cPipe::Open() and cPipe::Close() are based on code originally received from
00404 // Andreas Vitting <Andreas@huji.de>
00405 
00406 cPipe::cPipe(void)
00407 {
00408   pid = -1;
00409   f = NULL;
00410 }
00411 
00412 cPipe::~cPipe()
00413 {
00414   Close();
00415 }
00416 
00417 bool cPipe::Open(const char *Command, const char *Mode)
00418 {
00419   int fd[2];
00420 
00421   if (pipe(fd) < 0) {
00422      LOG_ERROR;
00423      return false;
00424      }
00425   if ((pid = fork()) < 0) { // fork failed
00426      LOG_ERROR;
00427      close(fd[0]);
00428      close(fd[1]);
00429      return false;
00430      }
00431 
00432   const char *mode = "w";
00433   int iopipe = 0;
00434 
00435   if (pid > 0) { // parent process
00436      if (strcmp(Mode, "r") == 0) {
00437         mode = "r";
00438         iopipe = 1;
00439         }
00440      close(fd[iopipe]);
00441      if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
00442         LOG_ERROR;
00443         close(fd[1 - iopipe]);
00444         }
00445      return f != NULL;
00446      }
00447   else { // child process
00448      int iofd = STDOUT_FILENO;
00449      if (strcmp(Mode, "w") == 0) {
00450         mode = "r";
00451         iopipe = 1;
00452         iofd = STDIN_FILENO;
00453         }
00454      close(fd[iopipe]);
00455      if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
00456         LOG_ERROR;
00457         close(fd[1 - iopipe]);
00458         _exit(-1);
00459         }
00460      else {
00461         int MaxPossibleFileDescriptors = getdtablesize();
00462         for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
00463             close(i); //close all dup'ed filedescriptors
00464         if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
00465            LOG_ERROR_STR(Command);
00466            close(fd[1 - iopipe]);
00467            _exit(-1);
00468            }
00469         }
00470      _exit(0);
00471      }
00472 }
00473 
00474 int cPipe::Close(void)
00475 {
00476   int ret = -1;
00477 
00478   if (f) {
00479      fclose(f);
00480      f = NULL;
00481      }
00482 
00483   if (pid > 0) {
00484      int status = 0;
00485      int i = 5;
00486      while (i > 0) {
00487            ret = waitpid(pid, &status, WNOHANG);
00488            if (ret < 0) {
00489               if (errno != EINTR && errno != ECHILD) {
00490                  LOG_ERROR;
00491                  break;
00492                  }
00493               }
00494            else if (ret == pid)
00495               break;
00496            i--;
00497            cCondWait::SleepMs(100);
00498            }
00499      if (!i) {
00500         kill(pid, SIGKILL);
00501         ret = -1;
00502         }
00503      else if (ret == -1 || !WIFEXITED(status))
00504         ret = -1;
00505      pid = -1;
00506      }
00507 
00508   return ret;
00509 }
00510 
00511 // --- SystemExec ------------------------------------------------------------
00512 
00513 int SystemExec(const char *Command, bool Detached)
00514 {
00515   pid_t pid;
00516 
00517   if ((pid = fork()) < 0) { // fork failed
00518      LOG_ERROR;
00519      return -1;
00520      }
00521 
00522   if (pid > 0) { // parent process
00523      int status = 0;
00524      if (waitpid(pid, &status, 0) < 0) {
00525         LOG_ERROR;
00526         return -1;
00527         }
00528      return status;
00529      }
00530   else { // child process
00531      if (Detached) {
00532         // Fork again and let first child die - grandchild stays alive without parent
00533         if (fork() > 0)
00534            _exit(0);
00535         // Start a new session
00536         pid_t sid = setsid();
00537         if (sid < 0)
00538            LOG_ERROR;
00539         // close STDIN and re-open as /dev/null
00540         int devnull = open("/dev/null", O_RDONLY);
00541         if (devnull < 0 || dup2(devnull, 0) < 0)
00542            LOG_ERROR;
00543         }
00544      int MaxPossibleFileDescriptors = getdtablesize();
00545      for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
00546          close(i); //close all dup'ed filedescriptors
00547      if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
00548         LOG_ERROR_STR(Command);
00549         _exit(-1);
00550         }
00551      _exit(0);
00552      }
00553 }
00554