threading.h   [plain text]


/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// threading - multi-threading support
//
// Once upon a time, this file provided a system-independent abstraction layer
// for various thread models. These times are long gone, and we might as well
// admit that we're sitting on top of pthreads (plus certain other system facilities).
//
#ifndef _H_THREADING
#define _H_THREADING

#include <security_utilities/utilities.h>
#include <security_utilities/errors.h>
#include <security_utilities/debugging.h>
# include <pthread.h>

#include <security_utilities/threading_internal.h>


namespace Security {


//
// Potentially, debug-logging all Mutex activity can really ruin your
// performance day. We take some measures to reduce the impact, but if
// you really can't stomach any overhead, define THREAD_NDEBUG to turn
// (only) thread debug-logging off. NDEBUG will turn this on automatically.
// On the other hand, throwing out all debug code will change the ABI of
// Mutexi in incompatible ways. Thus, we still generate the debug-style out-of-line
// code even with THREAD_NDEBUG, so that debug-style code will work with us.
// If you want to ditch it completely, #define THREAD_CLEAN_NDEBUG.
//
#if defined(NDEBUG) || defined(THREAD_CLEAN_NDEBUG)
# if !defined(THREAD_NDEBUG)
#  define THREAD_NDEBUG
# endif
#endif


//
// An abstraction of a per-thread untyped storage slot of pointer size.
// Do not use this in ordinary code; this is for implementing other primitives only.
// Use a PerThreadPointer or ThreadNexus.
//
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;
};


//
// Per-thread pointers are implemented using the pthread TLS (thread local storage)
// facility.
// Let's be clear on what gets destroyed when, here. Following the pthread lead,
// when a thread dies its PerThreadPointer object(s) are properly destroyed.
// However, if a PerThreadPointer itself is destroyed, NOTHING HAPPENS. Yes, there are
// reasons for this. This is not (on its face) a bug, so don't yell. But be aware...
//
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); }
};


//
// Pthread Synchronization primitives.
// These have a common header, strictly for our convenience.
//
class LockingPrimitive {
protected:
	void init(bool logMe);

	bool debugLog;						// log *this* mutex
	static bool debugHasInitialized;	// global: debug state set up
	static bool loggingMutexi;			// global: we are debug-logging mutexi	
};


//
// Mutexi
//
class Mutex : public LockingPrimitive {
    NOCOPY(Mutex)
    friend class Condition;
    
    void check(int err)	{ if (err) UnixError::throwMe(err); }

public:
	enum Type {
		normal,
		recursive
	};
	
    Mutex(bool log = true);				// normal with optional debug logging
	Mutex(Type type, bool log = true);	// recursive with optional debug logging
	~Mutex();							// destroy (must be unlocked)
    void lock();						// lock and wait
	bool tryLock();						// lock or return false
    void unlock();						// unlock (must be locked)

private:
    pthread_mutex_t me;
	
	unsigned long mUseCount;			// number of locks succeeded
	unsigned long mContentionCount;		// number of contentions (valid only if debugLog)
};


//
// Condition variables
//
class Condition : public LockingPrimitive {
    NOCOPY(Condition)
    
    void check(int err)	{ if (err) UnixError::throwMe(err); }

public:	
    Condition(Mutex &mutex);			// create with specific Mutex
	~Condition();
    void wait();						// wait for signal
	void signal();						// signal one
    void broadcast();					// signal all

    Mutex &mutex;						// associated Mutex
	
private:
    pthread_cond_t me;
};


//
// A CountingMutex adds a counter to a Mutex.
// NOTE: This is not officially a semaphore - it's an automatically managed
// counter married to a Mutex.
//
class CountingMutex : public Mutex {
public:
    CountingMutex() : mCount(0) { }
    ~CountingMutex() { assert(mCount == 0); }

    void enter();						// lock, add one, unlock
    bool tryEnter();					// enter or return false
    void exit();						// lock, subtract one, unlock

    // these methods do not lock - use only while you hold the lock
    unsigned int count() const { return mCount; }
    bool isIdle() const { return mCount == 0; }

    // convert Mutex lock to CountingMutex enter/exit. Expert use only
    void finishEnter();					// all but the initial lock
	void finishExit();					// all but the initial lock
   
private:
    unsigned int mCount;				// counter level
};
 

//
// A guaranteed-unlocker stack-based class.
// By default, this will use lock/unlock methods, but you can provide your own
// alternates (to, e.g., use enter/exit, or some more specialized pair of operations).
//
// NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack)
// by a single thread.
//
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 TakeLock, class ReleaseLock,
	void (TakeLock::*_lock)() = &TakeLock::lock,
	void (TakeLock::*_unlock)() = &TakeLock::unlock,
	void (ReleaseLock::*_rlock)() = &ReleaseLock::lock,
	void (ReleaseLock::*_runlock)() = &ReleaseLock::unlock>
class StSyncLock {
public:
    StSyncLock(TakeLock &tlck, ReleaseLock &rlck) : taken(tlck), released(rlck) { 
		(released.*_unlock)(); 
		(taken.*_lock)(); 
		mActive = true; 
	}
    StSyncLock(TakeLock &tlck, ReleaseLock &rlck, bool option) : taken(tlck), released(rlck), mActive(option) { }
    ~StSyncLock()						{ if (mActive) { (taken.*_unlock)(); (released.*_rlock)(); }}
	
	bool isActive() const				{ return mActive; }
	void lock()							{ if(!mActive) { (released.*_runlock)(); (taken.*_lock)(); mActive = true; }}
	void unlock()						{ if(mActive) { (taken.*_unlock)(); (released.*_rlock)(); mActive = false; }}
	void release()						{ assert(mActive); mActive = false; }
	
protected:
    TakeLock &taken;
    ReleaseLock &released;
    bool mActive;
};


//
// Atomic increment/decrement operations.
// The default implementation uses a Mutex. However, many architectures can do
// much better than that.
// Be very clear on the nature of AtomicCounter. It implies no memory barriers of
// any kind. This means that (1) you cannot protect any other memory region with it
// (use a Mutex for that), and (2) it may not enforce cross-processor ordering, which
// means that you have no guarantee that you'll see modifications by other processors
// made earlier (unless another mechanism provides the memory barrier).
// On the other hand, if your compiler has brains, this is blindingly fast...
//
template <class Integer = int>
class StaticAtomicCounter {
protected:

#if defined(_HAVE_ATOMIC_OPERATIONS)
    AtomicWord mValue;
public:
    operator Integer() const	{ return mValue; }

    // infix versions (primary)
    Integer operator ++ ()		{ return atomicIncrement(mValue); }
    Integer operator -- ()		{ return atomicDecrement(mValue); }
    
    // postfix versions
    Integer operator ++ (int)	{ return atomicIncrement(mValue) - 1; }
    Integer operator -- (int)	{ return atomicDecrement(mValue) + 1; }

    // generic offset
    Integer operator += (int delta) { return atomicOffset(mValue, delta); }

#else // no atomic integers, use locks

    Integer mValue;
    mutable Mutex mLock;
public:
    StaticAtomicCounter() : 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)	{ StaticAtomicCounter<Integer>::mValue = init; }
};


//
// A class implementing a separate thread of execution.
// Do not expect many high-level semantics to be portable. If you can,
// restrict yourself to expect parallel execution and little else.
//
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); }
    
    private:
        pthread_t mIdent;
    };

public:
    Thread() { }				// constructor
    virtual ~Thread();	 		// virtual destructor
    void run();					// begin running the thread
    
public:
	static void yield();		// unstructured short-term processor yield
    
protected:
    virtual void action() = 0; 	// the action to be performed

private:
    Identity self;				// my own identity (instance constant)

    static void *runner(void *); // argument to pthread_create
};


//
// A "just run this function in a thread" variant of Thread
//
class ThreadRunner : public Thread {
    typedef void Action();
public:
    ThreadRunner(Action *todo);

private:
    void action();
    Action *mAction;
};


//
// Once upon a time, pthread mutexi on Mac OS X did not support recursion.
// In desperation, NestingMutex was born. Things are better now. This class
// is a historical wart.
// In today's enlightened age, use Mutex(Mutex::recursive).
//
class NestingMutex {
public:
    NestingMutex();
    
    void lock();
    bool tryLock();
    void unlock();

private:
    Mutex mLock;
    Mutex mWait;
    Thread::Identity mIdent;
    UInt32 mCount;
};


} // end namespace Security

#endif //_H_THREADING