Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

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.23 2002/11/03 11:00:03 kls Exp $
00008  */
00009 
00010 #include "thread.h"
00011 #include <errno.h>
00012 #include <signal.h>
00013 #include <sys/resource.h>
00014 #include <sys/time.h>
00015 #include <sys/wait.h>
00016 #include <unistd.h>
00017 #include "tools.h"
00018 
00019 // --- cCondVar --------------------------------------------------------------
00020 
00021 cCondVar::cCondVar(void)
00022 {
00023   pthread_cond_init(&cond, 0);
00024 }
00025 
00026 cCondVar::~cCondVar()
00027 {
00028   pthread_cond_destroy(&cond);
00029 }
00030 
00031 void cCondVar::Wait(cMutex &Mutex)
00032 {
00033   if (Mutex.locked && Mutex.lockingPid == getpid()) {
00034      int locked = Mutex.locked;
00035      Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
00036                        // does an implizit unlock of the mutex
00037      pthread_cond_wait(&cond, &Mutex.mutex);
00038      Mutex.locked = locked;
00039      }
00040 }
00041 
00042 bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
00043 {
00044   bool r = true; // true = condition signaled false = timeout
00045 
00046   if (Mutex.locked && Mutex.lockingPid == getpid()) {
00047      struct timeval now;                   // unfortunately timedwait needs the absolute time, not the delta :-(
00048      if (gettimeofday(&now, NULL) == 0) {  // get current time
00049         now.tv_usec += TimeoutMs * 1000;   // add the timeout
00050         while (now.tv_usec >= 1000000) {   // take care of an overflow
00051               now.tv_sec++;
00052               now.tv_usec -= 1000000;
00053               }
00054         struct timespec abstime;              // build timespec for timedwait
00055         abstime.tv_sec = now.tv_sec;          // seconds
00056         abstime.tv_nsec = now.tv_usec * 1000; // nano seconds
00057 
00058         int locked = Mutex.locked;
00059         Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
00060                           // does an implizit unlock of the mutex.
00061         if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
00062            r = false;
00063         Mutex.locked = locked;
00064         }
00065      }
00066   return r;
00067 }
00068 
00069 void cCondVar::Broadcast(void)
00070 {
00071   pthread_cond_broadcast(&cond);
00072 }
00073 
00074 /*
00075 void cCondVar::Signal(void)
00076 {
00077   pthread_cond_signal(&cond);
00078 }
00079 */
00080 
00081 // --- cMutex ----------------------------------------------------------------
00082 
00083 cMutex::cMutex(void)
00084 {
00085   lockingPid = 0;
00086   locked = 0;
00087   pthread_mutex_init(&mutex, NULL);
00088 }
00089 
00090 cMutex::~cMutex()
00091 {
00092   pthread_mutex_destroy(&mutex);
00093 }
00094 
00095 void cMutex::Lock(void)
00096 {
00097   if (getpid() != lockingPid || !locked) {
00098      pthread_mutex_lock(&mutex);
00099      lockingPid = getpid();
00100      }
00101   locked++;
00102 }
00103 
00104 void cMutex::Unlock(void)
00105 {
00106  if (!--locked) {
00107     lockingPid = 0;
00108     pthread_mutex_unlock(&mutex);
00109     }
00110 }
00111 
00112 // --- cThread ---------------------------------------------------------------
00113 
00114 // The signal handler is necessary to be able to use SIGIO to wake up any
00115 // pending 'select()' call.
00116 
00117 time_t cThread::lastPanic = 0;
00118 int cThread::panicLevel = 0;
00119 bool cThread::signalHandlerInstalled = false;
00120 bool cThread::emergencyExitRequested = false;
00121 
00122 cThread::cThread(void)
00123 {
00124   if (!signalHandlerInstalled) {
00125      signal(SIGIO, SignalHandler);
00126      signalHandlerInstalled = true;
00127      }
00128   running = false;
00129   parentPid = threadPid = 0;
00130 }
00131 
00132 cThread::~cThread()
00133 {
00134 }
00135 
00136 void cThread::SignalHandler(int signum)
00137 {
00138   signal(signum, SignalHandler);
00139 }
00140 
00141 void *cThread::StartThread(cThread *Thread)
00142 {
00143   Thread->threadPid = getpid();
00144   Thread->Action();
00145   return NULL;
00146 }
00147 
00148 bool cThread::Start(void)
00149 {
00150   if (!running) {
00151      running = true;
00152      parentPid = getpid();
00153      pthread_create(&thread, NULL, (void *(*) (void *))&StartThread, (void *)this);
00154      pthread_setschedparam(thread, SCHED_RR, 0);
00155      usleep(10000); // otherwise calling Active() immediately after Start() causes a "pure virtual method called" error
00156      }
00157   return true; //XXX return value of pthread_create()???
00158 }
00159 
00160 bool cThread::Active(void)
00161 {
00162   if (threadPid) {
00163      if (kill(threadPid, SIGIO) < 0) { // couldn't find another way of checking whether the thread is still running - any ideas?
00164         if (errno == ESRCH)
00165            threadPid = 0;
00166         else
00167            LOG_ERROR;
00168         }
00169      else
00170         return true;
00171      }
00172   return false;
00173 }
00174 
00175 void cThread::Cancel(int WaitSeconds)
00176 {
00177   running = false;
00178   if (WaitSeconds > 0) {
00179      for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
00180          if (!Active())
00181             return;
00182          usleep(10000);
00183          }
00184      esyslog("ERROR: thread %d won't end (waited %d seconds) - cancelling it...", threadPid, WaitSeconds);
00185      }
00186   pthread_cancel(thread);
00187 }
00188 
00189 void cThread::WakeUp(void)
00190 {
00191   kill(parentPid, SIGIO); // makes any waiting 'select()' call return immediately
00192 }
00193 
00194 #define MAXPANICLEVEL 10
00195 
00196 void cThread::RaisePanic(void)
00197 {
00198   if (lastPanic > 0) {
00199      if (time(NULL) - lastPanic < 5)
00200         panicLevel++;
00201      else if (panicLevel > 0)
00202         panicLevel--;
00203      }
00204   lastPanic = time(NULL);
00205   if (panicLevel > MAXPANICLEVEL) {
00206      esyslog("ERROR: max. panic level exceeded");
00207      EmergencyExit(true);
00208      }
00209   else
00210      dsyslog("panic level: %d", panicLevel);
00211 }
00212 
00213 bool cThread::EmergencyExit(bool Request)
00214 {
00215   if (!Request)
00216      return emergencyExitRequested;
00217   esyslog("initiating emergency exit");
00218   return emergencyExitRequested = true; // yes, it's an assignment, not a comparison!
00219 }
00220 
00221 // --- cMutexLock ------------------------------------------------------------
00222 
00223 cMutexLock::cMutexLock(cMutex *Mutex)
00224 {
00225   mutex = NULL;
00226   locked = false;
00227   Lock(Mutex);
00228 }
00229 
00230 cMutexLock::~cMutexLock()
00231 {
00232   if (mutex && locked)
00233      mutex->Unlock();
00234 }
00235 
00236 bool cMutexLock::Lock(cMutex *Mutex)
00237 {
00238   if (Mutex && !mutex) {
00239      mutex = Mutex;
00240      Mutex->Lock();
00241      locked = true;
00242      return true;
00243      }
00244   return false;
00245 }
00246 
00247 // --- cThreadLock -----------------------------------------------------------
00248 
00249 cThreadLock::cThreadLock(cThread *Thread)
00250 {
00251   thread = NULL;
00252   locked = false;
00253   Lock(Thread);
00254 }
00255 
00256 cThreadLock::~cThreadLock()
00257 {
00258   if (thread && locked)
00259      thread->Unlock();
00260 }
00261 
00262 bool cThreadLock::Lock(cThread *Thread)
00263 {
00264   if (Thread && !thread) {
00265      thread = Thread;
00266      Thread->Lock();
00267      locked = true;
00268      return true;
00269      }
00270   return false;
00271 }
00272 
00273 // --- cPipe -----------------------------------------------------------------
00274 
00275 // cPipe::Open() and cPipe::Close() are based on code originally received from
00276 // Andreas Vitting <Andreas@huji.de>
00277 
00278 cPipe::cPipe(void)
00279 {
00280   pid = -1;
00281   f = NULL;
00282 }
00283 
00284 cPipe::~cPipe()
00285 {
00286   Close();
00287 }
00288 
00289 bool cPipe::Open(const char *Command, const char *Mode)
00290 {
00291   int fd[2];
00292 
00293   if (pipe(fd) < 0) {
00294      LOG_ERROR;
00295      return false;
00296      }
00297   if ((pid = fork()) < 0) { // fork failed
00298      LOG_ERROR;
00299      close(fd[0]);
00300      close(fd[1]);
00301      return false;
00302      }
00303 
00304   char *mode = "w";
00305   int iopipe = 0;
00306 
00307   if (pid > 0) { // parent process
00308      if (strcmp(Mode, "r") == 0) {
00309         mode = "r";
00310         iopipe = 1;
00311         }
00312      close(fd[iopipe]);
00313      f = fdopen(fd[1 - iopipe], mode);
00314      if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
00315         LOG_ERROR;
00316         close(fd[1 - iopipe]);
00317         }
00318      return f != NULL;
00319      }
00320   else { // child process
00321      int iofd = STDOUT_FILENO;
00322      if (strcmp(Mode, "w") == 0) {
00323         mode = "r";
00324         iopipe = 1;
00325         iofd = STDIN_FILENO;
00326         }
00327      close(fd[iopipe]);
00328      if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
00329         LOG_ERROR;
00330         close(fd[1 - iopipe]);
00331         _exit(-1);
00332         }
00333      else {
00334         int MaxPossibleFileDescriptors = getdtablesize();
00335         for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
00336             close(i); //close all dup'ed filedescriptors
00337         if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
00338            LOG_ERROR_STR(Command);
00339            close(fd[1 - iopipe]);
00340            _exit(-1);
00341            }
00342         }
00343      _exit(0);
00344      }
00345 }
00346 
00347 int cPipe::Close(void)
00348 {
00349   int ret = -1;
00350 
00351   if (f) {
00352      fclose(f);
00353      f = NULL;
00354      }
00355 
00356   if (pid > 0) {
00357      int status = 0;
00358      int i = 5;
00359      while (i > 0) {
00360            ret = waitpid(pid, &status, WNOHANG);
00361            if (ret < 0) {
00362               if (errno != EINTR && errno != ECHILD) {
00363                  LOG_ERROR;
00364                  break;
00365                  }
00366               }
00367            else if (ret == pid)
00368               break;
00369            i--;
00370            usleep(100000);
00371            }
00372      if (!i) {
00373         kill(pid, SIGKILL);
00374         ret = -1;
00375         }
00376      else if (ret == -1 || !WIFEXITED(status))
00377         ret = -1;
00378      pid = -1;
00379      }
00380 
00381   return ret;
00382 }
00383 
00384 // --- SystemExec ------------------------------------------------------------
00385 
00386 int SystemExec(const char *Command)
00387 {
00388   pid_t pid;
00389 
00390   if ((pid = fork()) < 0) { // fork failed
00391      LOG_ERROR;
00392      return -1;
00393      }
00394 
00395   if (pid > 0) { // parent process
00396      int status;
00397      if (waitpid(pid, &status, 0) < 0) {
00398         LOG_ERROR;
00399         return -1;
00400         }
00401      return status;
00402      }
00403   else { // child process
00404      int MaxPossibleFileDescriptors = getdtablesize();
00405      for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
00406          close(i); //close all dup'ed filedescriptors
00407      if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
00408         LOG_ERROR_STR(Command);
00409         _exit(-1);
00410         }
00411      _exit(0);
00412      }
00413 }
00414 

Generated on Wed Feb 5 23:30:12 2003 for VDR by doxygen1.3-rc2