/** * @file XTPSynchro.h * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ /** @cond */ #if !defined(__XTPSYNCHRO_H__) # define __XTPSYNCHRO_H__ /** @endcond */ # if _MSC_VER > 1000 # pragma once # endif // _MSC_VER > 1000 # include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" /** @cond */ # ifdef _DEBUG # define XTP_ASYNC_ASSERT(expr) \ if (!(expr)) \ DebugBreak(); # else # define XTP_ASYNC_ASSERT(expr) # endif # define XTP_SYNC_MAX_EXPONENTIAL_TIMEOUT 0x400 namespace XTPDetails { struct XTPSynchroPrivateUseTag { }; } // namespace XTPDetails /** @endcond */ /** * @brief * Implements no-named simple and lightweight synchronization event primitive. */ class _XTP_EXT_CLASS CXTPSimpleEvent { long m_bState; /**< Current event state (1 - signaled, 0 - non-signaled) */ public: /** * @brief * Constructs an event object. * @param bInitialState TRUE to construct an event in signaled state. FALSE * to construct an event in non-signaled state. */ explicit CXTPSimpleEvent(BOOL bInitialState); /** * @brief * Resets an event to non-signaled state. */ void Reset(); /** * @brief * Sets an event to signaled state. */ void Set(); /** * @brief * Determines if an event is in signaled state. * * @return TRUE if an event is in signaled state, FALSE otherwise. */ BOOL IsSignaled() const; /** * @brief * Waits infinitely for an event to enter signaled states. */ void Wait(); /** @cond */ private: /** * noncopyable */ CXTPSimpleEvent(const CXTPSimpleEvent&); CXTPSimpleEvent& operator=(const CXTPSimpleEvent&); /** @endcond */ }; /** * @brief * Implements simple and lightweight synchronization critical section primitive. * Multi-core and multi-CPU safe. */ class _XTP_EXT_CLASS CXTPSimpleCriticalSection { long m_nLock; /**< Not 0 if the critical section is locked */ long m_nRecursionCounter; /**< Recursive lock counter */ DWORD m_dwThreadId; /**< The owner thread's ID */ public: /** * @brief * Construct critical section. */ CXTPSimpleCriticalSection() : m_nLock(0) , m_nRecursionCounter(0) , m_dwThreadId(0) { } /** * @brief * Handles critical section destruction. */ ~CXTPSimpleCriticalSection(); /** * @brief * Enters critical section. If another thread has already entered the * same critical section, the calling thread will be suspended until * the critical section is fully released by another thread. * Enterring a critical section more then one time on the same thread * only increases recusion counter. */ void Enter(); /** * @brief * Releases the critical section. In case of re-entry on the same thread * the internal recusion counter is decreased without releasing the * critical section. */ void Leave(); /** * @brief * A helper class for locking a scope or providing exception safe locking. */ class CLock { CXTPSimpleCriticalSection& m_cs; public: /** * @brief * Locks a critical section passed. * @param cs A reference of critical section to lock. */ explicit CLock(CXTPSimpleCriticalSection& cs) : m_cs(cs) { m_cs.Enter(); } /** * @brief * Unlocks the previously locked critical. */ ~CLock() { m_cs.Leave(); } }; /** @cond */ private: // noncopyable CXTPSimpleCriticalSection(const CXTPSimpleCriticalSection&); CXTPSimpleCriticalSection& operator=(const CXTPSimpleCriticalSection&); /** @endcond */ }; /** * @brief * Implements recursive read-write lock synchronization pattern. */ class _XTP_EXT_CLASS CXTPRWCriticalSection { public: /** * @brief * Constructs read-write lock critical section. */ CXTPRWCriticalSection(); /** * @brief * Handles read-write lock critical section. destruction. */ ~CXTPRWCriticalSection(); public: class CSharedLock; class CExclusiveLock; /** * @brief * Determines if critical section is currently locked in shared mode. * @return * TRUE if critical section is locked in shared mode, otherwise FALSE. * FALSE is also returned if critical section has been previously locked * exclusively in the same thread, regardless whether shared mode lock has * been obtained later or not. */ BOOL IsLockedSharedly() const volatile; /** * @brief * Determines if critical section is currently locked exclusively. * @return * TRUE if critical section is locked in exclusive mode, otherwise FALSE. */ BOOL IsLockedExclusively() const volatile; /** * @brief * Enters shared lock mode. If the critical section is currently locked * exclusively in another thread, the call will be blocked until exclusive * lock is released. If the critical section is currently locked * exclusively in the current thread the function returns immediately without * acquiring shared lock. */ void LockShared(); /** * @brief * Leaves shared lock mode. */ void UnlockShared(); /** * @brief * Enters exclusive lock mode. If the critical section is currently locked * sharedly in another thread, the call will be blocked until all shared * lock are released. If the critical section is currently locked * sharedly in the current thread this state is cached and then exclusive * lock gets acquired. Once exclusive lock is released, the cached shared lock * state for the current thread is restored. */ void LockExclusive(); /** * @brief * Leaves exclusive lock mode. */ void UnlockExclusive(); /** @cond */ private: # ifdef _DEBUG class Diagnostic; friend class Diagnostic; static Diagnostic m_Diagnostic; # endif void PushLocalSharedLockState(); void PopSharedLockState(); void LockShared(BOOL bPopMode); void UnlockShared(BOOL bPushMode); long IncrementLocalSharedLockCounter(); long DecrementLocalSharedLockCounter(); long GetLocalSharedLockCounter() const; private: long m_nSharedLockCounter; /**< Number of shared locks obtained. */ long m_nExclusiveLockCounter; /**< Number of exclusive locks obtained. */ DWORD m_dwExclusiveLockTid; /**< Thread ID from where exclusive lock has been obtained. */ CXTPSimpleCriticalSection m_csGeneral; /**< Internal data access guard. */ CXTPSimpleEvent m_sharedLockEvent; /**< Shared lock event */ static const DWORD m_nLocalSharedLockCounterTlsIdx; typedef CMap LocalSharedLockCounterMap; /** @endcond */ }; /** * @brief * A convenience class for obtaining shared lock for a scope. */ class CXTPRWCriticalSection::CSharedLock { public: /** * @brief * Constructs shared lock instance, obtains shared lock on critical section provided. * @param cs Critical section for which to obtain shared lock. */ explicit CSharedLock(CXTPRWCriticalSection& cs) : m_pCS(&cs) { m_pCS->LockShared(); } /** * @brief * Destructs shared lock instance, releases shared lock on critical section * provided during construction. */ ~CSharedLock() { if (NULL != m_pCS) { m_pCS->UnlockShared(); } } /** @cond */ public: CSharedLock(CXTPRWCriticalSection* pCS, XTPDetails::XTPSynchroPrivateUseTag) : m_pCS(pCS) { if (NULL != m_pCS) { m_pCS->LockShared(); } } private: CSharedLock(const CSharedLock&); /**< deleted */ private: CXTPRWCriticalSection* m_pCS; /** @endcond */ }; /** * @brief * A convenience class for obtaining exclusive lock for a scope. */ class CXTPRWCriticalSection::CExclusiveLock { public: /** * @brief * Constructs exclusive lock instance, obtains exclusive lock on critical * section provided. * @param cs Critical section for which to obtain exclusive lock. */ explicit CExclusiveLock(CXTPRWCriticalSection& cs) : m_pCS(&cs) { m_pCS->LockExclusive(); } /** * @brief * Destructs exclusive lock instance, releases exclusive lock on critical * section provided during construction. */ ~CExclusiveLock() { if (NULL != m_pCS) { m_pCS->UnlockExclusive(); } } /** @cond */ public: CExclusiveLock(CXTPRWCriticalSection* pCS, XTPDetails::XTPSynchroPrivateUseTag) : m_pCS(pCS) { if (NULL != m_pCS) { m_pCS->LockExclusive(); } } private: CExclusiveLock(const CExclusiveLock&); // deleted private: CXTPRWCriticalSection* m_pCS; /** @endcond */ }; /** * @brief * Obtains shared lock for the current scope. * @param cs Critical section for which to obtain shared lock. */ # define XTP_RWCS_LOCK_SHARED_SCOPE(cs) \ CXTPRWCriticalSection::CSharedLock $__xtpRWCSSharedLock(cs) /** * @brief * Obtains exclusive lock for the current scope. * @param cs Critical section for which to obtain exclusive lock. */ # define XTP_RWCS_LOCK_EXCLUSIVE_SCOPE(cs) \ CXTPRWCriticalSection::CExclusiveLock $__xtpRWCSExclusiveLock(cs) /** * @brief * A convenience class for guarding an adapted object reference by * a RW-lock provided. * @param Adaptee Type of an adapted object reference. * @param RWLock RW-lock type. Can be either CXTPRWCriticalSection::CSharedLock * or CXTPRWCriticalSection::CExclusiveLock. */ template class CXTPAsyncGuard : public CXTPBlockStatementBase { public: /** * @brief * Constructs the guard object. * @param adaptee A reference to an object to be guarded. * @param cs Critical section reference for while a lock is to be obtained. */ CXTPAsyncGuard(Adaptee& adaptee, CXTPRWCriticalSection& cs) : m_locker(cs) , m_adaptee(adaptee) , m_pConditionCounter(NULL) { } /** * @brief * Constructs the guard object. * @param adaptee A reference to an object to be guarded. * @param cs Critical section reference for while a lock is to be obtained. * @param nConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ CXTPAsyncGuard(Adaptee& adaptee, CXTPRWCriticalSection& cs, long& nConditionCounter) : m_locker(1 == InterlockedIncrement(&nConditionCounter) ? &cs : NULL, XTPDetails::XTPSynchroPrivateUseTag()) , m_adaptee(adaptee) , m_pConditionCounter(&nConditionCounter) { _ASSERTE(0 < nConditionCounter); } /** * @brief * Copies the guard object. * @param rhs A reference to a source guard object. */ CXTPAsyncGuard(const CXTPAsyncGuard& rhs) : m_locker(rhs.m_locker.m_pCS) , m_adaptee(rhs.m_adaptee) , m_pConditionCounter(rhs.m_pConditionCounter) { if (NULL != m_pConditionCounter) { InterlockedIncrement(m_pConditionCounter); } } /** * @brief * Handles guard destruction. Unlocks owned critical section. If there was an * option condition counter provided, unlocks critical section only if decrmented * counter value is zero. */ ~CXTPAsyncGuard() { if (NULL != m_pConditionCounter) { long nConditionCounter = InterlockedDecrement(m_pConditionCounter); _ASSERTE(0 <= nConditionCounter); UNREFERENCED_PARAMETER(nConditionCounter); } } /** * @brief * Accesses an adapted object's pointer. * @return * An adapted object's pointer. */ Adaptee* operator->() { return &m_adaptee; } /** * @brief * Accesses an adapted object's pointer. * @return * An adapted object's pointer. */ const Adaptee* operator->() const { return &m_adaptee; } /** * @brief * Accesses an adapted object's reference. * @return * An adapted object's reference. */ Adaptee& operator*() { return m_adaptee; } /** * @brief * Accesses an adapted object's reference. * @return * An adapted object's reference. */ const Adaptee& operator*() const { return m_adaptee; } /** * @brief * Casts object to an adapted object's pointer. * @return * An adapted object's pointer. */ operator Adaptee*() { return &m_adaptee; } /** * @brief * Casts object to an adapted object's pointer. * @return * An adapted object's pointer. */ operator const Adaptee*() const { return &m_adaptee; } /** @cond */ private: RWLock m_locker; Adaptee& m_adaptee; long* m_pConditionCounter; /** @endcond */ }; /** @cond */ namespace XTPDetails { template struct XTPAsyncGuardShortcut { typedef CXTPAsyncGuard XTPAsyncSharedGuard; typedef CXTPAsyncGuard XTPAsyncExclusiveGuard; typedef CXTPAsyncGuard XTPAsyncConstSharedGuard; typedef CXTPAsyncGuard XTPAsyncConstExclusiveGuard; }; class CXTPConditionBlock : public CXTPBlockStatementBase { long& m_nConditionCounter; public: CXTPConditionBlock(long& nConditionCounter) : m_nConditionCounter(nConditionCounter) { _ASSERTE(0 <= m_nConditionCounter); InterlockedIncrement(&m_nConditionCounter); } ~CXTPConditionBlock() { long nConditionCounter = InterlockedDecrement(&m_nConditionCounter); _ASSERTE(0 <= nConditionCounter); UNREFERENCED_PARAMETER(nConditionCounter); } }; } // namespace XTPDetails /** @endcond */ /** * @brief * A base class for a synchronized object. */ class CXTPSynchronized { public: /** * @brief * Accesses a synchronized object's critical section. * @return * Synchronized object's critical section. */ CXTPRWCriticalSection& GetAccessCriticalSection() const { return m_cs; } /** @cond */ protected: CXTPSynchronized() { } private: mutable CXTPRWCriticalSection m_cs; /** @endcond */ }; /** * @brief * A convenience function for building a shared-lock guard * for an object specified. * @param pObject A pointer to a synchronized object to be guarded. * @return * A shared-lock guard for an object provided. */ template AFX_INLINE CXTPAsyncGuard XTPAccessShared(T* pObject) { _ASSERTE(NULL != pObject); return CXTPAsyncGuard( *pObject, pObject->CXTPSynchronized::GetAccessCriticalSection()); } /** * @brief * A convenience function for building a shared-lock guard * for an object specified. * @param pObject A pointer to a synchronized object to be guarded. * @param nConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. * @return * A shared-lock guard for an object provided. */ template AFX_INLINE CXTPAsyncGuard XTPAccessShared(T* pObject, long& nConditionCounter) { _ASSERTE(NULL != pObject); return CXTPAsyncGuard( *pObject, pObject->CXTPSynchronized::GetAccessCriticalSection(), nConditionCounter); } /** * @brief * A convenience function for building an exclusive-lock guard * for an object specified. * @param pObject A pointer to a synchronized object to be guarded. * @return * An exclusive-lock guard for an object provided. */ template AFX_INLINE CXTPAsyncGuard XTPAccessExclusive(T* pObject) { _ASSERTE(NULL != pObject); return CXTPAsyncGuard( *pObject, pObject->CXTPSynchronized::GetAccessCriticalSection()); } /** * @brief * A convenience function for building an exclusive-lock guard * for an object specified. * @param pObject A pointer to a synchronized object to be guarded. * @param nConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. * @return * An exclusive-lock guard for an object provided. */ template AFX_INLINE CXTPAsyncGuard XTPAccessExclusive(T* pObject, long& nConditionCounter) { _ASSERTE(NULL != pObject); return CXTPAsyncGuard( *pObject, pObject->CXTPSynchronized::GetAccessCriticalSection(), nConditionCounter); } /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name */ # define XTP_GUARD_SHARED_(Type, Object, Guard) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncSharedGuard, Guard, \ XTPAccessShared(Object)) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_SHARED_COND_(Type, Object, Guard, ConditionCounter) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncSharedGuard, Guard, \ XTPAccessShared(Object, ConditionCounter)) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer */ # define XTP_GUARD_SHARED(Type, Object) XTP_GUARD_SHARED_(Type, Object, $__xtpGuard) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_SHARED_COND(Type, Object, ConditionCounter) \ XTP_GUARD_SHARED_COND_(Type, Object, $__xtpGuard, ConditionCounter) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name */ # define XTP_GUARD_SHARED_CONST_(Type, Object, Guard) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncConstSharedGuard, \ Guard, XTPAccessShared(Object)) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_SHARED_COND_CONST_(Type, Object, Guard, ConditionCounter) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncConstSharedGuard, \ Guard, XTPAccessShared(Object, ConditionCounter)) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer */ # define XTP_GUARD_SHARED_CONST(Type, Object) XTP_GUARD_SHARED_CONST_(Type, Object, $__xtpGuard) /** * @brief * Creates a shared-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_SHARED_COND_CONST(Type, Object, ConditionCounter) \ XTP_GUARD_SHARED_COND_CONST_(Type, Object, $__xtpGuard, ConditionCounter) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name */ # define XTP_GUARD_EXCLUSIVE_(Type, Object, Guard) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncExclusiveGuard, \ Guard, XTPAccessExclusive(Object)) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_EXCLUSIVE_COND_(Type, Object, Guard, ConditionCounter) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncExclusiveGuard, \ Guard, XTPAccessExclusive(Object, ConditionCounter)) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer */ # define XTP_GUARD_EXCLUSIVE(Type, Object) XTP_GUARD_EXCLUSIVE_(Type, Object, $__xtpGuard) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_EXCLUSIVE_COND(Type, Object, ConditionCounter) \ XTP_GUARD_EXCLUSIVE_COND_(Type, Object, $__xtpGuard, ConditionCounter) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name */ # define XTP_GUARD_EXCLUSIVE_CONST_(Type, Object, Guard) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncConstExclusiveGuard, \ Guard, XTPAccessExclusive(Object)) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param Guard Guard variable name * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_EXCLUSIVE_CONST_COND_(Type, Object, Guard, ConditionCounter) \ XTP_BLOCKSTATEMENT(::XTPDetails::XTPAsyncGuardShortcut::XTPAsyncConstExclusiveGuard, \ Guard, XTPAccessExclusive(Object, ConditionCounter)) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer */ # define XTP_GUARD_EXCLUSIVE_CONST(Type, Object) \ XTP_GUARD_EXCLUSIVE_CONST_(Type, Object, $__xtpGuard) /** * @brief * Creates an exclusive-lock guarded scope for an object specified. * @param Type Object type * @param Object Synchronized object pointer * @param ConditionCounter If zero, the guard will lock the critical section and * increment the counter until the critical section is unlocked. * Otherwise critical section does not get locked. */ # define XTP_GUARD_EXCLUSIVE_COND_CONST(Type, Object, ConditionCounter) \ XTP_GUARD_EXCLUSIVE_COND_CONST_(Type, Object, $__xtpGuard, ConditionCounter) # define XTP_ENTER_GUARD_CONDITION(ConditionCounter) \ XTP_BLOCKSTATEMENT(::XTPDetails::CXTPConditionBlock, $__xtpConditionBlock, ConditionCounter) # include "Common/Base/Diagnostic/XTPEnableNoisyWarnings.h" /** @cond */ #endif // !defined(__XTPSYNCHRO_H__) /** @endcond */