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

Generated on Tue Nov 6 19:57:56 2007 for VDR by  doxygen 1.5.3-20071008