#ifndef THREADTOOLS_H #define THREADTOOLS_H #include #include "platform.h" #include "dbg.h" #include "vcrmode.h" #ifdef PLATFORM_WINDOWS_PC #include #endif #ifdef POSIX #include #include #define WAIT_OBJECT_0 0 #define WAIT_TIMEOUT 0x00000102 #define WAIT_FAILED -1 #define THREAD_PRIORITY_HIGHEST 2 #endif #if defined( _WIN32 ) #pragma once #pragma warning(push) #pragma warning(disable:4251) #endif #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 POSIX #define THREAD_LOCAL __thread #endif #endif #endif typedef unsigned long ThreadId_t; FORWARD_DECLARE_HANDLE(ThreadHandle_t); typedef unsigned (*ThreadFunc_t)(void* pParam); PLATFORM_OVERLOAD ThreadHandle_t CreateSimpleThread(ThreadFunc_t, void* pParam, ThreadId_t* pID, unsigned stackSize = 0); PLATFORM_INTERFACE ThreadHandle_t CreateSimpleThread(ThreadFunc_t, void* pParam, unsigned stackSize = 0); PLATFORM_INTERFACE bool ReleaseThreadHandle(ThreadHandle_t); PLATFORM_INTERFACE void ThreadSleep(unsigned duration = 0); PLATFORM_INTERFACE uint ThreadGetCurrentId(); PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle(); PLATFORM_INTERFACE int ThreadGetPriority(ThreadHandle_t hThread = NULL); PLATFORM_INTERFACE bool ThreadSetPriority(ThreadHandle_t hThread, int priority); inline bool ThreadSetPriority(int priority) { return ThreadSetPriority(NULL, priority); } PLATFORM_INTERFACE bool ThreadInMainThread(); PLATFORM_INTERFACE void DeclareCurrentThreadIsMainThread(); typedef int (*ThreadedLoadLibraryFunc_t)(); PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc(ThreadedLoadLibraryFunc_t func); PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc(); #if defined( _WIN32 ) && !defined( _WIN64 ) && !defined( _X360 ) extern "C" unsigned long __declspec(dllimport) __stdcall GetCurrentThreadId(); #define ThreadGetCurrentId GetCurrentThreadId #endif inline void ThreadPause() { #if defined( PLATFORM_WINDOWS_PC ) _mm_pause(); #elif POSIX __asm __volatile("pause"); #elif defined( _X360 ) #else #error "implement me" #endif } PLATFORM_INTERFACE bool ThreadJoin(ThreadHandle_t, unsigned timeout = TT_INFINITE); PLATFORM_INTERFACE void ThreadDetach(ThreadHandle_t); PLATFORM_INTERFACE void ThreadSetDebugName(ThreadId_t id, const char* pszName); inline void ThreadSetDebugName(const char* pszName) { ThreadSetDebugName((ThreadId_t)-1, pszName); } PLATFORM_INTERFACE void ThreadSetAffinity(ThreadHandle_t hThread, int nAffinityMask); enum ThreadWaitResult_t { TW_FAILED = 0xffffffff, TW_TIMEOUT = 0x00000102, }; #ifdef _WIN32 PLATFORM_INTERFACE int ThreadWaitForObjects(int nEvents, const HANDLE* pHandles, bool bWaitAll = true, unsigned timeout = TT_INFINITE); inline int ThreadWaitForObject(HANDLE handle, bool bWaitAll = true, unsigned timeout = TT_INFINITE) { return ThreadWaitForObjects(1, &handle, bWaitAll, timeout); } #endif #ifdef _WIN32 #define NOINLINE #elif POSIX #define NOINLINE __attribute__ ((noinline)) #endif #if defined( _X360 ) || defined( _PS3 ) #define ThreadMemoryBarrier() __lwsync() #elif defined(_MSC_VER) #if _MSC_VER < 1500 #pragma intrinsic(_ReadWriteBarrier) #endif #define ThreadMemoryBarrier() _ReadWriteBarrier() #elif defined(GNUC) #define ThreadMemoryBarrier() asm volatile("" ::: "memory") #else #error Every platform needs to define ThreadMemoryBarrier to at least prevent compiler reordering #endif #if defined(_WIN32) && !defined(_X360) #if ( _MSC_VER >= 1310 ) #define USE_INTRINSIC_INTERLOCKED #endif #endif #ifdef USE_INTRINSIC_INTERLOCKED extern "C" { long __cdecl _InterlockedIncrement(volatile long*); long __cdecl _InterlockedDecrement(volatile long*); long __cdecl _InterlockedExchange(volatile long*, long); long __cdecl _InterlockedExchangeAdd(volatile long*, long); long __cdecl _InterlockedCompareExchange(volatile long*, long, long); } #pragma intrinsic( _InterlockedCompareExchange ) #pragma intrinsic( _InterlockedDecrement ) #pragma intrinsic( _InterlockedExchange ) #pragma intrinsic( _InterlockedExchangeAdd ) #pragma intrinsic( _InterlockedIncrement ) inline long ThreadInterlockedIncrement(long volatile* p) { Assert((size_t)p % 4 == 0); return _InterlockedIncrement(p); } inline long ThreadInterlockedDecrement(long volatile* p) { Assert((size_t)p % 4 == 0); return _InterlockedDecrement(p); } inline long ThreadInterlockedExchange(long volatile* p, long value) { Assert((size_t)p % 4 == 0); return _InterlockedExchange(p, value); } inline long ThreadInterlockedExchangeAdd(long volatile* p, long value) { Assert((size_t)p % 4 == 0); return _InterlockedExchangeAdd(p, value); } inline long ThreadInterlockedCompareExchange(long volatile* p, long value, long comperand) { Assert((size_t)p % 4 == 0); return _InterlockedCompareExchange(p, value, comperand); } inline bool ThreadInterlockedAssignIf(long volatile* p, long value, long comperand) { Assert((size_t)p % 4 == 0); return (_InterlockedCompareExchange(p, value, comperand) == comperand); } #else PLATFORM_INTERFACE long ThreadInterlockedIncrement(long volatile*); PLATFORM_INTERFACE long ThreadInterlockedDecrement(long volatile*); PLATFORM_INTERFACE long ThreadInterlockedExchange(long volatile*, long value); PLATFORM_INTERFACE long ThreadInterlockedExchangeAdd(long volatile*, long value); PLATFORM_INTERFACE long ThreadInterlockedCompareExchange(long volatile*, long value, long comperand); PLATFORM_INTERFACE bool ThreadInterlockedAssignIf(long volatile*, long value, long comperand); #endif inline unsigned ThreadInterlockedExchangeSubtract(long volatile* p, long value) { return ThreadInterlockedExchangeAdd((long volatile*)p, -value); } #if defined( USE_INTRINSIC_INTERLOCKED ) && !defined( _WIN64 ) #define TIPTR() inline void* ThreadInterlockedExchangePointer(void* volatile* p, void* value) { return (void*)_InterlockedExchange(reinterpret_cast(p), reinterpret_cast(value)); } inline void* ThreadInterlockedCompareExchangePointer(void* volatile* p, void* value, void* comperand) { return (void*)_InterlockedCompareExchange(reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand)); } inline bool ThreadInterlockedAssignPointerIf(void* volatile* p, void* value, void* comperand) { return (_InterlockedCompareExchange(reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand)) == reinterpret_cast(comperand)); } #else PLATFORM_INTERFACE void* ThreadInterlockedExchangePointer(void* volatile*, void* value) NOINLINE; PLATFORM_INTERFACE void* ThreadInterlockedCompareExchangePointer(void* volatile*, void* value, void* comperand) NOINLINE; PLATFORM_INTERFACE bool ThreadInterlockedAssignPointerIf(void* volatile*, void* value, void* comperand) NOINLINE; #endif inline void const* ThreadInterlockedExchangePointerToConst(void const* volatile* p, void const* value) { return ThreadInterlockedExchangePointer(const_cast (p), const_cast (value)); } inline void const* ThreadInterlockedCompareExchangePointerToConst(void const* volatile* p, void const* value, void const* comperand) { return ThreadInterlockedCompareExchangePointer(const_cast (p), const_cast (value), const_cast (comperand)); } inline bool ThreadInterlockedAssignPointerToConstIf(void const* volatile* p, void const* value, void const* comperand) { return ThreadInterlockedAssignPointerIf(const_cast (p), const_cast (value), const_cast (comperand)); } #if defined( PLATFORM_64BITS ) #if defined (_WIN32) typedef __m128i int128; inline int128 int128_zero() { return _mm_setzero_si128(); } #else typedef __int128_t int128; #define int128_zero() 0 #endif PLATFORM_INTERFACE bool ThreadInterlockedAssignIf128(volatile int128* pDest, const int128& value, const int128& comperand) NOINLINE; #endif PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64(int64 volatile*) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64(int64 volatile*) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedCompareExchange64(int64 volatile*, int64 value, int64 comperand) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedExchange64(int64 volatile*, int64 value) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64(int64 volatile*, int64 value) NOINLINE; PLATFORM_INTERFACE bool ThreadInterlockedAssignIf64(volatile int64* pDest, int64 value, int64 comperand) NOINLINE; inline unsigned ThreadInterlockedExchangeSubtract(unsigned volatile* p, unsigned value) { return ThreadInterlockedExchangeAdd((long volatile*)p, value); } inline unsigned ThreadInterlockedIncrement(unsigned volatile* p) { return ThreadInterlockedIncrement((long volatile*)p); } inline unsigned ThreadInterlockedDecrement(unsigned volatile* p) { return ThreadInterlockedDecrement((long volatile*)p); } inline unsigned ThreadInterlockedExchange(unsigned volatile* p, unsigned value) { return ThreadInterlockedExchange((long volatile*)p, value); } inline unsigned ThreadInterlockedExchangeAdd(unsigned volatile* p, unsigned value) { return ThreadInterlockedExchangeAdd((long volatile*)p, value); } inline unsigned ThreadInterlockedCompareExchange(unsigned volatile* p, unsigned value, unsigned comperand) { return ThreadInterlockedCompareExchange((long volatile*)p, value, comperand); } inline bool ThreadInterlockedAssignIf(unsigned volatile* p, unsigned value, unsigned comperand) { return ThreadInterlockedAssignIf((long volatile*)p, value, comperand); } inline int ThreadInterlockedExchangeSubtract(int volatile* p, int value) { return ThreadInterlockedExchangeAdd((long volatile*)p, value); } inline int ThreadInterlockedIncrement(int volatile* p) { return ThreadInterlockedIncrement((long volatile*)p); } inline int ThreadInterlockedDecrement(int volatile* p) { return ThreadInterlockedDecrement((long volatile*)p); } inline int ThreadInterlockedExchange(int volatile* p, int value) { return ThreadInterlockedExchange((long volatile*)p, value); } inline int ThreadInterlockedExchangeAdd(int volatile* p, int value) { return ThreadInterlockedExchangeAdd((long volatile*)p, value); } inline int ThreadInterlockedCompareExchange(int volatile* p, int value, int comperand) { return ThreadInterlockedCompareExchange((long volatile*)p, value, comperand); } inline bool ThreadInterlockedAssignIf(int volatile* p, int value, int comperand) { return ThreadInterlockedAssignIf((long volatile*)p, value, comperand); } #if defined(_WIN32) && defined(THREAD_PROFILER) PLATFORM_INTERFACE void ThreadNotifySyncPrepare(void* p); PLATFORM_INTERFACE void ThreadNotifySyncCancel(void* p); PLATFORM_INTERFACE void ThreadNotifySyncAcquired(void* p); PLATFORM_INTERFACE void ThreadNotifySyncReleasing(void* p); #else #define ThreadNotifySyncPrepare(p) ((void)0) #define ThreadNotifySyncCancel(p) ((void)0) #define ThreadNotifySyncAcquired(p) ((void)0) #define ThreadNotifySyncReleasing(p) ((void)0) #endif #ifndef NO_THREAD_LOCAL #if defined(_LINUX) && !defined(OSX) #define PLAT_COMPILER_SUPPORTED_THREADLOCALS 1 #define CTHREADLOCALINTEGER( typ ) __thread int #define CTHREADLOCALINT __thread int #define CTHREADLOCALPTR( typ ) __thread typ * #define CTHREADLOCAL( typ ) __thread typ #define GETLOCAL( x ) ( x ) #endif #if defined(WIN32) || defined(OSX) #ifndef __AFXTLS_H__ #define CTHREADLOCALINT CThreadLocalInt #define CTHREADLOCALINTEGER( typ ) CThreadLocalInt #define CTHREADLOCALPTR( typ ) CThreadLocalPtr #define CTHREADLOCAL( typ ) CThreadLocal #define GETLOCAL( x ) ( x.Get() ) #endif #endif #endif #ifndef __AFXTLS_H__ #ifndef NO_THREAD_LOCAL class PLATFORM_CLASS CThreadLocalBase { public: CThreadLocalBase(); ~CThreadLocalBase(); void* Get() const; void Set(void*); private: #ifdef _WIN32 uint32 m_index; #elif POSIX pthread_key_t m_index; #endif }; #ifndef __AFXTLS_H__ template class CThreadLocal : public CThreadLocalBase { public: CThreadLocal() { COMPILE_TIME_ASSERT(sizeof(T) == sizeof(void*)); } T Get() const { return reinterpret_cast(CThreadLocalBase::Get()); } void Set(T val) { CThreadLocalBase::Set(reinterpret_cast(val)); } }; #endif template class CThreadLocalInt : public CThreadLocal { public: CThreadLocalInt() { COMPILE_TIME_ASSERT(sizeof(T) >= sizeof(int)); } operator int() const { return (int)this->Get(); } int operator=(int i) { this->Set((intp)i); return i; } int operator++() { T i = this->Get(); this->Set(++i); return (int)i; } int operator++(int) { T i = this->Get(); this->Set(i + 1); return (int)i; } int operator--() { T i = this->Get(); this->Set(--i); return (int)i; } int operator--(int) { T i = this->Get(); this->Set(i - 1); return (int)i; } }; template 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: CThreadLocalPtr(T* pFrom); CThreadLocalPtr(const CThreadLocalPtr& from); T** operator &(); T* const* operator &() const; void operator=(const CThreadLocalPtr& from); bool operator==(const CThreadLocalPtr& p) const; bool operator!=(const CThreadLocalPtr& p) const; }; #endif #endif template class CInterlockedIntT { public: CInterlockedIntT() : m_value(0) { COMPILE_TIME_ASSERT(sizeof(T) == sizeof(long)); } CInterlockedIntT(T value) : m_value(value) {} T GetRaw() const { return m_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 ThreadInterlockedAssignIf((long*)&m_value, (long)newValue, (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 CInterlockedInt; typedef CInterlockedIntT CInterlockedUInt; template class CInterlockedPtr { public: CInterlockedPtr() : m_value(0) {} 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); } #if defined( PLATFORM_64BITS ) T* operator++() { return ((T*)ThreadInterlockedExchangeAdd64((int64*)&m_value, sizeof(T))) + 1; } T* operator++(int) { return (T*)ThreadInterlockedExchangeAdd64((int64*)&m_value, sizeof(T)); } T* operator--() { return ((T*)ThreadInterlockedExchangeAdd64((int64*)&m_value, -sizeof(T))) - 1; } T* operator--(int) { return (T*)ThreadInterlockedExchangeAdd64((int64*)&m_value, -sizeof(T)); } bool AssignIf(T* conditionValue, T* newValue) { return ThreadInterlockedAssignPointerToConstIf((void const**)&m_value, (void const*)newValue, (void const*)conditionValue); } T* operator=(T* newValue) { ThreadInterlockedExchangePointerToConst((void const**)&m_value, (void const*)newValue); return newValue; } void operator+=(int add) { ThreadInterlockedExchangeAdd64((int64*)&m_value, add * sizeof(T)); } #else 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 ThreadInterlockedAssignPointerToConstIf((void const**)&m_value, (void const*)newValue, (void const*)conditionValue); } T* operator=(T* newValue) { ThreadInterlockedExchangePointerToConst((void const**)&m_value, (void const*)newValue); return newValue; } void operator+=(int add) { ThreadInterlockedExchangeAdd((long*)&m_value, add * sizeof(T)); } #endif void operator-=(int subtract) { operator+=(-subtract); } T* operator+(int rhs) const { return m_value + rhs; } T* operator-(int rhs) const { return m_value - rhs; } T* operator+(unsigned rhs) const { return m_value + rhs; } T* operator-(unsigned rhs) const { return m_value - rhs; } size_t operator-(T* p) const { return m_value - p; } size_t operator-(const CInterlockedPtr& p) const { return m_value - p.m_value; } private: T* volatile m_value; }; class PLATFORM_CLASS CThreadMutex { public: CThreadMutex(); ~CThreadMutex(); void Lock(); void Lock() const { (const_cast(this))->Lock(); } void Unlock(); void Unlock() const { (const_cast(this))->Unlock(); } bool TryLock(); bool TryLock() const { return (const_cast(this))->TryLock(); } bool AssertOwnedByCurrentThread(); void SetTrace(bool); private: CThreadMutex(const CThreadMutex&); CThreadMutex& operator=(const CThreadMutex&); #if defined( _WIN32 ) #ifdef _WIN64 #define TT_SIZEOF_CRITICALSECTION 40 #else #ifndef _X360 #define TT_SIZEOF_CRITICALSECTION 24 #else #define TT_SIZEOF_CRITICALSECTION 28 #endif #endif byte m_CriticalSection[TT_SIZEOF_CRITICALSECTION]; #elif defined(POSIX) pthread_mutex_t m_Mutex; pthread_mutexattr_t m_Attr; #else #error #endif #ifdef THREAD_MUTEX_TRACING_SUPPORTED uint m_currentOwnerID; uint16 m_lockCount; bool m_bTrace; #endif }; #if !defined(THREAD_PROFILER) class CThreadFastMutex { public: CThreadFastMutex() : m_ownerID(0), m_depth(0) { } private: FORCEINLINE bool TryLockInline(const uint32 threadId) volatile { if (threadId != m_ownerID && !ThreadInterlockedAssignIf((volatile long*)&m_ownerID, (long)threadId, 0)) return false; ThreadMemoryBarrier(); ++m_depth; return true; } bool TryLock(const uint32 threadId) volatile { return TryLockInline(threadId); } PLATFORM_CLASS void Lock(const uint32 threadId, unsigned nSpinSleepTime) volatile; public: bool TryLock() volatile { #ifdef _DEBUG if (m_depth == INT_MAX) DebuggerBreak(); if (m_depth < 0) DebuggerBreak(); #endif return TryLockInline(ThreadGetCurrentId()); } #ifndef _DEBUG FORCEINLINE #endif void Lock(unsigned int nSpinSleepTime = 0) volatile { const uint32 threadId = ThreadGetCurrentId(); if (!TryLockInline(threadId)) { ThreadPause(); Lock(threadId, nSpinSleepTime); } #ifdef _DEBUG if (m_ownerID != ThreadGetCurrentId()) DebuggerBreak(); if (m_depth == INT_MAX) DebuggerBreak(); if (m_depth < 0) DebuggerBreak(); #endif } #ifndef _DEBUG FORCEINLINE #endif void Unlock() volatile { #ifdef _DEBUG if (m_ownerID != ThreadGetCurrentId()) DebuggerBreak(); if (m_depth <= 0) DebuggerBreak(); #endif --m_depth; if (!m_depth) { ThreadMemoryBarrier(); ThreadInterlockedExchange(&m_ownerID, 0); } } #ifdef WIN32 bool TryLock() const volatile { return (const_cast(this))->TryLock(); } void Lock(unsigned nSpinSleepTime = 1) const volatile { (const_cast(this))->Lock(nSpinSleepTime); } void Unlock() const volatile { (const_cast(this))->Unlock(); } #endif bool AssertOwnedByCurrentThread() { return true; } void SetTrace(bool) {} uint32 GetOwnerId() const { return m_ownerID; } int GetDepth() const { return m_depth; } private: volatile uint32 m_ownerID; int m_depth; }; class ALIGN128 CAlignedThreadFastMutex : public CThreadFastMutex { public: CAlignedThreadFastMutex() { Assert((size_t)this % 128 == 0 && sizeof(*this) == 128); } private: uint8 pad[128 - sizeof(CThreadFastMutex)]; } ALIGN128_POST; #else typedef CThreadMutex CThreadFastMutex; #endif class CThreadNullMutex { public: static void Lock() {} static void Unlock() {} static bool TryLock() { return true; } static bool AssertOwnedByCurrentThread() { return true; } static void SetTrace(bool b) {} static uint32 GetOwnerId() { return 0; } static int GetDepth() { return 0; } }; template class CThreadConditionalMutex : public BaseClass { public: void Lock() { if (*pCondition) BaseClass::Lock(); } void Lock() const { if (*pCondition) BaseClass::Lock(); } void Unlock() { if (*pCondition) BaseClass::Unlock(); } void Unlock() const { if (*pCondition) BaseClass::Unlock(); } bool TryLock() { if (*pCondition) return BaseClass::TryLock(); else return true; } bool TryLock() const { if (*pCondition) return BaseClass::TryLock(); else return true; } bool AssertOwnedByCurrentThread() { if (*pCondition) return BaseClass::AssertOwnedByCurrentThread(); else return true; } void SetTrace(bool b) { if (*pCondition) BaseClass::SetTrace(b); } }; template class CThreadTerminalMutex : public BaseClass { public: bool TryLock() { if (!BaseClass::TryLock()) { DebuggerBreak(); return false; } return true; } bool TryLock() const { if (!BaseClass::TryLock()) { DebuggerBreak(); return false; } return true; } void Lock() { if (!TryLock()) BaseClass::Lock(); } void Lock() const { if (!TryLock()) BaseClass::Lock(); } }; template class CAutoLockT { public: FORCEINLINE CAutoLockT(MUTEX_TYPE& lock) : m_lock(lock) { m_lock.Lock(); } FORCEINLINE CAutoLockT(const MUTEX_TYPE& lock) : m_lock(const_cast(lock)) { m_lock.Lock(); } FORCEINLINE ~CAutoLockT() { m_lock.Unlock(); } private: MUTEX_TYPE& m_lock; CAutoLockT(const CAutoLockT&); CAutoLockT& operator=(const CAutoLockT&); }; typedef CAutoLockT CAutoLock; template struct CAutoLockTypeDeducer {}; template <> struct CAutoLockTypeDeducer { typedef CThreadMutex Type_t; }; template <> struct CAutoLockTypeDeducer { typedef CThreadNullMutex Type_t; }; #if !defined(THREAD_PROFILER) template <> struct CAutoLockTypeDeducer { typedef CThreadFastMutex Type_t; }; template <> struct CAutoLockTypeDeducer { typedef CAlignedThreadFastMutex Type_t; }; #endif #define AUTO_LOCK_( type, mutex ) \ CAutoLockT< type > UNIQUE_ID( static_cast( mutex ) ) #if defined(GNUC) template T strip_cv_quals_for_mutex(T&); template T strip_cv_quals_for_mutex(const T&); template T strip_cv_quals_for_mutex(volatile T&); template T strip_cv_quals_for_mutex(const volatile T&); #define AUTO_LOCK( mutex ) \ AUTO_LOCK_( typeof(::strip_cv_quals_for_mutex(mutex)), mutex ) #else #define AUTO_LOCK( mutex ) \ AUTO_LOCK_( CAutoLockTypeDeducer::Type_t, mutex ) #endif #define AUTO_LOCK_FM( mutex ) \ AUTO_LOCK_( CThreadFastMutex, mutex ) #define LOCAL_THREAD_LOCK_( tag ) \ ; \ static CThreadFastMutex autoMutex_##tag; \ AUTO_LOCK( autoMutex_##tag ) #define LOCAL_THREAD_LOCK() \ LOCAL_THREAD_LOCK_(_) class PLATFORM_CLASS CThreadSyncObject { public: ~CThreadSyncObject(); bool operator!() const; #ifdef _WIN32 operator HANDLE() { return GetHandle(); } const HANDLE GetHandle() const { return m_hSyncObject; } #endif bool Wait(uint32 dwTimeout = TT_INFINITE); protected: CThreadSyncObject(); void AssertUseable(); #ifdef _WIN32 HANDLE m_hSyncObject; bool m_bCreatedHandle; #elif defined(POSIX) pthread_mutex_t m_Mutex; pthread_cond_t m_Condition; bool m_bInitalized; int m_cSet; bool m_bManualReset; bool m_bWakeForEvent; #else #error "Implement me" #endif private: CThreadSyncObject(const CThreadSyncObject&); CThreadSyncObject& operator=(const CThreadSyncObject&); }; #if defined( _WIN32 ) class PLATFORM_CLASS CThreadSemaphore : public CThreadSyncObject { public: CThreadSemaphore(long initialValue, long maxValue); bool Release(long releaseCount = 1, long* pPreviousCount = NULL); private: CThreadSemaphore(const CThreadSemaphore&); CThreadSemaphore& operator=(const CThreadSemaphore&); }; class PLATFORM_CLASS CThreadFullMutex : public CThreadSyncObject { public: CThreadFullMutex(bool bEstablishInitialOwnership = false, const char* pszName = NULL); bool Release(); 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 PLATFORM_CLASS CThreadEvent : public CThreadSyncObject { public: CThreadEvent(bool fManualReset = false); #ifdef WIN32 CThreadEvent(HANDLE hHandle); #endif bool Set(); bool Reset(); bool Check(); bool Wait(uint32 dwTimeout = TT_INFINITE); private: CThreadEvent(const CThreadEvent&); CThreadEvent& operator=(const CThreadEvent&); }; class CThreadManualEvent : public CThreadEvent { public: CThreadManualEvent() : CThreadEvent(true) { } }; inline int ThreadWaitForEvents(int nEvents, CThreadEvent* const* pEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE) { #ifdef POSIX Assert(nEvents == 1); if (pEvents[0]->Wait(timeout)) return WAIT_OBJECT_0; else return WAIT_TIMEOUT; #else HANDLE handles[64]; for (unsigned int i = 0; i < min(nEvents, ARRAYSIZE(handles)); i++) handles[i] = pEvents[i]->GetHandle(); return ThreadWaitForObjects(nEvents, handles, bWaitAll, timeout); #endif } class PLATFORM_CLASS CThreadRWLock { public: CThreadRWLock(); void LockForRead(); void UnlockRead(); void LockForWrite(); void UnlockWrite(); void LockForRead() const { const_cast(this)->LockForRead(); } void UnlockRead() const { const_cast(this)->UnlockRead(); } void LockForWrite() const { const_cast(this)->LockForWrite(); } void UnlockWrite() const { const_cast(this)->UnlockWrite(); } private: void WaitForRead(); #ifdef WIN32 CThreadFastMutex m_mutex; #else CThreadMutex m_mutex; #endif CThreadEvent m_CanWrite; CThreadEvent m_CanRead; int m_nWriters; int m_nActiveReaders; int m_nPendingReaders; }; class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock { public: CThreadSpinRWLock() { COMPILE_TIME_ASSERT(sizeof(LockInfo_t) == sizeof(int64)); Assert((intp)this % 8 == 0); memset(this, 0, sizeof(*this)); } bool TryLockForWrite(); bool TryLockForRead(); void LockForRead(); void UnlockRead(); void LockForWrite(); void UnlockWrite(); bool TryLockForWrite() const { return const_cast(this)->TryLockForWrite(); } bool TryLockForRead() const { return const_cast(this)->TryLockForRead(); } void LockForRead() const { const_cast(this)->LockForRead(); } void UnlockRead() const { const_cast(this)->UnlockRead(); } void LockForWrite() const { const_cast(this)->LockForWrite(); } void UnlockWrite() const { const_cast(this)->UnlockWrite(); } private: struct LockInfo_t { uint32 m_writerId; int m_nReaders; }; bool AssignIf(const LockInfo_t& newValue, const LockInfo_t& comperand); bool TryLockForWrite(const uint32 threadId); void SpinLockForWrite(const uint32 threadId); volatile LockInfo_t m_lockInfo; CInterlockedInt m_nWriters; } ALIGN8_POST; class PLATFORM_CLASS CThread { public: CThread(); virtual ~CThread(); const char* GetName(); void SetName(const char*); size_t CalcStackDepth(void* pStackVariable) { return ((byte*)m_pStackBase - (byte*)pStackVariable); } virtual bool Start(unsigned nBytesStack = 0); bool IsAlive(); bool Join(unsigned timeout = TT_INFINITE); #ifdef _WIN32 HANDLE GetThreadHandle(); uint GetThreadId(); #elif defined( LINUX ) uint GetThreadId(); #endif int GetResult(); void Stop(int exitCode = 0); int GetPriority() const; bool SetPriority(int); void SuspendCooperative(); void ResumeCooperative(); void BWaitForThreadSuspendCooperative(); #ifndef LINUX unsigned int Suspend(); unsigned int Resume(); #endif bool Terminate(int exitCode = 0); static CThread* GetCurrentCThread(); #ifdef Yield #undef Yield #endif static void Yield(); static void Sleep(unsigned duration); protected: virtual bool Init(); virtual int Run() = 0; virtual void OnExit(); void Cleanup(); bool WaitForCreateComplete(CThreadEvent* pEvent); typedef unsigned(__stdcall* ThreadProc_t)(void*); virtual ThreadProc_t GetThreadProc(); virtual bool IsThreadRunning(); CThreadMutex m_Lock; #ifdef WIN32 ThreadHandle_t GetThreadID() const { return (ThreadHandle_t)m_hThread; } #else ThreadId_t GetThreadID() const { return (ThreadId_t)m_threadId; } #endif private: enum Flags { SUPPORT_STOP_PROTOCOL = 1 << 0 }; struct ThreadInit_t { CThread* pThread; CThreadEvent* pInitCompleteEvent; bool* pfInitSuccess; }; static unsigned __stdcall ThreadProc(void* pv); CThread(const CThread&); CThread& operator=(const CThread&); #ifdef _WIN32 HANDLE m_hThread; ThreadId_t m_threadId; #elif defined(POSIX) pthread_t m_threadId; #endif CInterlockedInt m_nSuspendCount; CThreadEvent m_SuspendEvent; CThreadEvent m_SuspendEventSignal; int m_result; char m_szName[32]; void* m_pStackBase; unsigned m_flags; }; class PLATFORM_CLASS CValidatableThread : public CThread { public: CValidatableThread() { m_bSleepForValidate = false; m_bSleepingForValidate = false; } #ifdef DBGFLAG_VALIDATE virtual void SleepForValidate() { m_bSleepForValidate = true; } bool BSleepingForValidate() { return m_bSleepingForValidate; } virtual void WakeFromValidate() { m_bSleepForValidate = false; } #endif protected: bool m_bSleepForValidate; bool m_bSleepingForValidate; }; enum WTCallResult_t { WTCR_FAIL = -1, WTCR_TIMEOUT = -2, WTCR_THREAD_GONE = -3, }; class CFunctor; class PLATFORM_CLASS CWorkerThread : public CThread { public: CWorkerThread(); int CallWorker(unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true, CFunctor* pParamFunctor = NULL); int CallMaster(unsigned, unsigned timeout = TT_INFINITE); bool WaitForCall(unsigned dwTimeout, unsigned* pResult = NULL); bool WaitForCall(unsigned* pResult = NULL); bool PeekCall(unsigned* pParam = NULL, CFunctor** ppParamFunctor = NULL); void Reply(unsigned); int WaitForReply(unsigned timeout = TT_INFINITE); CThreadEvent& GetCallHandle(); unsigned GetCallParam(CFunctor** ppParamFunctor = NULL) const; int BoostPriority(); protected: #ifndef _WIN32 #define __stdcall #endif typedef uint32(__stdcall* WaitFunc_t)(int nEvents, CThreadEvent* const* pEvents, int bWaitAll, uint32 timeout); int Call(unsigned, unsigned timeout, bool fBoost, WaitFunc_t = NULL, CFunctor* pParamFunctor = NULL); int WaitForReply(unsigned timeout, WaitFunc_t); private: CWorkerThread(const CWorkerThread&); CWorkerThread& operator=(const CWorkerThread&); CThreadEvent m_EventSend; CThreadEvent m_EventComplete; unsigned m_Param; CFunctor* m_pParamFunctor; int m_ReturnVal; }; template class CMessageQueue { CThreadEvent SignalEvent; CThreadMutex QueueAccessMutex; struct MsgNode { MsgNode* Next; T Data; }; MsgNode* Head; MsgNode* Tail; public: CMessageQueue(void) { Head = Tail = NULL; } bool MessageWaiting(void) { return (Head != NULL); } void WaitMessage(T* pMsg) { for (;;) { while (!MessageWaiting()) SignalEvent.Wait(); QueueAccessMutex.Lock(); if (!Head) { QueueAccessMutex.Unlock(); continue; } *(pMsg) = Head->Data; MsgNode* remove_this = Head; Head = Head->Next; if (!Head) Tail = NULL; QueueAccessMutex.Unlock(); delete remove_this; break; } } void QueueMessage(T const& Msg) { MsgNode* new1 = new MsgNode; new1->Data = Msg; new1->Next = NULL; QueueAccessMutex.Lock(); if (Tail) { Tail->Next = new1; Tail = new1; } else { Head = new1; Tail = new1; } SignalEvent.Set(); QueueAccessMutex.Unlock(); } }; #ifdef _WIN32 typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; #ifndef _X360 extern "C" { 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*); }; #endif 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 %p 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) { m_currentOwnerID = thisThreadID; if (m_bTrace) Msg("Thread %u now owns lock %p\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 %p\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 %p, 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 defined(POSIX) inline CThreadMutex::CThreadMutex() { pthread_mutexattr_init(&m_Attr); pthread_mutexattr_settype(&m_Attr, PTHREAD_MUTEX_RECURSIVE); 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 inline CThreadRWLock::CThreadRWLock() : m_CanRead(true), m_nWriters(0), m_nActiveReaders(0), m_nPendingReaders(0) { } inline void CThreadRWLock::LockForRead() { m_mutex.Lock(); if (m_nWriters) { WaitForRead(); } m_nActiveReaders++; m_mutex.Unlock(); } inline void CThreadRWLock::UnlockRead() { m_mutex.Lock(); m_nActiveReaders--; if (m_nActiveReaders == 0 && m_nWriters != 0) { m_CanWrite.Set(); } m_mutex.Unlock(); } inline bool CThreadSpinRWLock::AssignIf(const LockInfo_t& newValue, const LockInfo_t& comperand) { return ThreadInterlockedAssignIf64((int64*)&m_lockInfo, *((int64*)&newValue), *((int64*)&comperand)); } inline bool CThreadSpinRWLock::TryLockForWrite(const uint32 threadId) { if (m_lockInfo.m_nReaders > 0 || (m_lockInfo.m_writerId && m_lockInfo.m_writerId != threadId)) { return false; } static const LockInfo_t oldValue = { 0, 0 }; LockInfo_t newValue = { threadId, 0 }; const bool bSuccess = AssignIf(newValue, oldValue); #if defined(_X360) if (bSuccess) { } #endif return bSuccess; } inline bool CThreadSpinRWLock::TryLockForWrite() { m_nWriters++; if (!TryLockForWrite(ThreadGetCurrentId())) { m_nWriters--; return false; } return true; } inline bool CThreadSpinRWLock::TryLockForRead() { if (m_nWriters != 0) { return false; } LockInfo_t oldValue; LockInfo_t newValue; oldValue.m_nReaders = m_lockInfo.m_nReaders; oldValue.m_writerId = 0; newValue.m_nReaders = oldValue.m_nReaders + 1; newValue.m_writerId = 0; const bool bSuccess = AssignIf(newValue, oldValue); #if defined(_X360) if (bSuccess) { } #endif return bSuccess; } inline void CThreadSpinRWLock::LockForWrite() { const uint32 threadId = ThreadGetCurrentId(); m_nWriters++; if (!TryLockForWrite(threadId)) { ThreadPause(); SpinLockForWrite(threadId); } } template FORCEINLINE T ReadVolatileMemory(T const* pPtr) { volatile const T* pVolatilePtr = (volatile const T*)pPtr; return *pVolatilePtr; } #if defined( _WIN32 ) #pragma warning(pop) #endif #endif