#include <security_utilities/threading.h>
#include <security_utilities/globalizer.h>
#include <security_utilities/memutils.h>
#include <unistd.h> // WWDC 2007 thread-crash workaround
#include <syslog.h> // WWDC 2007 thread-crash workaround
ThreadStoreSlot::ThreadStoreSlot(Destructor *destructor)
{
if (int err = pthread_key_create(&mKey, destructor))
UnixError::throwMe(err);
}
ThreadStoreSlot::~ThreadStoreSlot()
{
pthread_key_delete(mKey);
}
struct MutexAttributes {
pthread_mutexattr_t recursive;
pthread_mutexattr_t checking;
MutexAttributes()
{
pthread_mutexattr_init(&recursive);
pthread_mutexattr_settype(&recursive, PTHREAD_MUTEX_RECURSIVE);
#if !defined(NDEBUG)
pthread_mutexattr_init(&checking);
pthread_mutexattr_settype(&checking, PTHREAD_MUTEX_ERRORCHECK);
#endif //NDEBUG
}
};
static ModuleNexus<MutexAttributes> mutexAttrs;
Mutex::Mutex()
{
check(pthread_mutex_init(&me, NULL));
}
Mutex::Mutex(Type type)
{
switch (type) {
case normal:
check(pthread_mutex_init(&me, IFELSEDEBUG(&mutexAttrs().checking, NULL)));
break;
case recursive: check(pthread_mutex_init(&me, &mutexAttrs().recursive));
break;
};
}
Mutex::~Mutex()
{
int result = pthread_mutex_destroy(&me);
check(result);
}
void Mutex::lock()
{
check(pthread_mutex_lock(&me));
}
bool Mutex::tryLock()
{
if (int err = pthread_mutex_trylock(&me)) {
if (err != EBUSY)
UnixError::throwMe(err);
return false;
}
return true;
}
void Mutex::unlock()
{
int result = pthread_mutex_unlock(&me);
check(result);
}
Condition::Condition(Mutex &lock) : mutex(lock)
{
check(pthread_cond_init(&me, NULL));
}
Condition::~Condition()
{
check(pthread_cond_destroy(&me));
}
void Condition::wait()
{
check(pthread_cond_wait(&me, &mutex.me));
}
void Condition::signal()
{
check(pthread_cond_signal(&me));
}
void Condition::broadcast()
{
check(pthread_cond_broadcast(&me));
}
void CountingMutex::enter()
{
lock();
mCount++;
secdebug("cmutex", "%p up to %d", this, mCount);
unlock();
}
bool CountingMutex::tryEnter()
{
if (!tryLock())
return false;
mCount++;
secdebug("cmutex", "%p up to %d (was try)", this, mCount);
unlock();
return true;
}
void CountingMutex::exit()
{
lock();
assert(mCount > 0);
mCount--;
secdebug("cmutex", "%p down to %d", this, mCount);
unlock();
}
void CountingMutex::finishEnter()
{
mCount++;
secdebug("cmutex", "%p finish up to %d", this, mCount);
unlock();
}
void CountingMutex::finishExit()
{
assert(mCount > 0);
mCount--;
secdebug("cmutex", "%p finish down to %d", this, mCount);
unlock();
}
Thread::~Thread()
{
}
void Thread::run()
{
pthread_attr_t ptattrs;
int err, ntries = 10;
if ((err = pthread_attr_init(&ptattrs)) ||
(err = pthread_attr_setdetachstate(&ptattrs, PTHREAD_CREATE_DETACHED)))
{
syslog(LOG_ERR, "error %d setting thread detach state", err);
}
while ((err = pthread_create(&self.mIdent, &ptattrs, runner, this) &&
--ntries))
{
syslog(LOG_ERR, "pthread_create() error %d", err);
usleep(50000); }
if (err)
{
syslog(LOG_ERR, "too many failed pthread_create() attempts");
}
else
secdebug("thread", "%p created", self.mIdent);
}
void *Thread::runner(void *arg)
{
try {
Thread *me = static_cast<Thread *>(arg);
secdebug("thread", "%p starting", me->self.mIdent);
me->action();
secdebug("thread", "%p terminating", me->self.mIdent);
delete me;
return NULL;
}
catch (...)
{
return NULL;
}
}
void Thread::yield()
{
::sched_yield();
}
ThreadRunner::ThreadRunner(Action *todo)
{
mAction = todo;
run();
}
void ThreadRunner::action()
{
mAction();
}