2000-10-08 09:25:20 +02:00
|
|
|
/*
|
|
|
|
* thread.c: A simple thread base class
|
|
|
|
*
|
|
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
|
|
* how to reach the author.
|
|
|
|
*
|
2016-12-08 10:18:32 +01:00
|
|
|
* $Id: thread.c 4.2 2016/12/08 10:18:32 kls Exp $
|
2000-10-08 09:25:20 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "thread.h"
|
2000-12-08 16:23:32 +01:00
|
|
|
#include <errno.h>
|
2006-01-08 16:05:23 +01:00
|
|
|
#include <linux/unistd.h>
|
2003-10-18 12:29:08 +02:00
|
|
|
#include <malloc.h>
|
|
|
|
#include <stdarg.h>
|
2007-06-17 12:45:57 +02:00
|
|
|
#include <stdlib.h>
|
2001-09-15 13:00:58 +02:00
|
|
|
#include <sys/resource.h>
|
2005-12-11 12:10:28 +01:00
|
|
|
#include <sys/syscall.h>
|
2002-08-15 11:46:22 +02:00
|
|
|
#include <sys/time.h>
|
2000-12-08 16:23:32 +01:00
|
|
|
#include <sys/wait.h>
|
2008-04-13 12:14:58 +02:00
|
|
|
#include <sys/prctl.h>
|
2000-10-08 09:25:20 +02:00
|
|
|
#include <unistd.h>
|
2000-12-08 16:23:32 +01:00
|
|
|
#include "tools.h"
|
2000-10-08 09:25:20 +02:00
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
#define ABORT { dsyslog("ABORT!"); abort(); } // use debugger to trace back the problem
|
|
|
|
|
|
|
|
//#define DEBUG_LOCKING // uncomment this line to activate debug output for locking
|
|
|
|
|
|
|
|
#ifdef DEBUG_LOCKING
|
|
|
|
#define dbglocking(a...) fprintf(stderr, a)
|
|
|
|
#else
|
|
|
|
#define dbglocking(a...)
|
|
|
|
#endif
|
|
|
|
|
2005-05-06 14:43:17 +02:00
|
|
|
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
|
|
|
|
{
|
|
|
|
struct timeval now;
|
|
|
|
if (gettimeofday(&now, NULL) == 0) { // get current time
|
2008-09-06 09:41:15 +02:00
|
|
|
now.tv_sec += MillisecondsFromNow / 1000; // add full seconds
|
|
|
|
now.tv_usec += (MillisecondsFromNow % 1000) * 1000; // add microseconds
|
|
|
|
if (now.tv_usec >= 1000000) { // take care of an overflow
|
|
|
|
now.tv_sec++;
|
|
|
|
now.tv_usec -= 1000000;
|
|
|
|
}
|
2005-05-06 14:43:17 +02:00
|
|
|
Abstime->tv_sec = now.tv_sec; // seconds
|
|
|
|
Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
// --- cCondWait -------------------------------------------------------------
|
|
|
|
|
|
|
|
cCondWait::cCondWait(void)
|
|
|
|
{
|
|
|
|
signaled = false;
|
|
|
|
pthread_mutex_init(&mutex, NULL);
|
|
|
|
pthread_cond_init(&cond, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
cCondWait::~cCondWait()
|
|
|
|
{
|
2004-10-31 09:54:50 +01:00
|
|
|
pthread_cond_broadcast(&cond); // wake up any sleepers
|
2004-10-16 09:36:28 +02:00
|
|
|
pthread_cond_destroy(&cond);
|
|
|
|
pthread_mutex_destroy(&mutex);
|
|
|
|
}
|
|
|
|
|
2004-10-24 11:12:05 +02:00
|
|
|
void cCondWait::SleepMs(int TimeoutMs)
|
|
|
|
{
|
|
|
|
cCondWait w;
|
2005-01-14 14:08:47 +01:00
|
|
|
w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
|
2004-10-24 11:12:05 +02:00
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
bool cCondWait::Wait(int TimeoutMs)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
if (!signaled) {
|
|
|
|
if (TimeoutMs) {
|
2005-05-06 14:43:17 +02:00
|
|
|
struct timespec abstime;
|
|
|
|
if (GetAbsTime(&abstime, TimeoutMs)) {
|
2004-10-16 09:36:28 +02:00
|
|
|
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(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
signaled = true;
|
2004-10-31 09:54:50 +01:00
|
|
|
pthread_cond_broadcast(&cond);
|
2004-10-16 09:36:28 +02:00
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
}
|
|
|
|
|
2001-08-03 14:18:08 +02:00
|
|
|
// --- cCondVar --------------------------------------------------------------
|
|
|
|
|
|
|
|
cCondVar::cCondVar(void)
|
|
|
|
{
|
|
|
|
pthread_cond_init(&cond, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cCondVar::~cCondVar()
|
|
|
|
{
|
2004-10-31 09:54:50 +01:00
|
|
|
pthread_cond_broadcast(&cond); // wake up any sleepers
|
2001-08-03 14:18:08 +02:00
|
|
|
pthread_cond_destroy(&cond);
|
|
|
|
}
|
|
|
|
|
2002-08-15 11:46:22 +02:00
|
|
|
void cCondVar::Wait(cMutex &Mutex)
|
2001-08-03 14:18:08 +02:00
|
|
|
{
|
2003-10-18 13:20:01 +02:00
|
|
|
if (Mutex.locked) {
|
2002-08-15 11:46:22 +02:00
|
|
|
int locked = Mutex.locked;
|
|
|
|
Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
|
2006-01-28 11:34:35 +01:00
|
|
|
// does an implicit unlock of the mutex
|
2002-08-15 11:46:22 +02:00
|
|
|
pthread_cond_wait(&cond, &Mutex.mutex);
|
|
|
|
Mutex.locked = locked;
|
|
|
|
}
|
2001-08-03 14:18:08 +02:00
|
|
|
}
|
|
|
|
|
2002-08-15 11:46:22 +02:00
|
|
|
bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
|
2001-08-03 14:18:08 +02:00
|
|
|
{
|
2006-01-28 11:34:35 +01:00
|
|
|
bool r = true; // true = condition signaled, false = timeout
|
2002-08-15 11:46:22 +02:00
|
|
|
|
2003-10-18 13:20:01 +02:00
|
|
|
if (Mutex.locked) {
|
2005-05-06 14:43:17 +02:00
|
|
|
struct timespec abstime;
|
|
|
|
if (GetAbsTime(&abstime, TimeoutMs)) {
|
2002-08-15 11:46:22 +02:00
|
|
|
int locked = Mutex.locked;
|
|
|
|
Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
|
2006-01-28 11:34:35 +01:00
|
|
|
// does an implicit unlock of the mutex.
|
2002-08-15 11:46:22 +02:00
|
|
|
if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
|
|
|
|
r = false;
|
|
|
|
Mutex.locked = locked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
2001-08-03 14:18:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void cCondVar::Broadcast(void)
|
|
|
|
{
|
|
|
|
pthread_cond_broadcast(&cond);
|
|
|
|
}
|
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
// --- cRwLock ---------------------------------------------------------------
|
2003-12-22 13:29:24 +01:00
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
cRwLock::cRwLock(bool PreferWriter)
|
2003-12-22 13:29:24 +01:00
|
|
|
{
|
2016-12-08 10:18:32 +01:00
|
|
|
locked = 0;
|
|
|
|
writeLockThreadId = 0;
|
2006-01-01 14:53:03 +01:00
|
|
|
pthread_rwlockattr_t attr;
|
|
|
|
pthread_rwlockattr_init(&attr);
|
|
|
|
pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
|
2003-12-22 13:29:24 +01:00
|
|
|
pthread_rwlock_init(&rwlock, &attr);
|
|
|
|
}
|
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
cRwLock::~cRwLock()
|
2003-12-22 13:29:24 +01:00
|
|
|
{
|
|
|
|
pthread_rwlock_destroy(&rwlock);
|
|
|
|
}
|
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
bool cRwLock::Lock(bool Write, int TimeoutMs)
|
2003-12-22 13:29:24 +01:00
|
|
|
{
|
|
|
|
int Result = 0;
|
|
|
|
struct timespec abstime;
|
|
|
|
if (TimeoutMs) {
|
2005-05-06 14:43:17 +02:00
|
|
|
if (!GetAbsTime(&abstime, TimeoutMs))
|
|
|
|
TimeoutMs = 0;
|
2003-12-22 13:29:24 +01:00
|
|
|
}
|
2016-12-08 10:18:32 +01:00
|
|
|
if (Write) {
|
2003-12-22 13:29:24 +01:00
|
|
|
Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
|
2016-12-08 10:18:32 +01:00
|
|
|
if (Result == 0)
|
|
|
|
writeLockThreadId = cThread::ThreadId();
|
|
|
|
}
|
|
|
|
else if (writeLockThreadId == cThread::ThreadId()) {
|
|
|
|
locked++; // there can be any number of stacked read locks, so we keep track here
|
|
|
|
Result = 0; // aquiring a read lock while holding a write lock within the same thread is OK
|
|
|
|
}
|
2003-12-22 13:29:24 +01:00
|
|
|
else
|
|
|
|
Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
|
|
|
|
return Result == 0;
|
|
|
|
}
|
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
void cRwLock::Unlock(void)
|
2003-12-22 13:29:24 +01:00
|
|
|
{
|
2016-12-08 10:18:32 +01:00
|
|
|
if (writeLockThreadId == cThread::ThreadId()) { // this is the thread that obtained the initial write lock
|
|
|
|
if (locked) { // this is the unlock of a read lock within the write lock
|
|
|
|
locked--;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
writeLockThreadId = 0;
|
2003-12-22 13:29:24 +01:00
|
|
|
pthread_rwlock_unlock(&rwlock);
|
|
|
|
}
|
|
|
|
|
2001-06-02 10:47:40 +02:00
|
|
|
// --- cMutex ----------------------------------------------------------------
|
|
|
|
|
|
|
|
cMutex::cMutex(void)
|
|
|
|
{
|
|
|
|
locked = 0;
|
2006-01-01 14:53:03 +01:00
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
pthread_mutexattr_init(&attr);
|
|
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
|
2003-10-18 13:20:01 +02:00
|
|
|
pthread_mutex_init(&mutex, &attr);
|
2001-06-02 10:47:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cMutex::~cMutex()
|
|
|
|
{
|
|
|
|
pthread_mutex_destroy(&mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cMutex::Lock(void)
|
|
|
|
{
|
2003-10-18 13:20:01 +02:00
|
|
|
pthread_mutex_lock(&mutex);
|
2001-06-02 10:47:40 +02:00
|
|
|
locked++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cMutex::Unlock(void)
|
|
|
|
{
|
2016-12-08 10:18:32 +01:00
|
|
|
if (!--locked)
|
|
|
|
pthread_mutex_unlock(&mutex);
|
2001-06-02 10:47:40 +02:00
|
|
|
}
|
|
|
|
|
2000-10-08 09:25:20 +02:00
|
|
|
// --- cThread ---------------------------------------------------------------
|
|
|
|
|
2006-01-03 10:20:41 +01:00
|
|
|
tThreadId cThread::mainThreadId = 0;
|
2000-10-08 09:25:20 +02:00
|
|
|
|
2012-10-04 12:32:31 +02:00
|
|
|
cThread::cThread(const char *Description, bool LowPriority)
|
2000-10-08 09:25:20 +02:00
|
|
|
{
|
2005-08-14 11:24:57 +02:00
|
|
|
active = running = false;
|
2004-12-19 10:58:20 +01:00
|
|
|
childTid = 0;
|
2006-01-04 15:01:22 +01:00
|
|
|
childThreadId = 0;
|
2003-10-18 12:29:08 +02:00
|
|
|
description = NULL;
|
2006-03-26 09:27:30 +02:00
|
|
|
if (Description)
|
|
|
|
SetDescription("%s", Description);
|
2012-10-04 12:32:31 +02:00
|
|
|
lowPriority = LowPriority;
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cThread::~cThread()
|
|
|
|
{
|
2005-08-13 13:17:24 +02:00
|
|
|
Cancel(); // just in case the derived class didn't call it
|
2003-10-18 12:29:08 +02:00
|
|
|
free(description);
|
|
|
|
}
|
|
|
|
|
2005-05-29 11:44:52 +02:00
|
|
|
void cThread::SetPriority(int Priority)
|
|
|
|
{
|
|
|
|
if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
|
|
|
|
LOG_ERROR;
|
|
|
|
}
|
|
|
|
|
2009-04-13 13:55:23 +02:00
|
|
|
void cThread::SetIOPriority(int Priority)
|
|
|
|
{
|
2013-04-10 15:38:29 +02:00
|
|
|
if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0) // idle class
|
2009-04-13 13:55:23 +02:00
|
|
|
LOG_ERROR;
|
|
|
|
}
|
|
|
|
|
2003-10-18 12:29:08 +02:00
|
|
|
void cThread::SetDescription(const char *Description, ...)
|
|
|
|
{
|
|
|
|
free(description);
|
|
|
|
description = NULL;
|
|
|
|
if (Description) {
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, Description);
|
2012-05-08 11:23:56 +02:00
|
|
|
description = strdup(cString::vsprintf(Description, ap));
|
2003-10-18 12:29:08 +02:00
|
|
|
va_end(ap);
|
|
|
|
}
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void *cThread::StartThread(cThread *Thread)
|
|
|
|
{
|
2006-01-04 15:01:22 +01:00
|
|
|
Thread->childThreadId = ThreadId();
|
2008-04-13 12:14:58 +02:00
|
|
|
if (Thread->description) {
|
2012-10-04 12:32:31 +02:00
|
|
|
dsyslog("%s thread started (pid=%d, tid=%d, prio=%s)", Thread->description, getpid(), Thread->childThreadId, Thread->lowPriority ? "low" : "high");
|
2008-04-13 12:14:58 +02:00
|
|
|
#ifdef PR_SET_NAME
|
|
|
|
if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
|
|
|
|
esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
|
|
|
|
#endif
|
|
|
|
}
|
2012-10-04 12:32:31 +02:00
|
|
|
if (Thread->lowPriority) {
|
|
|
|
Thread->SetPriority(19);
|
|
|
|
Thread->SetIOPriority(7);
|
|
|
|
}
|
2000-10-08 09:25:20 +02:00
|
|
|
Thread->Action();
|
2003-10-18 12:29:08 +02:00
|
|
|
if (Thread->description)
|
2006-01-04 15:01:22 +01:00
|
|
|
dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
|
2004-12-19 10:58:20 +01:00
|
|
|
Thread->running = false;
|
2005-08-14 11:24:57 +02:00
|
|
|
Thread->active = false;
|
2000-10-08 09:25:20 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-01-07 14:46:14 +01:00
|
|
|
#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
|
|
|
|
|
2000-10-08 09:25:20 +02:00
|
|
|
bool cThread::Start(void)
|
|
|
|
{
|
2007-01-07 14:46:14 +01:00
|
|
|
if (!running) {
|
|
|
|
if (active) {
|
|
|
|
// Wait until the previous incarnation of this thread has completely ended
|
|
|
|
// before starting it newly:
|
|
|
|
cTimeMs RestartTimeout;
|
|
|
|
while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
|
|
|
|
cCondWait::SleepMs(THREAD_STOP_SLEEP);
|
2004-12-19 10:58:20 +01:00
|
|
|
}
|
2007-01-07 14:46:14 +01:00
|
|
|
if (!active) {
|
|
|
|
active = running = true;
|
|
|
|
if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
|
|
|
|
pthread_detach(childTid); // auto-reap
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG_ERROR;
|
|
|
|
active = running = false;
|
|
|
|
return false;
|
|
|
|
}
|
2004-12-19 10:58:20 +01:00
|
|
|
}
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
2004-12-19 10:58:20 +01:00
|
|
|
return true;
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
|
|
|
|
2005-08-14 11:24:57 +02:00
|
|
|
bool cThread::Active(void)
|
2000-10-08 09:25:20 +02:00
|
|
|
{
|
2005-08-14 11:24:57 +02:00
|
|
|
if (active) {
|
2003-10-18 11:14:33 +02:00
|
|
|
//
|
|
|
|
// Single UNIX Spec v2 says:
|
|
|
|
//
|
|
|
|
// The pthread_kill() function is used to request
|
|
|
|
// that a signal be delivered to the specified thread.
|
|
|
|
//
|
|
|
|
// As in kill(), if sig is zero, error checking is
|
|
|
|
// performed but no signal is actually sent.
|
|
|
|
//
|
|
|
|
int err;
|
|
|
|
if ((err = pthread_kill(childTid, 0)) != 0) {
|
|
|
|
if (err != ESRCH)
|
2000-12-08 16:23:32 +01:00
|
|
|
LOG_ERROR;
|
2003-10-18 11:14:33 +02:00
|
|
|
childTid = 0;
|
2005-08-14 11:24:57 +02:00
|
|
|
active = running = false;
|
2000-12-08 16:23:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cThread::Cancel(int WaitSeconds)
|
|
|
|
{
|
2005-08-14 11:24:57 +02:00
|
|
|
running = false;
|
2006-09-24 12:54:47 +02:00
|
|
|
if (active && WaitSeconds > -1) {
|
2004-10-24 10:34:20 +02:00
|
|
|
if (WaitSeconds > 0) {
|
|
|
|
for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
|
2005-08-14 11:24:57 +02:00
|
|
|
if (!Active())
|
2004-10-24 10:34:20 +02:00
|
|
|
return;
|
2004-10-24 11:12:05 +02:00
|
|
|
cCondWait::SleepMs(10);
|
2004-10-24 10:34:20 +02:00
|
|
|
}
|
2006-02-12 12:26:06 +01:00
|
|
|
esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
|
2004-10-24 10:34:20 +02:00
|
|
|
}
|
2004-12-19 10:58:20 +01:00
|
|
|
pthread_cancel(childTid);
|
|
|
|
childTid = 0;
|
2005-08-14 11:24:57 +02:00
|
|
|
active = false;
|
2000-12-08 16:23:32 +01:00
|
|
|
}
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
|
|
|
|
2005-12-11 12:10:28 +01:00
|
|
|
tThreadId cThread::ThreadId(void)
|
|
|
|
{
|
2006-08-20 10:28:35 +02:00
|
|
|
return syscall(__NR_gettid);
|
2005-12-11 12:10:28 +01:00
|
|
|
}
|
|
|
|
|
2006-01-03 10:20:41 +01:00
|
|
|
void cThread::SetMainThreadId(void)
|
|
|
|
{
|
|
|
|
if (mainThreadId == 0)
|
|
|
|
mainThreadId = ThreadId();
|
|
|
|
else
|
|
|
|
esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
|
|
|
|
}
|
|
|
|
|
2002-02-23 13:55:57 +01:00
|
|
|
// --- cMutexLock ------------------------------------------------------------
|
|
|
|
|
|
|
|
cMutexLock::cMutexLock(cMutex *Mutex)
|
|
|
|
{
|
|
|
|
mutex = NULL;
|
|
|
|
locked = false;
|
|
|
|
Lock(Mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
cMutexLock::~cMutexLock()
|
|
|
|
{
|
|
|
|
if (mutex && locked)
|
|
|
|
mutex->Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cMutexLock::Lock(cMutex *Mutex)
|
|
|
|
{
|
|
|
|
if (Mutex && !mutex) {
|
|
|
|
mutex = Mutex;
|
|
|
|
Mutex->Lock();
|
|
|
|
locked = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2000-10-08 09:25:20 +02:00
|
|
|
// --- cThreadLock -----------------------------------------------------------
|
|
|
|
|
|
|
|
cThreadLock::cThreadLock(cThread *Thread)
|
|
|
|
{
|
2000-10-29 13:17:22 +01:00
|
|
|
thread = NULL;
|
|
|
|
locked = false;
|
|
|
|
Lock(Thread);
|
2000-10-08 09:25:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cThreadLock::~cThreadLock()
|
|
|
|
{
|
2000-10-29 13:17:22 +01:00
|
|
|
if (thread && locked)
|
2000-10-08 09:25:20 +02:00
|
|
|
thread->Unlock();
|
|
|
|
}
|
|
|
|
|
2000-10-29 13:17:22 +01:00
|
|
|
bool cThreadLock::Lock(cThread *Thread)
|
|
|
|
{
|
|
|
|
if (Thread && !thread) {
|
|
|
|
thread = Thread;
|
2001-10-27 13:23:06 +02:00
|
|
|
Thread->Lock();
|
|
|
|
locked = true;
|
|
|
|
return true;
|
2000-10-29 13:17:22 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
// --- cStateLock ------------------------------------------------------------
|
|
|
|
|
|
|
|
cStateLock::cStateLock(const char *Name)
|
|
|
|
:rwLock(true)
|
|
|
|
{
|
|
|
|
name = Name;
|
|
|
|
threadId = 0;
|
|
|
|
state = 0;
|
|
|
|
explicitModify = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs)
|
|
|
|
{
|
|
|
|
dbglocking("%5d %-10s %10p lock state = %d/%d write = %d timeout = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, Write, TimeoutMs);
|
|
|
|
StateKey.timedOut = false;
|
|
|
|
if (StateKey.stateLock) {
|
|
|
|
esyslog("ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s)", StateKey.stateLock->threadId, name);
|
|
|
|
ABORT;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (rwLock.Lock(Write, TimeoutMs)) {
|
|
|
|
StateKey.stateLock = this;
|
|
|
|
if (Write) {
|
|
|
|
dbglocking("%5d %-10s %10p locked write\n", cThread::ThreadId(), name, &StateKey);
|
|
|
|
threadId = cThread::ThreadId();
|
|
|
|
StateKey.write = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (state != StateKey.state) {
|
|
|
|
dbglocking("%5d %-10s %10p locked read\n", cThread::ThreadId(), name, &StateKey);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dbglocking("%5d %-10s %10p state unchanged\n", cThread::ThreadId(), name, &StateKey);
|
|
|
|
StateKey.stateLock = NULL;
|
|
|
|
rwLock.Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TimeoutMs) {
|
|
|
|
dbglocking("%5d %-10s %10p timeout\n", cThread::ThreadId(), name, &StateKey);
|
|
|
|
StateKey.timedOut = true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
|
|
|
|
{
|
|
|
|
dbglocking("%5d %-10s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState);
|
|
|
|
if (StateKey.stateLock != this) {
|
|
|
|
esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name);
|
|
|
|
ABORT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (StateKey.write && threadId != cThread::ThreadId()) {
|
|
|
|
esyslog("ERROR: cStateLock::Unlock() called without holding a lock (tid=%d, lock=%s)", threadId, name);
|
|
|
|
ABORT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (StateKey.write && IncState && !explicitModify)
|
|
|
|
state++;
|
|
|
|
StateKey.state = state;
|
2016-12-08 10:18:32 +01:00
|
|
|
if (StateKey.write) {
|
|
|
|
StateKey.write = false;
|
|
|
|
threadId = 0;
|
|
|
|
explicitModify = false;
|
|
|
|
}
|
2015-09-01 11:14:27 +02:00
|
|
|
rwLock.Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cStateLock::IncState(void)
|
|
|
|
{
|
|
|
|
if (threadId != cThread::ThreadId()) {
|
|
|
|
esyslog("ERROR: cStateLock::IncState() called without holding a lock (tid=%d, lock=%s)", threadId, name);
|
|
|
|
ABORT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
state++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- cStateKey -------------------------------------------------------------
|
|
|
|
|
|
|
|
cStateKey::cStateKey(bool IgnoreFirst)
|
|
|
|
{
|
|
|
|
stateLock = NULL;
|
|
|
|
write = false;
|
|
|
|
state = 0;
|
|
|
|
if (!IgnoreFirst)
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
cStateKey::~cStateKey()
|
|
|
|
{
|
|
|
|
if (stateLock) {
|
|
|
|
esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this);
|
|
|
|
ABORT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cStateKey::Reset(void)
|
|
|
|
{
|
|
|
|
state = -1; // lock and key are initialized differently, to make the first check return true
|
|
|
|
}
|
|
|
|
|
|
|
|
void cStateKey::Remove(bool IncState)
|
|
|
|
{
|
|
|
|
if (stateLock) {
|
|
|
|
stateLock->Unlock(*this, IncState);
|
|
|
|
stateLock = NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
|
|
|
|
ABORT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cStateKey::StateChanged(void)
|
|
|
|
{
|
|
|
|
if (!stateLock) {
|
|
|
|
esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this);
|
|
|
|
ABORT;
|
|
|
|
}
|
|
|
|
else if (write)
|
|
|
|
return state != stateLock->state;
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-09-22 11:52:33 +02:00
|
|
|
// --- cIoThrottle -----------------------------------------------------------
|
|
|
|
|
|
|
|
cMutex cIoThrottle::mutex;
|
|
|
|
int cIoThrottle::count = 0;
|
|
|
|
|
|
|
|
cIoThrottle::cIoThrottle(void)
|
|
|
|
{
|
|
|
|
active = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cIoThrottle::~cIoThrottle()
|
|
|
|
{
|
|
|
|
Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIoThrottle::Activate(void)
|
|
|
|
{
|
|
|
|
if (!active) {
|
|
|
|
mutex.Lock();
|
|
|
|
count++;
|
|
|
|
active = true;
|
|
|
|
dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId());
|
|
|
|
mutex.Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIoThrottle::Release(void)
|
|
|
|
{
|
|
|
|
if (active) {
|
|
|
|
mutex.Lock();
|
|
|
|
count--;
|
|
|
|
active = false;
|
|
|
|
dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId());
|
|
|
|
mutex.Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIoThrottle::Engaged(void)
|
|
|
|
{
|
|
|
|
return count > 0;
|
|
|
|
}
|
|
|
|
|
2001-09-15 13:00:58 +02:00
|
|
|
// --- cPipe -----------------------------------------------------------------
|
|
|
|
|
|
|
|
// cPipe::Open() and cPipe::Close() are based on code originally received from
|
|
|
|
// Andreas Vitting <Andreas@huji.de>
|
|
|
|
|
|
|
|
cPipe::cPipe(void)
|
|
|
|
{
|
|
|
|
pid = -1;
|
|
|
|
f = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cPipe::~cPipe()
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cPipe::Open(const char *Command, const char *Mode)
|
|
|
|
{
|
|
|
|
int fd[2];
|
|
|
|
|
|
|
|
if (pipe(fd) < 0) {
|
|
|
|
LOG_ERROR;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((pid = fork()) < 0) { // fork failed
|
|
|
|
LOG_ERROR;
|
|
|
|
close(fd[0]);
|
|
|
|
close(fd[1]);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-08-24 13:18:21 +02:00
|
|
|
const char *mode = "w";
|
2001-09-15 13:00:58 +02:00
|
|
|
int iopipe = 0;
|
|
|
|
|
|
|
|
if (pid > 0) { // parent process
|
|
|
|
if (strcmp(Mode, "r") == 0) {
|
|
|
|
mode = "r";
|
|
|
|
iopipe = 1;
|
|
|
|
}
|
|
|
|
close(fd[iopipe]);
|
|
|
|
if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
|
|
|
|
LOG_ERROR;
|
|
|
|
close(fd[1 - iopipe]);
|
|
|
|
}
|
|
|
|
return f != NULL;
|
|
|
|
}
|
|
|
|
else { // child process
|
|
|
|
int iofd = STDOUT_FILENO;
|
|
|
|
if (strcmp(Mode, "w") == 0) {
|
|
|
|
iopipe = 1;
|
|
|
|
iofd = STDIN_FILENO;
|
|
|
|
}
|
|
|
|
close(fd[iopipe]);
|
|
|
|
if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
|
|
|
|
LOG_ERROR;
|
|
|
|
close(fd[1 - iopipe]);
|
|
|
|
_exit(-1);
|
|
|
|
}
|
|
|
|
else {
|
2002-11-02 14:00:25 +01:00
|
|
|
int MaxPossibleFileDescriptors = getdtablesize();
|
|
|
|
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
|
|
|
close(i); //close all dup'ed filedescriptors
|
2001-09-15 13:00:58 +02:00
|
|
|
if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
|
|
|
|
LOG_ERROR_STR(Command);
|
|
|
|
close(fd[1 - iopipe]);
|
|
|
|
_exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cPipe::Close(void)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
fclose(f);
|
|
|
|
f = NULL;
|
|
|
|
}
|
|
|
|
|
2002-03-09 11:51:56 +01:00
|
|
|
if (pid > 0) {
|
2001-09-15 13:00:58 +02:00
|
|
|
int status = 0;
|
|
|
|
int i = 5;
|
2002-03-09 11:51:56 +01:00
|
|
|
while (i > 0) {
|
|
|
|
ret = waitpid(pid, &status, WNOHANG);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno != EINTR && errno != ECHILD) {
|
|
|
|
LOG_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ret == pid)
|
|
|
|
break;
|
2001-09-15 13:00:58 +02:00
|
|
|
i--;
|
2004-10-24 11:12:05 +02:00
|
|
|
cCondWait::SleepMs(100);
|
2001-09-15 13:00:58 +02:00
|
|
|
}
|
|
|
|
if (!i) {
|
|
|
|
kill(pid, SIGKILL);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
else if (ret == -1 || !WIFEXITED(status))
|
|
|
|
ret = -1;
|
|
|
|
pid = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2001-10-20 10:39:27 +02:00
|
|
|
|
|
|
|
// --- SystemExec ------------------------------------------------------------
|
|
|
|
|
2007-02-25 10:56:29 +01:00
|
|
|
int SystemExec(const char *Command, bool Detached)
|
2001-10-20 10:39:27 +02:00
|
|
|
{
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
if ((pid = fork()) < 0) { // fork failed
|
|
|
|
LOG_ERROR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pid > 0) { // parent process
|
2007-02-25 10:56:29 +01:00
|
|
|
int status = 0;
|
2007-06-17 12:45:57 +02:00
|
|
|
if (waitpid(pid, &status, 0) < 0) {
|
2001-10-20 10:39:27 +02:00
|
|
|
LOG_ERROR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
else { // child process
|
2007-02-25 10:56:29 +01:00
|
|
|
if (Detached) {
|
2007-06-17 12:45:57 +02:00
|
|
|
// Fork again and let first child die - grandchild stays alive without parent
|
|
|
|
if (fork() > 0)
|
2007-10-19 14:37:03 +02:00
|
|
|
_exit(0);
|
2007-02-25 10:56:29 +01:00
|
|
|
// Start a new session
|
|
|
|
pid_t sid = setsid();
|
|
|
|
if (sid < 0)
|
|
|
|
LOG_ERROR;
|
|
|
|
// close STDIN and re-open as /dev/null
|
|
|
|
int devnull = open("/dev/null", O_RDONLY);
|
|
|
|
if (devnull < 0 || dup2(devnull, 0) < 0)
|
|
|
|
LOG_ERROR;
|
|
|
|
}
|
2001-10-20 10:39:27 +02:00
|
|
|
int MaxPossibleFileDescriptors = getdtablesize();
|
|
|
|
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
|
|
|
close(i); //close all dup'ed filedescriptors
|
|
|
|
if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
|
|
|
|
LOG_ERROR_STR(Command);
|
|
|
|
_exit(-1);
|
|
|
|
}
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
}
|