|
SASC-NG
DVBSoftCAM
|
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