//========== Copyright 2005, Valve Corporation, All rights reserved. ======== // // Purpose: A collection of utility classes to simplify thread handling, and // as much as possible contain portability problems. Here avoiding // including windows.h. // //============================================================================= #ifndef THREADTOOLS_H #define THREADTOOLS_H #include "tier0/platform.h" #include "tier0/dbg.h" #include "tier0/vcrmode.h" #ifdef _LINUX #include <pthread.h> #endif #if defined( _WIN32 ) #pragma once #endif #ifndef STATIC_TIER0 #ifdef TIER0_DLL_EXPORT #define TT_INTERFACE DLL_EXPORT #define TT_OVERLOAD DLL_GLOBAL_EXPORT #define TT_CLASS DLL_CLASS_EXPORT #else #define TT_INTERFACE DLL_IMPORT #define TT_OVERLOAD DLL_GLOBAL_IMPORT #define TT_CLASS DLL_CLASS_IMPORT #endif #else // BUILD_AS_DLL #define TT_INTERFACE extern #define TT_OVERLOAD #define TT_CLASS #endif // BUILD_AS_DL #ifndef _RETAIL #define THREAD_MUTEX_TRACING_SUPPORTED #if defined(_WIN32) && defined(_DEBUG) #define THREAD_MUTEX_TRACING_ENABLED #endif #endif #ifdef _WIN32 typedef void *HANDLE; #endif //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const unsigned TT_INFINITE = 0xffffffff; #ifndef NO_THREAD_LOCAL #ifndef THREAD_LOCAL #ifdef _WIN32 #define THREAD_LOCAL __declspec(thread) #elif _LINUX #define THREAD_LOCAL __thread #endif #endif #endif // NO_THREAD_LOCAL #ifndef _XBOX typedef unsigned long ThreadId_t; #else typedef unsigned ThreadId_t; #endif //----------------------------------------------------------------------------- // // Simple thread creation. Differs from VCR mode/CreateThread/_beginthreadex // in that it accepts a standard C function rather than compiler specific one. // //----------------------------------------------------------------------------- FORWARD_DECLARE_HANDLE( ThreadHandle_t ); typedef unsigned (*ThreadFunc_t)( void *pParam ); TT_INTERFACE ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, unsigned stackSize = 0 ); //----------------------------------------------------------------------------- TT_INTERFACE void ThreadSleep(unsigned duration = 0); TT_INTERFACE uint ThreadGetCurrentId(); TT_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); TT_INTERFACE bool ThreadInMainThread(); inline void ThreadPause() { #ifdef _WIN32 __asm pause; #elif _LINUX __asm __volatile("pause"); #else #error "implement me" #endif } //----------------------------------------------------------------------------- // // Interlock methods. These perform very fast atomic thread // safe operations. These are especially relevant in a multi-core setting. // //----------------------------------------------------------------------------- #ifdef _WIN32 #define NOINLINE #elif _LINUX #define NOINLINE __attribute__ ((noinline)) #endif TT_INTERFACE long ThreadInterlockedIncrement( long volatile * ) NOINLINE; TT_INTERFACE long ThreadInterlockedDecrement( long volatile * ) NOINLINE; TT_INTERFACE long ThreadInterlockedExchange( long volatile *, long value ) NOINLINE; TT_INTERFACE long ThreadInterlockedExchangeAdd( long volatile *, long value ) NOINLINE; TT_INTERFACE long ThreadInterlockedCompareExchange( long volatile *, long value, long comperand ) NOINLINE; TT_INTERFACE void *ThreadInterlockedExchangePointer( void * volatile *, void *value ) NOINLINE; TT_INTERFACE void *ThreadInterlockedCompareExchangePointer( void * volatile *, void *value, void *comperand ) NOINLINE; TT_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile * ) NOINLINE; TT_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile * ) NOINLINE; TT_INTERFACE int64 ThreadInterlockedCompareExchange64( int64 volatile *, int64 value, int64 comperand ) NOINLINE; TT_INTERFACE int64 ThreadInterlockedExchange64( int64 volatile *, int64 value ) NOINLINE; TT_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *, int64 value ) NOINLINE; TT_INTERFACE bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand ) NOINLINE; //----------------------------------------------------------------------------- // Encapsulation of a thread local datum (needed because THREAD_LOCAL doesn't // work in a DLL loaded with LoadLibrary() //----------------------------------------------------------------------------- #ifndef __AFXTLS_H__ // not compatible with some Windows headers #ifndef NO_THREAD_LOCAL class TT_CLASS CThreadLocalBase { public: CThreadLocalBase(); ~CThreadLocalBase(); void * Get() const; void Set(void *); private: #ifdef _WIN32 uint32 m_index; #elif _LINUX pthread_key_t m_index; #endif }; //--------------------------------------------------------- #ifndef __AFXTLS_H__ template <class T> class CThreadLocal : public CThreadLocalBase { public: CThreadLocal() { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(void *) ); } T Get() const { return reinterpret_cast<T>(CThreadLocalBase::Get()); } void Set(T val) { CThreadLocalBase::Set(reinterpret_cast<void *>(val)); } }; #endif //--------------------------------------------------------- template <class T = int> class CThreadLocalInt : public CThreadLocal<T> { public: operator const T() const { return Get(); } int operator=( T i ) { Set( i ); return i; } T operator++() { T i = Get(); Set( ++i ); return i; } T operator++(int) { T i = Get(); Set( i + 1 ); return i; } T operator--() { T i = Get(); Set( --i ); return i; } T operator--(int) { T i = Get(); Set( i - 1 ); return i; } }; //--------------------------------------------------------- template <class T> class CThreadLocalPtr : private CThreadLocalBase { public: CThreadLocalPtr() {} operator const void *() const { return (T *)Get(); } operator void *() { return (T *)Get(); } operator const T *() const { return (T *)Get(); } operator const T *() { return (T *)Get(); } operator T *() { return (T *)Get(); } int operator=( int i ) { AssertMsg( i == 0, "Only NULL allowed on integer assign" ); Set( NULL ); return 0; } T * operator=( T *p ) { Set( p ); return p; } bool operator !() const { return (!Get()); } bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() != NULL); } bool operator==( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() == NULL); } bool operator==( const void *p ) const { return (Get() == p); } bool operator!=( const void *p ) const { return (Get() != p); } bool operator==( const T *p ) const { return operator==((void*)p); } bool operator!=( const T *p ) const { return operator!=((void*)p); } T * operator->() { return (T *)Get(); } T & operator *() { return *((T *)Get()); } const T * operator->() const { return (T *)Get(); } const T & operator *() const { return *((T *)Get()); } const T & operator[]( int i ) const { return *((T *)Get() + i); } T & operator[]( int i ) { return *((T *)Get() + i); } private: // Disallowed operations CThreadLocalPtr( T *pFrom ); CThreadLocalPtr( const CThreadLocalPtr<T> &from ); T **operator &(); T * const *operator &() const; void operator=( const CThreadLocalPtr<T> &from ); bool operator==( const CThreadLocalPtr<T> &p ) const; bool operator!=( const CThreadLocalPtr<T> &p ) const; }; #endif // NO_THREAD_LOCAL #endif // !__AFXTLS_H__ //----------------------------------------------------------------------------- // // A super-fast thread-safe integer A simple class encapsulating the notion of an // atomic integer used across threads that uses the built in and faster // "interlocked" functionality rather than a full-blown mutex. Useful for simple // things like reference counts, etc. // //----------------------------------------------------------------------------- template <typename T> class CInterlockedIntT { public: CInterlockedIntT() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(long) ); } CInterlockedIntT( T value ) : m_value( value ) {} operator T() const { return m_value; } bool operator!() const { return ( m_value == 0 ); } bool operator==( T rhs ) const { return ( m_value == rhs ); } bool operator!=( T rhs ) const { return ( m_value != rhs ); } T operator++() { return (T)ThreadInterlockedIncrement( (long *)&m_value ); } T operator++(int) { return operator++() - 1; } T operator--() { return (T)ThreadInterlockedDecrement( (long *)&m_value ); } T operator--(int) { return operator--() + 1; } bool AssignIf( T conditionValue, T newValue ) { return ( ThreadInterlockedCompareExchange( (long *)&m_value, (long)newValue, (long)conditionValue ) == (long)conditionValue ); } T operator=( T newValue ) { ThreadInterlockedExchange((long *)&m_value, newValue); return m_value; } void operator+=( T add ) { ThreadInterlockedExchangeAdd( (long *)&m_value, (long)add ); } void operator-=( T subtract ) { operator+=( -subtract ); } void operator*=( T multiplier ) { T original, result; do { original = m_value; result = original * multiplier; } while ( !AssignIf( original, result ) ); } void operator/=( T divisor ) { T original, result; do { original = m_value; result = original / divisor; } while ( !AssignIf( original, result ) ); } T operator+( T rhs ) const { return m_value + rhs; } T operator-( T rhs ) const { return m_value - rhs; } private: volatile T m_value; }; typedef CInterlockedIntT<int> CInterlockedInt; typedef CInterlockedIntT<unsigned> CInterlockedUInt; //----------------------------------------------------------------------------- template <typename T> class CInterlockedPtr { public: CInterlockedPtr() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T *) == sizeof(long) ); /* Will need to rework operator+= for 64 bit */ } CInterlockedPtr( T *value ) : m_value( value ) {} operator T *() const { return m_value; } bool operator!() const { return ( m_value == 0 ); } bool operator==( T *rhs ) const { return ( m_value == rhs ); } bool operator!=( T *rhs ) const { return ( m_value != rhs ); } T *operator++() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) )) + 1; } T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) ); } T *operator--() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) )) - 1; } T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) ); } bool AssignIf( T *conditionValue, T *newValue ) { return ( ThreadInterlockedCompareExchangePointer( (void **)&m_value, (void *)newValue, (void *)conditionValue ) == (void *)conditionValue ); } T *operator=( T *newValue ) { ThreadInterlockedExchangePointer((void **)&m_value, newValue); return newValue; } void operator+=( int add ) { ThreadInterlockedExchangeAdd( (long *)&m_value, add * sizeof(T) ); } void operator-=( int subtract ) { operator+=( -subtract ); } T *operator+( int rhs ) const { return m_value + rhs; } T *operator-( int rhs ) const { return m_value - rhs; } private: T * volatile m_value; }; //----------------------------------------------------------------------------- // // Platform independent for critical sections management // //----------------------------------------------------------------------------- class TT_CLASS CThreadMutex { public: CThreadMutex(); ~CThreadMutex(); //------------------------------------------------------ // Mutex acquisition/release. Const intentionally defeated. //------------------------------------------------------ void Lock(); void Lock() const { (const_cast<CThreadMutex *>(this))->Lock(); } void Unlock(); void Unlock() const { (const_cast<CThreadMutex *>(this))->Unlock(); } bool TryLock(); bool TryLock() const { return (const_cast<CThreadMutex *>(this))->TryLock(); } //------------------------------------------------------ // Use this to make deadlocks easier to track by asserting // when it is expected that the current thread owns the mutex //------------------------------------------------------ bool AssertOwnedByCurrentThread(); //------------------------------------------------------ // Enable tracing to track deadlock problems //------------------------------------------------------ void SetTrace( bool ); private: // Disallow copying CThreadMutex( const CThreadMutex & ); CThreadMutex &operator=( const CThreadMutex & ); #if defined( _WIN32 ) // Efficient solution to breaking the windows.h dependency, invariant is tested. #ifndef _XBOX #define TT_SIZEOF_CRITICALSECTION 24 #else #define TT_SIZEOF_CRITICALSECTION 28 #endif byte m_CriticalSection[TT_SIZEOF_CRITICALSECTION]; #elif _LINUX pthread_mutex_t m_Mutex; pthread_mutexattr_t m_Attr; #else #error #endif #ifdef THREAD_MUTEX_TRACING_SUPPORTED // Debugging (always here to allow mixed debug/release builds w/o changing size) uint m_currentOwnerID; uint16 m_lockCount; bool m_bTrace; #endif }; //----------------------------------------------------------------------------- // // An alternative mutex that is useful for cases when thread contention is // rare, but a mutex is required. Instances should be declared volatile. // Sleep of 0 may not be sufficient to keep high priority threads from starving // lesser threads. This class is not a suitable replacement for a critical // section if the resource contention is high. // //----------------------------------------------------------------------------- class TT_CLASS CThreadFastMutex { public: CThreadFastMutex() : m_ownerID( 0 ), m_depth( 0 ) { } private: bool TryLock( const uint32 threadId ) volatile { if ( threadId != m_ownerID && ThreadInterlockedCompareExchange( (volatile long *)&m_ownerID, (long)threadId, 0 ) != 0 ) return false; m_depth++; return true; } public: bool TryLock() volatile { return TryLock( ThreadGetCurrentId() ); } void Lock( unsigned nSpinSleepTime = 1 ) volatile { const uint32 threadId = ThreadGetCurrentId(); if ( TryLock( threadId ) ) { return; } ThreadPause(); for ( int i = 16; i > 0; --i ) { if ( TryLock( threadId ) ) { return; } ThreadPause(); ThreadSleep( 0 ); } while ( !TryLock( threadId ) ) { ThreadPause(); ThreadSleep( nSpinSleepTime ); }; #ifdef _DEBUG if ( m_ownerID != ThreadGetCurrentId() ) DebuggerBreak(); #endif } void Unlock() volatile { #ifdef _DEBUG if ( m_ownerID != ThreadGetCurrentId() ) DebuggerBreak(); #endif m_depth--; if ( !m_depth ) m_ownerID = 0; } bool TryLock() const volatile { return (const_cast<CThreadFastMutex *>(this))->TryLock(); } void Lock(unsigned nSpinSleepTime = 1 ) const volatile { (const_cast<CThreadFastMutex *>(this))->Lock( nSpinSleepTime ); } void Unlock() const volatile { (const_cast<CThreadFastMutex *>(this))->Unlock(); } // To match regular CThreadMutex: bool AssertOwnedByCurrentThread() { return true; } void SetTrace( bool ) {} private: volatile uint32 m_ownerID; volatile int m_depth; }; //----------------------------------------------------------------------------- // // Class to Lock a critical section, and unlock it automatically // when the lock goes out of scope // //----------------------------------------------------------------------------- template <class MUTEX_TYPE = CThreadMutex> class CAutoLockT { public: CAutoLockT(MUTEX_TYPE &lock) : m_lock(lock) { m_lock.Lock(); } CAutoLockT(const MUTEX_TYPE &lock) : m_lock(const_cast<CThreadMutex &>(lock)) { m_lock.Lock(); } ~CAutoLockT() { m_lock.Unlock(); } private: MUTEX_TYPE &m_lock; // Disallow copying CAutoLockT<MUTEX_TYPE>( const CAutoLockT<MUTEX_TYPE> & ); CAutoLockT<MUTEX_TYPE> &operator=( const CAutoLockT<MUTEX_TYPE> & ); }; typedef CAutoLockT<CThreadMutex> CAutoLock; //--------------------------------------------------------- template <int size> struct CAutoLockTypeDeducer {}; template <> struct CAutoLockTypeDeducer<sizeof(CThreadMutex)> { typedef CThreadMutex Type_t; }; template <> struct CAutoLockTypeDeducer<sizeof(CThreadFastMutex)> { typedef CThreadFastMutex Type_t; }; #define AUTO_LOCK( mutex ) \ CAutoLockT<CAutoLockTypeDeducer<sizeof(mutex)>::Type_t> autoLock_##mutex ( const_cast<CAutoLockTypeDeducer<sizeof(mutex)>::Type_t &>( mutex ) ) #define AUTO_LOCK_FM( mutex ) \ AUTO_LOCK( mutex ) #define LOCAL_THREAD_LOCK_( tag ) \ ; \ static CThreadMutex autoMutex_##tag; \ CAutoLock autoLock_##tag ( autoMutex_##tag ) #define LOCAL_THREAD_LOCK() \ LOCAL_THREAD_LOCK_(_) //----------------------------------------------------------------------------- // // Base class for event, semaphore and mutex objects. // //----------------------------------------------------------------------------- class TT_CLASS CThreadSyncObject { public: ~CThreadSyncObject(); //----------------------------------------------------- // Query if object is useful //----------------------------------------------------- bool operator!() const; //----------------------------------------------------- // Access handle //----------------------------------------------------- #ifdef _WIN32 operator HANDLE() { return m_hSyncObject; } #endif //----------------------------------------------------- // Wait for a signal from the object //----------------------------------------------------- bool Wait( uint32 dwTimeout = TT_INFINITE ); protected: CThreadSyncObject(); void AssertUseable(); #ifdef _WIN32 HANDLE m_hSyncObject; #elif _LINUX pthread_mutex_t m_Mutex; pthread_cond_t m_Condition; bool m_bInitalized; #else #error "Implement me" #endif private: CThreadSyncObject( const CThreadSyncObject & ); CThreadSyncObject &operator=( const CThreadSyncObject & ); }; //----------------------------------------------------------------------------- // // Wrapper for unnamed event objects // //----------------------------------------------------------------------------- #if defined( _WIN32 ) //----------------------------------------------------------------------------- // // CThreadSemaphore // //----------------------------------------------------------------------------- class TT_CLASS CThreadSemaphore : public CThreadSyncObject { public: CThreadSemaphore(long initialValue, long maxValue); //----------------------------------------------------- // Increases the count of the semaphore object by a specified // amount. Wait() decreases the count by one on return. //----------------------------------------------------- bool Release(long releaseCount = 1, long * pPreviousCount = NULL ); private: CThreadSemaphore(const CThreadSemaphore &); CThreadSemaphore &operator=(const CThreadSemaphore &); }; //----------------------------------------------------------------------------- // // A mutex suitable for out-of-process, multi-processor usage // //----------------------------------------------------------------------------- class TT_CLASS CThreadFullMutex : public CThreadSyncObject { public: CThreadFullMutex( bool bEstablishInitialOwnership = false, const char * pszName = NULL ); //----------------------------------------------------- // Release ownership of the mutex //----------------------------------------------------- bool Release(); // To match regular CThreadMutex: void Lock() { Wait(); } void Lock( unsigned timeout ) { Wait( timeout ); } void Unlock() { Release(); } bool AssertOwnedByCurrentThread() { return true; } void SetTrace( bool ) {} private: CThreadFullMutex( const CThreadFullMutex & ); CThreadFullMutex &operator=( const CThreadFullMutex & ); }; #endif class TT_CLASS CThreadEvent : public CThreadSyncObject { public: CThreadEvent( bool fManualReset = false ); //----------------------------------------------------- // Set the state to signaled //----------------------------------------------------- bool Set(); //----------------------------------------------------- // Set the state to nonsignaled //----------------------------------------------------- bool Reset(); //----------------------------------------------------- // Check if the event is signaled //----------------------------------------------------- bool Check(); bool Wait( uint32 dwTimeout = TT_INFINITE ); private: CThreadEvent( const CThreadEvent & ); CThreadEvent &operator=( const CThreadEvent & ); #ifdef _LINUX CInterlockedInt m_cSet; #endif }; // Hard-wired manual event for use in array declarations class CThreadManualEvent : public CThreadEvent { public: CThreadManualEvent() : CThreadEvent( true ) { } }; //----------------------------------------------------------------------------- // // A thread wrapper similar to a Java thread. // //----------------------------------------------------------------------------- class TT_CLASS CThread { public: CThread(); virtual ~CThread(); //----------------------------------------------------- const char *GetName(); void SetName( const char * ); size_t CalcStackDepth( void *pStackVariable ) { return ((byte *)m_pStackBase - (byte *)pStackVariable); } //----------------------------------------------------- // Functions for the other threads //----------------------------------------------------- // Start thread running - error if already running virtual bool Start( unsigned nBytesStack = 0 ); // Returns true if thread has been created and hasn't yet exited bool IsAlive(); // This method causes the current thread to wait until this thread // is no longer alive. bool Join( unsigned timeout = TT_INFINITE ); #ifdef _WIN32 // Access the thread handle directly HANDLE GetThreadHandle(); uint GetThreadId(); #endif //----------------------------------------------------- int GetResult(); //----------------------------------------------------- // Functions for both this, and maybe, and other threads //----------------------------------------------------- // Forcibly, abnormally, but relatively cleanly stop the thread void Stop( int exitCode = 0 ); // Get the priority int GetPriority() const; // Set the priority bool SetPriority( int ); // Suspend a thread unsigned Suspend(); // Resume a suspended thread unsigned Resume(); // Force hard-termination of thread. Used for critical failures. bool Terminate( int exitCode = 0 ); //----------------------------------------------------- // Global methods //----------------------------------------------------- // Get the Thread object that represents the current thread, if any. // Can return NULL if the current thread was not created using // CThread static CThread *GetCurrentCThread(); // Offer a context switch. Under Win32, equivalent to Sleep(0) #ifdef Yield #undef Yield #endif static void Yield(); // This method causes the current thread to yield and not to be // scheduled for further execution until a certain amount of real // time has elapsed, more or less. static void Sleep( unsigned duration ); protected: // Optional pre-run call, with ability to fail-create. Note Init() // is forced synchronous with Start() virtual bool Init(); // Thread will run this function on startup, must be supplied by // derived class, performs the intended action of the thread. virtual int Run() = 0; // Called when the thread exits virtual void OnExit(); #ifdef _WIN32 // Allow for custom start waiting virtual bool WaitForCreateComplete( CThreadEvent *pEvent ); #endif // "Virtual static" facility typedef unsigned (__stdcall *ThreadProc_t)( void * ); virtual ThreadProc_t GetThreadProc(); CThreadMutex m_Lock; private: // Thread initially runs this. param is actually 'this'. function // just gets this and calls ThreadProc struct ThreadInit_t { CThread * pThread; #ifdef _WIN32 CThreadEvent *pInitCompleteEvent; #endif bool * pfInitSuccess; }; static unsigned __stdcall ThreadProc( void * pv ); // make copy constructor and assignment operator inaccessible CThread( const CThread & ); CThread &operator=( const CThread & ); #ifdef _WIN32 HANDLE m_hThread; ThreadId_t m_threadId; #elif _LINUX pthread_t m_threadId; #endif int m_result; char m_szName[32]; void * m_pStackBase; }; //----------------------------------------------------------------------------- // Simple thread class encompasses the notion of a worker thread, handing // synchronized communication. //----------------------------------------------------------------------------- #ifdef _WIN32 // These are internal reserved error results from a call attempt enum WTCallResult_t { WTCR_FAIL = -1, WTCR_TIMEOUT = -2, WTCR_THREAD_GONE = -3, }; class TT_CLASS CWorkerThread : public CThread { public: CWorkerThread(); //----------------------------------------------------- // // Inter-thread communication // // Calls in either direction take place on the same "channel." // Seperate functions are specified to make identities obvious // //----------------------------------------------------- // Master: Signal the thread, and block for a response int CallWorker( unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true ); // Worker: Signal the thread, and block for a response int CallMaster( unsigned, unsigned timeout = TT_INFINITE ); // Wait for the next request bool WaitForCall( unsigned dwTimeout, unsigned *pResult = NULL ); bool WaitForCall( unsigned *pResult = NULL ); // Is there a request? bool PeekCall( unsigned *pParam = NULL ); // Reply to the request void Reply( unsigned ); // If you want to do WaitForMultipleObjects you'll need to include // this handle in your wait list or you won't be responsive HANDLE GetCallHandle(); // Find out what the request was unsigned GetCallParam() const; // Boost the worker thread to the master thread, if worker thread is lesser, return old priority int BoostPriority(); protected: typedef uint32 (__stdcall *WaitFunc_t)( uint32 nHandles, const HANDLE*pHandles, int bWaitAll, uint32 timeout ); int Call( unsigned, unsigned timeout, bool fBoost, WaitFunc_t = NULL ); private: CWorkerThread( const CWorkerThread & ); CWorkerThread &operator=( const CWorkerThread & ); #ifdef _WIN32 CThreadEvent m_EventSend; CThreadEvent m_EventComplete; #endif unsigned m_Param; int m_ReturnVal; }; #else typedef CThread CWorkerThread; #endif //----------------------------------------------------------------------------- // // CThreadMutex. Inlining to reduce overhead and to allow client code // to decide debug status (tracing) // //----------------------------------------------------------------------------- #ifdef _WIN32 typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; extern "C" { #ifndef _XBOX void __declspec(dllimport) __stdcall InitializeCriticalSection(CRITICAL_SECTION *); void __declspec(dllimport) __stdcall EnterCriticalSection(CRITICAL_SECTION *); void __declspec(dllimport) __stdcall LeaveCriticalSection(CRITICAL_SECTION *); void __declspec(dllimport) __stdcall DeleteCriticalSection(CRITICAL_SECTION *); #else void __declspec(dllimport) __stdcall RtlInitializeCriticalSection(RTL_CRITICAL_SECTION *); void __declspec(dllimport) __stdcall RtlEnterCriticalSection(RTL_CRITICAL_SECTION *); void __declspec(dllimport) __stdcall RtlLeaveCriticalSection(RTL_CRITICAL_SECTION *); #define RtlDeleteCriticalSection(CriticalSection) ((void)0) #define InitializeCriticalSection RtlInitializeCriticalSection #define DeleteCriticalSection RtlDeleteCriticalSection #define EnterCriticalSection RtlEnterCriticalSection #define LeaveCriticalSection RtlLeaveCriticalSection #define TryEnterCriticalSection RtlTryEnterCriticalSection #endif }; //--------------------------------------------------------- inline CThreadMutex::CThreadMutex() { #ifdef THREAD_MUTEX_TRACING_ENABLED memset( &m_CriticalSection, 0, sizeof(m_CriticalSection) ); #endif InitializeCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); #ifdef THREAD_MUTEX_TRACING_SUPPORTED // These need to be initialized unconditionally in case mixing release & debug object modules // Lock and unlock may be emitted as COMDATs, in which case may get spurious output m_currentOwnerID = m_lockCount = 0; m_bTrace = false; #endif } //--------------------------------------------------------- inline CThreadMutex::~CThreadMutex() { DeleteCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); } //--------------------------------------------------------- inline void CThreadMutex::Lock() { #ifdef THREAD_MUTEX_TRACING_ENABLED uint thisThreadID = ThreadGetCurrentId(); if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) ) Msg( "Thread %u about to wait for lock %x owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); #endif VCRHook_EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); #ifdef THREAD_MUTEX_TRACING_ENABLED if (m_lockCount == 0) { // we now own it for the first time. Set owner information m_currentOwnerID = thisThreadID; if ( m_bTrace ) Msg( "Thread %u now owns lock 0x%x\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); } m_lockCount++; #endif } //--------------------------------------------------------- inline void CThreadMutex::Unlock() { #ifdef THREAD_MUTEX_TRACING_ENABLED AssertMsg( m_lockCount >= 1, "Invalid unlock of thread lock" ); m_lockCount--; if (m_lockCount == 0) { if ( m_bTrace ) Msg( "Thread %u releasing lock 0x%x\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); m_currentOwnerID = 0; } #endif LeaveCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); } //--------------------------------------------------------- inline bool CThreadMutex::AssertOwnedByCurrentThread() { #ifdef THREAD_MUTEX_TRACING_ENABLED if (ThreadGetCurrentId() == m_currentOwnerID) return true; AssertMsg3( 0, "Expected thread %u as owner of lock 0x%x, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); return false; #else return true; #endif } //--------------------------------------------------------- inline void CThreadMutex::SetTrace( bool bTrace ) { #ifdef THREAD_MUTEX_TRACING_ENABLED m_bTrace = bTrace; #endif } //--------------------------------------------------------- #elif _LINUX inline CThreadMutex::CThreadMutex() { // enable recursive locks as we need them pthread_mutexattr_init( &m_Attr ); pthread_mutexattr_settype( &m_Attr, PTHREAD_MUTEX_RECURSIVE_NP ); pthread_mutex_init( &m_Mutex, &m_Attr ); } //--------------------------------------------------------- inline CThreadMutex::~CThreadMutex() { pthread_mutex_destroy( &m_Mutex ); } //--------------------------------------------------------- inline void CThreadMutex::Lock() { pthread_mutex_lock( &m_Mutex ); } //--------------------------------------------------------- inline void CThreadMutex::Unlock() { pthread_mutex_unlock( &m_Mutex ); } //--------------------------------------------------------- inline bool CThreadMutex::AssertOwnedByCurrentThread() { return true; } //--------------------------------------------------------- inline void CThreadMutex::SetTrace(bool fTrace) { } #endif // _LINUX //----------------------------------------------------------------------------- #endif // THREADTOOLS_H
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |