/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : Public include file for the multi-threading API. #pragma once // Include basic multithread primitives. #include "MultiThread.h" #include "BitFiddling.h" #include ////////////////////////////////////////////////////////////////////////// // Lock types: // // CRYLOCK_FAST // A fast potentially (non-recursive) mutex. // CRYLOCK_RECURSIVE // A recursive mutex. ////////////////////////////////////////////////////////////////////////// enum CryLockType { CRYLOCK_FAST = 1, CRYLOCK_RECURSIVE = 2, }; #define CRYLOCK_HAVE_FASTLOCK 1 void CryThreadSetName(threadID nThreadId, const char* sThreadName); const char* CryThreadGetName(threadID nThreadId); ///////////////////////////////////////////////////////////////////////////// // // Primitive locks and conditions. // // Primitive locks are represented by instance of class CryLockT // // template class CryLockT { /* Unsupported lock type. */ }; ////////////////////////////////////////////////////////////////////////// // Typedefs. ////////////////////////////////////////////////////////////////////////// typedef CryLockT CryCriticalSection; typedef CryLockT CryCriticalSectionNonRecursive; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // CryAutoCriticalSection implements a helper class to automatically // lock critical section in constructor and release on destructor. // ////////////////////////////////////////////////////////////////////////// template class CryAutoLock { private: LockClass* m_pLock; CryAutoLock(); CryAutoLock(const CryAutoLock&); CryAutoLock& operator = (const CryAutoLock&); public: CryAutoLock(LockClass& Lock) : m_pLock(&Lock) { m_pLock->Lock(); } CryAutoLock(const LockClass& Lock) : m_pLock(const_cast(&Lock)) { m_pLock->Lock(); } ~CryAutoLock() { m_pLock->Unlock(); } }; ////////////////////////////////////////////////////////////////////////// // // CryOptionalAutoLock implements a helper class to automatically // lock critical section (if needed) in constructor and release on destructor. // ////////////////////////////////////////////////////////////////////////// template class CryOptionalAutoLock { private: LockClass* m_Lock; bool m_bLockAcquired; CryOptionalAutoLock(); CryOptionalAutoLock(const CryOptionalAutoLock&); CryOptionalAutoLock& operator = (const CryOptionalAutoLock&); public: CryOptionalAutoLock(LockClass& Lock, bool acquireLock) : m_Lock(&Lock) , m_bLockAcquired(false) { if (acquireLock) { Acquire(); } } ~CryOptionalAutoLock() { Release(); } void Release() { if (m_bLockAcquired) { m_Lock->Unlock(); m_bLockAcquired = false; } } void Acquire() { if (!m_bLockAcquired) { m_Lock->Lock(); m_bLockAcquired = true; } } }; ////////////////////////////////////////////////////////////////////////// // // CryAutoSet implements a helper class to automatically // set and reset value in constructor and release on destructor. // ////////////////////////////////////////////////////////////////////////// template class CryAutoSet { private: ValueClass* m_pValue; CryAutoSet(); CryAutoSet(const CryAutoSet&); CryAutoSet& operator = (const CryAutoSet&); public: CryAutoSet(ValueClass& value) : m_pValue(&value) { *m_pValue = (ValueClass)1; } ~CryAutoSet() { *m_pValue = (ValueClass)0; } }; ////////////////////////////////////////////////////////////////////////// // // Auto critical section is the most commonly used type of auto lock. // ////////////////////////////////////////////////////////////////////////// typedef CryAutoLock CryAutoCriticalSection; #define AUTO_LOCK_T(Type, lock) PREFAST_SUPPRESS_WARNING(6246); CryAutoLock __AutoLock(lock) #define AUTO_LOCK(lock) AUTO_LOCK_T(CryCriticalSection, lock) #define AUTO_LOCK_CS(csLock) CryAutoCriticalSection __AL__##csLock(csLock) ///////////////////////////////////////////////////////////////////////////// // // Threads. // Base class for runnable objects. // // A runnable is an object with a Run() and a Cancel() method. The Run() // method should perform the runnable's job. The Cancel() method may be // called by another thread requesting early termination of the Run() method. // The runnable may ignore the Cancel() call, the default implementation of // Cancel() does nothing. class CryRunnable { public: virtual ~CryRunnable() { } virtual void Run() = 0; virtual void Cancel() { } }; // Class holding information about a thread. // // A reference to the thread information can be obtained by calling GetInfo() // on the CrySimpleThread (or derived class) instance. // // NOTE: // If the code is compiled with NO_THREADINFO defined, then the GetInfo() // method will return a reference to a static dummy instance of this // structure. It is currently undecided if NO_THREADINFO will be defined for // release builds! struct CryThreadInfo { // The symbolic name of the thread. // // You may set this name directly or through the SetName() method of // CrySimpleThread (or derived class). AZStd::string m_Name; // A thread identification number. // The number is unique but architecture specific. Do not assume anything // about that number except for being unique. // // This field is filled when the thread is started (i.e. before the Run() // method or thread routine is called). It is advised that you do not // change this number manually. uint32 m_ID; }; // Simple thread class. // // CrySimpleThread is a simple wrapper around a system thread providing // nothing but system-level functionality of a thread. There are two typical // ways to use a simple thread: // // 1. Derive from the CrySimpleThread class and provide an implementation of // the Run() (and optionally Cancel()) methods. // 2. Specify a runnable object when the thread is started. The default // runnable type is CryRunnable. // // The Runnable class specfied as the template argument must provide Run() // and Cancel() methods compatible with the following signatures: // // void Runnable::Run(); // void Runnable::Cancel(); // // If the Runnable does not support cancellation, then the Cancel() method // should do nothing. // // The same instance of CrySimpleThread may be used for multiple thread // executions /in sequence/, i.e. it is valid to re-start the thread by // calling Start() after the thread has been joined by calling WaitForThread(). template class CrySimpleThread; // Standard thread class. // // The class provides a lock (mutex) and an associated condition variable. If // you don't need the lock, then you should used CrySimpleThread instead of // CryThread. template class CryThread; /////////////////////////////////////////////////////////////////////////////// // Include architecture specific code. #if AZ_LEGACY_CRYCOMMON_TRAIT_USE_PTHREADS #include #define AZ_RESTRICTED_SECTION_IMPLEMENTED #elif defined(WIN32) || defined(WIN64) #include #define AZ_RESTRICTED_SECTION_IMPLEMENTED #elif defined(AZ_RESTRICTED_PLATFORM) #if defined(AZ_PLATFORM_XENIA) #include "Xenia/CryThread_h_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/CryThread_h_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/CryThread_h_salem.inl" #endif #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else // Put other platform specific includes here! #include #endif #if !defined _CRYTHREAD_CONDLOCK_GLITCH typedef CryLockT CryMutex; #endif // !_CRYTHREAD_CONDLOCK_GLITCH // The the architecture specific code does not define a class CryRWLock, then // a default implementation is provided here. #if !defined _CRYTHREAD_HAVE_RWLOCK && !defined _CRYTHREAD_CONDLOCK_GLITCH class CryRWLock { CryCriticalSection m_lockExclusiveAccess; CryCriticalSection m_lockSharedAccessComplete; CryConditionVariable m_condSharedAccessComplete; int m_nSharedAccessCount; int m_nCompletedSharedAccessCount; bool m_bExclusiveAccess; CryRWLock(const CryRWLock&); CryRWLock& operator= (const CryRWLock&); void AdjustSharedAccessCount() { m_nSharedAccessCount -= m_nCompletedSharedAccessCount; m_nCompletedSharedAccessCount = 0; } public: CryRWLock() : m_nSharedAccessCount(0) , m_nCompletedSharedAccessCount(0) , m_bExclusiveAccess(false) { } void RLock() { m_lockExclusiveAccess.Lock(); if (++m_nSharedAccessCount == INT_MAX) { m_lockSharedAccessComplete.Lock(); AdjustSharedAccessCount(); m_lockSharedAccessComplete.Unlock(); } m_lockExclusiveAccess.Unlock(); } bool TryRLock() { if (!m_lockExclusiveAccess.TryLock()) { return false; } if (++m_nSharedAccessCount == INT_MAX) { m_lockSharedAccessComplete.Lock(); AdjustSharedAccessCount(); m_lockSharedAccessComplete.Unlock(); } m_lockExclusiveAccess.Unlock(); return true; } void RUnlock() { Unlock(); } void WLock() { m_lockExclusiveAccess.Lock(); m_lockSharedAccessComplete.Lock(); assert(!m_bExclusiveAccess); AdjustSharedAccessCount(); if (m_nSharedAccessCount > 0) { m_nCompletedSharedAccessCount -= m_nSharedAccessCount; do { m_condSharedAccessComplete.Wait(m_lockSharedAccessComplete); } while (m_nCompletedSharedAccessCount < 0); m_nSharedAccessCount = 0; } m_bExclusiveAccess = true; } bool TryWLock() { if (!m_lockExclusiveAccess.TryLock()) { return false; } if (!m_lockSharedAccessComplete.TryLock()) { m_lockExclusiveAccess.Unlock(); return false; } assert(!m_bExclusiveAccess); AdjustSharedAccessCount(); if (m_nSharedAccessCount > 0) { m_lockSharedAccessComplete.Unlock(); m_lockExclusiveAccess.Unlock(); return false; } else { m_bExclusiveAccess = true; } return true; } void WUnlock() { Unlock(); } void Unlock() { if (!m_bExclusiveAccess) { m_lockSharedAccessComplete.Lock(); if (++m_nCompletedSharedAccessCount == 0) { m_condSharedAccessComplete.NotifySingle(); } m_lockSharedAccessComplete.Unlock(); } else { m_bExclusiveAccess = false; m_lockSharedAccessComplete.Unlock(); m_lockExclusiveAccess.Unlock(); } } }; #endif // !defined _CRYTHREAD_HAVE_RWLOCK // Thread class. // // CryThread is an extension of CrySimpleThread providing a lock (mutex) and a // condition variable per instance. template class CryThread : public CrySimpleThread { CryMutex m_Lock; CryConditionVariable m_Cond; CryThread(const CryThread&); void operator = (const CryThread&); public: CryThread() { } void Lock() { m_Lock.Lock(); } bool TryLock() { return m_Lock.TryLock(); } void Unlock() { m_Lock.Unlock(); } void Wait() { m_Cond.Wait(m_Lock); } // Timed wait on the associated condition. // // The 'milliseconds' parameter specifies the relative timeout in // milliseconds. The method returns true if a notification was received and // false if the specified timeout expired without receiving a notification. // // UNIX note: the method will _not_ return if the calling thread receives a // signal. Instead the call is re-started with the _original_ timeout // value. This misfeature may be fixed in the future. bool TimedWait(uint32 milliseconds) { return m_Cond.TimedWait(m_Lock, milliseconds); } void Notify() { m_Cond.Notify(); } void NotifySingle() { m_Cond.NotifySingle(); } CryMutex& GetLock() { return m_Lock; } }; ////////////////////////////////////////////////////////////////////////// // // Sync primitive for multiple reads and exclusive locking change access // // Desc: // Useful in case if you have rarely modified object that needs // to be read quite often from different threads but still // need to be exclusively modified sometimes // Debug functionality: // Can be used for debug-only lock calls, which verify that no // simultaneous access is attempted. // Use the bDebug argument of LockRead or LockModify, // or use the DEBUG_READLOCK or DEBUG_MODIFYLOCK macros. // There is no overhead in release builds, if you use the macros, // and the lock definition is inside #ifdef _DEBUG. ////////////////////////////////////////////////////////////////////////// class CryReadModifyLock { public: CryReadModifyLock() : m_modifyCount(0) , m_readCount(0) { SetDebugLocked(false); } bool LockRead(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const { if (!WriteLock(bTry, bDebug, strDebug)) // wait until write unlocked { return false; } CryInterlockedIncrement(&m_readCount); // increment read counter m_writeLock.Unlock(); return true; } void UnlockRead() const { SetDebugLocked(false); const int counter = CryInterlockedDecrement(&m_readCount); // release read assert(counter >= 0); if (m_writeLock.TryLock()) { m_writeLock.Unlock(); } else if (counter == 0 && m_modifyCount) { m_ReadReleased.Set(); // signal the final read released } } bool LockModify(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const { if (!WriteLock(bTry, bDebug, strDebug)) { return false; } CryInterlockedIncrement(&m_modifyCount); // increment write counter (counter is for nested cases) while (m_readCount) { m_ReadReleased.Wait(); // wait for all threads finish read operation } return true; } void UnlockModify() const { SetDebugLocked(false); int counter = CryInterlockedDecrement(&m_modifyCount); // decrement write counter assert(counter >= 0); m_writeLock.Unlock(); // release exclusive lock } protected: mutable volatile int m_readCount; mutable volatile int m_modifyCount; mutable CryEvent m_ReadReleased; mutable CryCriticalSection m_writeLock; mutable bool m_debugLocked; mutable const char* m_debugLockStr; void SetDebugLocked(bool b, const char* str = 0) const { #ifdef _DEBUG m_debugLocked = b; m_debugLockStr = str; #endif } bool WriteLock(bool bTry, bool bDebug, const char* strDebug) const { if (!m_writeLock.TryLock()) { #ifdef _DEBUG assert(!m_debugLocked); assert(!bDebug); #endif if (bTry) { return false; } m_writeLock.Lock(); } #ifdef _DEBUG if (!m_readCount && !m_modifyCount) // not yet locked { SetDebugLocked(bDebug, strDebug); } #endif return true; } }; // Auto-locking classes. template class AutoLockRead { protected: const T& m_lock; public: AutoLockRead(const T& lock, cstr strDebug = 0) : m_lock(lock) { m_lock.LockRead(bDEBUG, strDebug, bDEBUG); } ~AutoLockRead() { m_lock.UnlockRead(); } }; template class AutoLockModify { protected: const T& m_lock; public: AutoLockModify(const T& lock, cstr strDebug = 0) : m_lock(lock) { m_lock.LockModify(bDEBUG, strDebug, bDEBUG); } ~AutoLockModify() { m_lock.UnlockModify(); } }; #define AUTO_READLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead AZ_JOIN(__readlock, __LINE__)(p, __FUNC__) #define AUTO_READLOCK_PROT(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead AZ_JOIN(__readlock_prot, __LINE__)(p, __FUNC__) #define AUTO_MODIFYLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockModify AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__) #if defined(_DEBUG) #define DEBUG_READLOCK(p) AutoLockRead AZ_JOIN(__readlock, __LINE__)(p, __FUNC__) #define DEBUG_MODIFYLOCK(p) AutoLockModify AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__) #else #define DEBUG_READLOCK(p) #define DEBUG_MODIFYLOCK(p) #endif // producer consumer queue implementations, but here instead of MultiThread_Container.h // since they requiere platform specific code, and including windows.h in a very common // header file leads to all kinds of problems namespace CryMT { ////////////////////////////////////////////////////////////////////////// // Producer/Consumer Queue for 1 to 1 thread communication // Realized with only volatile variables and memory barriers // *warning* this producer/consumer queue is only thread safe in a 1 to 1 situation // and doesn't provide any yields or similar to prevent spinning ////////////////////////////////////////////////////////////////////////// template class SingleProducerSingleConsumerQueue : public CryMT::detail::SingleProducerSingleConsumerQueueBase { public: SingleProducerSingleConsumerQueue(); ~SingleProducerSingleConsumerQueue(); void Init(size_t nSize); void Push(const T& rObj); void Pop(T* pResult); uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); } uint32 BufferSize() { return m_nBufferSize; } uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); } private: T* m_arrBuffer; uint32 m_nBufferSize; volatile uint32 m_nProducerIndex _ALIGN(16); volatile uint32 m_nComsumerIndex _ALIGN(16); } _ALIGN(128); /////////////////////////////////////////////////////////////////////////////// template inline SingleProducerSingleConsumerQueue::SingleProducerSingleConsumerQueue() : m_arrBuffer(NULL) , m_nBufferSize(0) , m_nProducerIndex(0) , m_nComsumerIndex(0) {} /////////////////////////////////////////////////////////////////////////////// template inline SingleProducerSingleConsumerQueue::~SingleProducerSingleConsumerQueue() { CryModuleMemalignFree(m_arrBuffer); m_nBufferSize = 0; } /////////////////////////////////////////////////////////////////////////////// template inline void SingleProducerSingleConsumerQueue::Init(size_t nSize) { assert(m_arrBuffer == NULL); assert(m_nBufferSize == 0); assert((nSize & (nSize - 1)) == 0); m_arrBuffer = alias_cast(CryModuleMemalign(nSize * sizeof(T), 16)); m_nBufferSize = nSize; } /////////////////////////////////////////////////////////////////////////////// template inline void SingleProducerSingleConsumerQueue::Push(const T& rObj) { assert(m_arrBuffer != NULL); assert(m_nBufferSize != 0); SingleProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T)); } /////////////////////////////////////////////////////////////////////////////// template inline void SingleProducerSingleConsumerQueue::Pop(T* pResult) { assert(m_arrBuffer != NULL); assert(m_nBufferSize != 0); SingleProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T)); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Producer/Consumer Queue for N to 1 thread communication // lockfree implemenation, to copy with multiple producers, // a internal producer refcount is managed, the queue is empty // as soon as there are no more producers and no new elements ////////////////////////////////////////////////////////////////////////// template class N_ProducerSingleConsumerQueue : public CryMT::detail::N_ProducerSingleConsumerQueueBase { public: N_ProducerSingleConsumerQueue(); ~N_ProducerSingleConsumerQueue(); void Init(size_t nSize); void Push(const T& rObj); bool Pop(T* pResult); // needs to be called before using, assumes that there is at least one producer // so the first one doesn't need to call AddProducer, but he has to deregister itself void SetRunningState(); // to correctly track when the queue is empty(and no new jobs will be added), refcount the producer void AddProducer(); void RemoveProducer(); uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); } uint32 BufferSize() { return m_nBufferSize; } uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); } private: T* m_arrBuffer; volatile uint32* m_arrStates; uint32 m_nBufferSize; volatile uint32 m_nProducerIndex; volatile uint32 m_nComsumerIndex; volatile uint32 m_nRunning; volatile uint32 m_nProducerCount; } _ALIGN(128); /////////////////////////////////////////////////////////////////////////////// template inline N_ProducerSingleConsumerQueue::N_ProducerSingleConsumerQueue() : m_arrBuffer(NULL) , m_arrStates(NULL) , m_nBufferSize(0) , m_nProducerIndex(0) , m_nComsumerIndex(0) , m_nRunning(0) , m_nProducerCount(0) {} /////////////////////////////////////////////////////////////////////////////// template inline N_ProducerSingleConsumerQueue::~N_ProducerSingleConsumerQueue() { CryModuleMemalignFree(m_arrBuffer); CryModuleMemalignFree((void*)m_arrStates); m_nBufferSize = 0; } /////////////////////////////////////////////////////////////////////////////// template inline void N_ProducerSingleConsumerQueue::Init(size_t nSize) { assert(m_arrBuffer == NULL); assert(m_arrStates == NULL); assert(m_nBufferSize == 0); assert((nSize & (nSize - 1)) == 0); m_arrBuffer = alias_cast(CryModuleMemalign(nSize * sizeof(T), 16)); m_arrStates = alias_cast(CryModuleMemalign(nSize * sizeof(uint32), 16)); memset((void*)m_arrStates, 0, sizeof(uint32) * nSize); m_nBufferSize = nSize; } /////////////////////////////////////////////////////////////////////////////// template inline void N_ProducerSingleConsumerQueue::SetRunningState() { #if !defined(_RELEASE) if (m_nRunning == 1) { __debugbreak(); } #endif m_nRunning = 1; m_nProducerCount = 1; } /////////////////////////////////////////////////////////////////////////////// template inline void N_ProducerSingleConsumerQueue::AddProducer() { assert(m_arrBuffer != NULL); assert(m_arrStates != NULL); assert(m_nBufferSize != 0); #if !defined(_RELEASE) if (m_nRunning == 0) { __debugbreak(); } #endif CryInterlockedIncrement((volatile int*)&m_nProducerCount); } /////////////////////////////////////////////////////////////////////////////// template inline void N_ProducerSingleConsumerQueue::RemoveProducer() { assert(m_arrBuffer != NULL); assert(m_arrStates != NULL); assert(m_nBufferSize != 0); #if !defined(_RELEASE) if (m_nRunning == 0) { __debugbreak(); } #endif if (CryInterlockedDecrement((volatile int*)&m_nProducerCount) == 0) { m_nRunning = 0; } } /////////////////////////////////////////////////////////////////////////////// template inline void N_ProducerSingleConsumerQueue::Push(const T& rObj) { assert(m_arrBuffer != NULL); assert(m_arrStates != NULL); assert(m_nBufferSize != 0); CryMT::detail::N_ProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates); } /////////////////////////////////////////////////////////////////////////////// template inline bool N_ProducerSingleConsumerQueue::Pop(T* pResult) { assert(m_arrBuffer != NULL); assert(m_arrStates != NULL); assert(m_nBufferSize != 0); return CryMT::detail::N_ProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates); } } //namespace CryMT // Include all multithreading containers. #include "MultiThread_Containers.h"