SASC-NG
DVBSoftCAM

thread.cpp

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