#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);
}
bool LockingPrimitive::debugHasInitialized;
bool LockingPrimitive::loggingMutexi;
inline void LockingPrimitive::init(bool log)
{
#if !defined(THREAD_NDEBUG)
if (!debugHasInitialized) {
loggingMutexi = Debug::debugging("mutex") || Debug::debugging("mutex-c");
debugHasInitialized = true;
}
debugLog = log && loggingMutexi;
#else
debugLog = false;
#endif //THREAD_NDEBUG
}
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(bool log)
{
init(log);
mUseCount = mContentionCount = 0;
check(pthread_mutex_init(&me, NULL));
}
Mutex::Mutex(Type type, bool log)
{
init(log);
mUseCount = mContentionCount = 0;
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()
{
#if !defined(THREAD_NDEBUG)
if (debugLog) {
if (mContentionCount > 0)
secdebug("mutex-c", "%p destroyed after %ld/%ld locks/contentions",
this, mUseCount, mContentionCount);
else if (mUseCount > 100)
secdebug("mutex", "%p destroyed after %ld locks", this, mUseCount);
}
#endif //THREAD_NDEBUG
check(pthread_mutex_destroy(&me));
}
void Mutex::lock()
{
#if !defined(THREAD_NDEBUG)
mUseCount++;
if (debugLog) {
switch (int err = pthread_mutex_trylock(&me)) {
case 0:
break;
case EBUSY:
if (debugLog)
secdebug("mutex-c", "%p contended (%ld of %ld)", this, ++mContentionCount, mUseCount);
check(pthread_mutex_lock(&me));
break;
default:
UnixError::throwMe(err);
}
if (mUseCount % 100 == 0)
secdebug("mutex", "%p locked %ld", this, mUseCount);
else
secdebug("mutex", "%p locked", this);
return;
}
#endif //THREAD_NDEBUG
check(pthread_mutex_lock(&me));
}
bool Mutex::tryLock()
{
mUseCount++;
if (int err = pthread_mutex_trylock(&me)) {
if (err != EBUSY)
UnixError::throwMe(err);
#if !defined(THREAD_NDEBUG)
if (debugLog)
secdebug("mutex-c", "%p trylock contended (%ld of %ld)",
this, ++mContentionCount, mUseCount);
#endif //THREAD_NDEBUG
return false;
}
#if !defined(THREAD_NDEBUG)
if (debugLog)
if (mUseCount % 100 == 0)
secdebug("mutex", "%p locked %ld", this, mUseCount);
else
secdebug("mutex", "%p locked", this);
#endif //THREAD_NDEBUG
return true;
}
void Mutex::unlock()
{
#if !defined(MUTEX_NDEBUG)
if (debugLog)
secdebug("mutex", "%p unlocked", this);
#endif //MUTEX_NDEBUG
check(pthread_mutex_unlock(&me));
}
Condition::Condition(Mutex &lock) : mutex(lock)
{
init(true);
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("mutex", "%p up to %d", this, mCount);
unlock();
}
bool CountingMutex::tryEnter()
{
if (!tryLock())
return false;
mCount++;
secdebug("mutex", "%p up to %d (was try)", this, mCount);
unlock();
return true;
}
void CountingMutex::exit()
{
lock();
assert(mCount > 0);
mCount--;
secdebug("mutex", "%p down to %d", this, mCount);
unlock();
}
void CountingMutex::finishEnter()
{
mCount++;
secdebug("mutex", "%p finish up to %d", this, mCount);
unlock();
}
void CountingMutex::finishExit()
{
assert(mCount > 0);
mCount--;
secdebug("mutex", "%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)
{
Thread *me = static_cast<Thread *>(arg);
#if 0 // for WWDC 2007 seed
if (int err = pthread_detach(me->self.mIdent))
UnixError::throwMe(err);
#endif
secdebug("thread", "%p starting", me->self.mIdent);
me->action();
secdebug("thread", "%p terminating", me->self.mIdent);
delete me;
return NULL;
}
void Thread::yield()
{
::sched_yield();
}
ThreadRunner::ThreadRunner(Action *todo)
{
mAction = todo;
run();
}
void ThreadRunner::action()
{
mAction();
}