vdr
1.7.27
|
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