#ifndef _H_THREADING
#define _H_THREADING
#include <Security/utilities.h>
#include <Security/debugging.h>
#if _USE_THREADS == _USE_PTHREADS
# include <pthread.h>
#endif
#include <Security/threading_internal.h>
namespace Security {
#if defined(NDEBUG) || defined(THREAD_CLEAN_NDEBUG)
# if !defined(THREAD_NDEBUG)
# define THREAD_NDEBUG
# endif
#endif
#if _USE_THREADS == _USE_PTHREADS
class ThreadStoreSlot {
public:
typedef void Destructor(void *);
ThreadStoreSlot(Destructor *destructor = NULL);
~ThreadStoreSlot();
void *get() const { return pthread_getspecific(mKey); }
operator void * () const { return get(); }
void operator = (void *value) const
{
if (int err = pthread_setspecific(mKey, value))
UnixError::throwMe(err);
}
private:
pthread_key_t mKey;
};
#endif //_USE_PTHREADS
#if _USE_THREADS == _USE_PTHREADS
template <class T>
class PerThreadPointer : public ThreadStoreSlot {
public:
PerThreadPointer(bool cleanup = true) : ThreadStoreSlot(cleanup ? destructor : NULL) { }
operator bool() const { return get() != NULL; }
operator T * () const { return reinterpret_cast<T *>(get()); }
T *operator -> () const { return static_cast<T *>(*this); }
T &operator * () const { return *static_cast<T *>(get()); }
void operator = (T *t) { ThreadStoreSlot::operator = (t); }
private:
static void destructor(void *element)
{ delete reinterpret_cast<T *>(element); }
};
#elif _USE_THREADS == _USE_NO_THREADS
template <class T>
class PerThreadPointer {
public:
PerThreadPointer(bool cleanup = true) : mCleanup(cleanup) { }
~PerThreadPointer() { }
operator bool() const { return mValue != NULL; }
operator T * () const { return mValue; }
T *operator -> () const { return mValue; }
T &operator * () const { assert(mValue); return *mValue; }
void operator = (T *t) { mValue = t; }
private:
T *mValue;
bool mCleanup;
};
#else
# error Unsupported threading model
#endif //_USE_THREADS
#if _USE_THREADS == _USE_PTHREADS
class Mutex {
NOCOPY(Mutex)
void check(int err) { if (err) UnixError::throwMe(err); }
public:
Mutex(bool log = true);
~Mutex();
void lock();
bool tryLock();
void unlock();
private:
pthread_mutex_t me;
bool debugLog; unsigned long useCount; unsigned long contentionCount; static bool debugHasInitialized; static bool loggingMutexi; };
#elif _USE_THREADS == _USE_NO_THREADS
class Mutex {
public:
void lock(bool = true) { }
void unlock() { }
bool tryLock() { return true; }
};
#else
# error Unsupported threading model
#endif //_USE_THREADS
class CountingMutex : public Mutex {
public:
CountingMutex() : mCount(0) { }
~CountingMutex() { assert(mCount == 0); }
void enter();
bool tryEnter();
void exit();
unsigned int count() const { return mCount; }
bool isIdle() const { return mCount == 0; }
void finishEnter();
void finishExit();
private:
unsigned int mCount;
};
template <class Lock,
void (Lock::*_lock)() = &Lock::lock,
void (Lock::*_unlock)() = &Lock::unlock>
class StLock {
public:
StLock(Lock &lck) : me(lck) { (me.*_lock)(); mActive = true; }
StLock(Lock &lck, bool option) : me(lck), mActive(option) { }
~StLock() { if (mActive) (me.*_unlock)(); }
bool isActive() const { return mActive; }
void lock() { if(!mActive) { (me.*_lock)(); mActive = true; }}
void unlock() { if(mActive) { (me.*_unlock)(); mActive = false; }}
void release() { assert(mActive); mActive = false; }
operator const Lock &() const { return me; }
protected:
Lock &me;
bool mActive;
};
template <class Integer = int>
class StaticAtomicCounter {
protected:
#if defined(_HAVE_ATOMIC_OPERATIONS)
AtomicWord mValue;
public:
operator Integer() const { return mValue; }
Integer operator ++ () { return atomicIncrement(mValue); }
Integer operator -- () { return atomicDecrement(mValue); }
Integer operator ++ (int) { return atomicIncrement(mValue) - 1; }
Integer operator -- (int) { return atomicDecrement(mValue) + 1; }
Integer operator += (int delta) { return atomicOffset(mValue, delta); }
#else // no atomic integers, use locks
Integer mValue;
mutable Mutex mLock;
public:
StaticAtomicCounter(Integer init = 0) : mValue(init), mLock(false) { }
operator Integer() const { StLock<Mutex> _(mLock); return mValue; }
Integer operator ++ () { StLock<Mutex> _(mLock); return ++mValue; }
Integer operator -- () { StLock<Mutex> _(mLock); return --mValue; }
Integer operator ++ (int) { StLock<Mutex> _(mLock); return mValue++; }
Integer operator -- (int) { StLock<Mutex> _(mLock); return mValue--; }
Integer operator += (int delta) { StLock<Mutex> _(mLock); return mValue += delta; }
#endif
};
template <class Integer = int>
class AtomicCounter : public StaticAtomicCounter<Integer> {
public:
AtomicCounter(Integer init = 0) { mValue = 0; }
};
#if _USE_THREADS == _USE_PTHREADS
class Thread {
NOCOPY(Thread)
public:
class Identity {
friend class Thread;
Identity(pthread_t id) : mIdent(id) { }
public:
Identity() { }
static Identity current() { return pthread_self(); }
bool operator == (const Identity &other) const
{ return pthread_equal(mIdent, other.mIdent); }
bool operator != (const Identity &other) const
{ return !(*this == other); }
static const int idLength = 10;
static void getIdString(char id[idLength]);
private:
pthread_t mIdent;
};
public:
Thread() { } virtual ~Thread(); void run();
public:
static void yield();
protected:
virtual void action() = 0;
private:
Identity self;
static void *runner(void *); };
#elif _USE_THREADS == _USE_NO_THREADS
class Thread {
NOCOPY(Thread)
public:
Thread() { } virtual ~Thread() { } void run() { action(); }
public:
class Identity {
public:
static Identity current() { return Identity(); }
bool operator == (const Identity &) const { return true; } bool operator != (const Identity &) const { return false; }
#if !defined(NDEBUG)
static const idLength = 9;
static void getIdString(char id[idLength]) { memcpy(id, "nothread", idLength); }
#endif
private:
Identity() { }
};
public:
void yield() { assert(false); }
protected:
virtual void action() = 0; };
#else
# error Unsupported threading model
#endif
class ThreadRunner : public Thread {
typedef void Action();
public:
ThreadRunner(Action *todo);
private:
void action();
Action *mAction;
};
class NestingMutex {
public:
NestingMutex();
void lock();
bool tryLock();
void unlock();
private:
Mutex mLock;
Mutex mWait;
Thread::Identity mIdent;
uint32 mCount;
};
}
#endif //_H_THREADING