#if !defined(THREAD_CLEAN_NDEBUG)
# define THREAD_MAKE_STUBS
#endif
#include <Security/threading.h>
#if _USE_THREADS == _USE_PTHREADS
ThreadStoreSlot::ThreadStoreSlot(Destructor *destructor)
{
if (int err = pthread_key_create(&mKey, destructor))
UnixError::throwMe(err);
}
ThreadStoreSlot::~ThreadStoreSlot()
{
#if BUG_2998157
pthread_key_delete(mKey);
#endif //BUG_2998157
}
#endif
#if _USE_THREADS == _USE_PTHREADS
#if !defined(THREAD_CLEAN_NDEBUG)
bool Mutex::debugHasInitialized;
bool Mutex::loggingMutexi;
Mutex::Mutex(bool log)
{
#if !defined(THREAD_NDEBUG)
if (!debugHasInitialized) {
loggingMutexi = Debug::debugging("mutex");
debugHasInitialized = true;
}
debugLog = log && loggingMutexi;
useCount = contentionCount = 0;
#else
debugLog = false;
#endif //THREAD_NDEBUG
check(pthread_mutex_init(&me, NULL));
}
Mutex::~Mutex()
{
#if !defined(THREAD_NDEBUG)
if (debugLog && (useCount > 100 || contentionCount > 0))
debug("mutex", "%p destroyed after %ld/%ld locks/contentions", this, useCount, contentionCount);
#endif //THREAD_NDEBUG
check(pthread_mutex_destroy(&me));
}
void Mutex::lock()
{
#if !defined(THREAD_NDEBUG)
useCount++;
if (debugLog) {
switch (int err = pthread_mutex_trylock(&me)) {
case 0:
break;
case EBUSY:
if (debugLog)
debug("mutex", "%p contended (%ld of %ld)", this, ++contentionCount, useCount);
check(pthread_mutex_lock(&me));
break;
default:
UnixError::throwMe(err);
}
if (useCount % 100 == 0)
debug("mutex", "%p locked %ld", this, useCount);
else
debug("mutex", "%p locked", this);
return;
}
#endif //THREAD_NDEBUG
check(pthread_mutex_lock(&me));
}
bool Mutex::tryLock()
{
useCount++;
if (int err = pthread_mutex_trylock(&me)) {
if (err != EBUSY)
UnixError::throwMe(err);
#if !defined(THREAD_NDEBUG)
if (debugLog)
debug("mutex", "%p trylock contended (%ld of %ld)",
this, ++contentionCount, useCount);
#endif //THREAD_NDEBUG
return false;
}
#if !defined(THREAD_NDEBUG)
if (debugLog)
if (useCount % 100 == 0)
debug("mutex", "%p locked %ld", this, useCount);
else
debug("mutex", "%p locked", this);
#endif //THREAD_NDEBUG
return true;
}
void Mutex::unlock()
{
#if !defined(MUTEX_NDEBUG)
if (debugLog)
debug("mutex", "%p unlocked", this);
#endif //MUTEX_NDEBUG
check(pthread_mutex_unlock(&me));
}
#endif //!THREAD_CLEAN_NDEBUG
#endif //PTHREADS
void CountingMutex::enter()
{
lock();
mCount++;
debug("mutex", "%p up to %d", this, mCount);
unlock();
}
bool CountingMutex::tryEnter()
{
if (!tryLock())
return false;
mCount++;
debug("mutex", "%p up to %d (was try)", this, mCount);
unlock();
return true;
}
void CountingMutex::exit()
{
lock();
assert(mCount > 0);
mCount--;
debug("mutex", "%p down to %d", this, mCount);
unlock();
}
void CountingMutex::finishEnter()
{
mCount++;
debug("mutex", "%p finish up to %d", this, mCount);
unlock();
}
void CountingMutex::finishExit()
{
assert(mCount > 0);
mCount--;
debug("mutex", "%p finish down to %d", this, mCount);
unlock();
}
#if _USE_THREADS == _USE_PTHREADS
Thread::~Thread()
{
}
void Thread::run()
{
if (int err = pthread_create(&self.mIdent, NULL, runner, this))
UnixError::throwMe(err);
debug("thread", "%p created", self.mIdent);
}
void *Thread::runner(void *arg)
{
Thread *me = static_cast<Thread *>(arg);
if (int err = pthread_detach(me->self.mIdent))
UnixError::throwMe(err);
debug("thread", "%p starting", me->self.mIdent);
me->action();
debug("thread", "%p terminating", me->self.mIdent);
delete me;
return NULL;
}
void Thread::yield()
{
sched_yield();
}
#if !defined(NDEBUG)
#include <Security/memutils.h>
void Thread::Identity::getIdString(char id[idLength])
{
pthread_t current = pthread_self();
void *p;
memcpy(&p, ¤t, sizeof(p));
snprintf(id, idLength, "%lx", long(p));
}
#endif // NDEBUG
#endif // PTHREADS
ThreadRunner::ThreadRunner(Action *todo)
{
mAction = todo;
run();
}
void ThreadRunner::action()
{
mAction();
}
NestingMutex::NestingMutex() : mCount(0)
{ }
void NestingMutex::lock()
{
while (!tryLock()) {
mWait.lock();
mWait.unlock();
}
}
bool NestingMutex::tryLock()
{
StLock<Mutex> _(mLock);
if (mCount == 0) { mCount = 1;
mIdent = Thread::Identity::current();
mWait.lock();
return true;
} else if (mIdent == Thread::Identity::current()) { mCount++;
return true;
} else { return false;
}
}
void NestingMutex::unlock()
{
StLock<Mutex> _(mLock);
assert(mCount > 0 && mIdent == Thread::Identity::current());
if (--mCount == 0) mWait.unlock();
}