diff options
Diffstat (limited to 'lib/thread.c')
-rw-r--r-- | lib/thread.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/lib/thread.c b/lib/thread.c new file mode 100644 index 0000000..5938d75 --- /dev/null +++ b/lib/thread.c @@ -0,0 +1,342 @@ +/* + * thread.c: + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <linux/unistd.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include "thread.h" + +//*************************************************************************** +// get abs time plus 'millisecondsFromNow' +//*************************************************************************** + +static bool absTime(struct timespec* abstime, int millisecondsFromNow) +{ + struct timeval now; + + if (gettimeofday(&now, 0) == 0) + { + // get current time + + now.tv_sec += millisecondsFromNow / 1000; // add full seconds + now.tv_usec += (millisecondsFromNow % 1000) * 1000; // add microseconds + + // take care of an overflow + + if (now.tv_usec >= 1000000) + { + now.tv_sec++; + now.tv_usec -= 1000000; + } + + abstime->tv_sec = now.tv_sec; // seconds + abstime->tv_nsec = now.tv_usec * 1000; // nano seconds + + return success; + } + + return fail; +} + +//*************************************************************************** +// Class cThread +//*************************************************************************** + +cThread::cThread(const char* Description, bool LowPriority) +{ + active = running = no; + childTid = 0; + childThreadId = 0; + description = 0; + silent = no; + + if (Description) + SetDescription("%s", Description); + + lowPriority = LowPriority; +} + +cThread::~cThread() +{ + Cancel(); // just in case the derived class didn't call it + free(description); +} + +void cThread::SetDescription(const char *Description, ...) +{ + free(description); + description = NULL; + + if (Description) + { + va_list ap; + va_start(ap, Description); + vasprintf(&description, Description, ap); + va_end(ap); + } +} + +void *cThread::StartThread(cThread *Thread) +{ + Thread->childThreadId = ThreadId(); + if (Thread->description) + { + tell(Thread->silent ? 2 : 0, "'%s' thread started (pid=%d, tid=%d, prio=%s)", Thread->description, getpid(), Thread->childThreadId, Thread->lowPriority ? "low" : "high"); +#ifdef PR_SET_NAME + if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0) + tell(0, "%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); +#endif + } + + if (Thread->lowPriority) + { + Thread->SetPriority(19); + Thread->SetIOPriority(7); + } + + Thread->action(); + + if (Thread->description) + tell(Thread->silent ? 2 : 0, "'%s' thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); + + Thread->running = false; + Thread->active = false; + + return NULL; +} + +//*************************************************************************** +// Priority +//*************************************************************************** + +void cThread::SetPriority(int priority) +{ + if (setpriority(PRIO_PROCESS, 0, priority) < 0) + tell(0, "Error: Setting priority failed"); +} + +void cThread::SetIOPriority(int priority) +{ + if (syscall(SYS_ioprio_set, 1, 0, (priority & 0xff) | (3 << 13)) < 0) // idle class + tell(0, "Error: Setting io priority failed"); +} + +//*************************************************************************** +// Start +//*************************************************************************** + +#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it +#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop + +bool cThread::Start(int s) +{ + silent = s; + + if (!running) + { + if (active) + { + // Wait until the previous incarnation of this thread has completely ended + // before starting it newly: + + cMyTimeMs RestartTimeout; + + while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT) + cCondWait::SleepMs(THREAD_STOP_SLEEP); + } + if (!active) + { + active = running = true; + + if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) + { + pthread_detach(childTid); // auto-reap + } + else + { + tell(0, "Error: Thread won't start"); + active = running = false; + return false; + } + } + } + + return true; +} + +bool cThread::Active(void) +{ + if (active) + { + int err; + + if ((err = pthread_kill(childTid, 0)) != 0) + { + if (err != ESRCH) + tell(0, "Error: Thread ..."); + childTid = 0; + active = running = false; + } + else + return true; + } + + return false; +} + +void cThread::Cancel(int WaitSeconds) +{ + running = false; + + if (active && WaitSeconds > -1) + { + if (WaitSeconds > 0) + { + for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) + { + if (!Active()) + return; + cCondWait::SleepMs(10); + } + + tell(0, "ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds); + } + + pthread_cancel(childTid); + childTid = 0; + active = false; + } +} + +pid_t cThread::ThreadId() +{ + return syscall(__NR_gettid); +} + +//*************************************************************************** +// cCondWait +//*************************************************************************** + +cCondWait::cCondWait() +{ + signaled = false; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); +} + +cCondWait::~cCondWait() +{ + pthread_cond_broadcast(&cond); // wake up any sleepers + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + +void cCondWait::SleepMs(int TimeoutMs) +{ + cCondWait w; + w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait +} + +bool cCondWait::Wait(int TimeoutMs) +{ + pthread_mutex_lock(&mutex); + + if (!signaled) + { + if (TimeoutMs) + { + struct timespec abstime; + + if (absTime(&abstime, TimeoutMs) == success) + { + while (!signaled) + { + if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT) + break; + } + } + } + else + pthread_cond_wait(&cond, &mutex); + } + + bool r = signaled; + signaled = false; + pthread_mutex_unlock(&mutex); + + return r; +} + +void cCondWait::Signal() +{ + pthread_mutex_lock(&mutex); + signaled = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); +} + +//*************************************************************************** +// cCondVar +//*************************************************************************** + +cCondVar::cCondVar(void) +{ + pthread_cond_init(&cond, 0); +} + +cCondVar::~cCondVar() +{ + pthread_cond_broadcast(&cond); // wake up any sleepers + pthread_cond_destroy(&cond); +} + +void cCondVar::Wait(cMyMutex &Mutex) +{ + if (Mutex.locked) + { + int locked = Mutex.locked; + Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait + // does an implicit unlock of the mutex + pthread_cond_wait(&cond, &Mutex.mutex); + Mutex.locked = locked; + } +} + +bool cCondVar::TimedWait(cMyMutex &Mutex, int TimeoutMs) +{ + bool r = true; // true = condition signaled, false = timeout + + if (Mutex.locked) + { + struct timespec abstime; + + if (absTime(&abstime, TimeoutMs) == success) + { + int locked = Mutex.locked; + + // have to clear the locked count here, as pthread_cond_timedwait + // does an implicit unlock of the mutex. + + Mutex.locked = 0; + + if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT) + r = false; + + Mutex.locked = locked; + } + } + + return r; +} + +void cCondVar::Broadcast(void) +{ + pthread_cond_broadcast(&cond); +} |