diff options
Diffstat (limited to 'src/vm/crst.h')
-rw-r--r-- | src/vm/crst.h | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/src/vm/crst.h b/src/vm/crst.h new file mode 100644 index 0000000000..7b470982d5 --- /dev/null +++ b/src/vm/crst.h @@ -0,0 +1,546 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// CRST.H +// + +// +// Debug-instrumented hierarchical critical sections. +// +// +// The hierarchy: +// -------------- +// The EE divides critical sections into numbered groups or "levels." +// Crsts that guard the lowest level data structures that don't +// use other services are grouped into the lowest-numbered levels. +// The higher-numbered levels are reserved for high-level crsts +// that guard broad swatches of code. Multiple groups can share the +// same number to indicate that they're disjoint (their locks will never +// nest.) +// +// The fundamental rule of the hierarchy that threads can only request +// a crst whose level is lower than any crst currently held by the thread. +// E.g. if a thread current holds a level-3 crst, he can try to enter +// a level-2 crst, but not a level-4 crst, nor a different level-3 +// crst. This prevents the cyclic dependencies that lead to deadlock. +// +// For debugging purposes Crsts are all also grouped by a type (e.g. +// CrstRemoting, the type of Crst used to synchronize certain remoting +// operations). Each type maps to one level (though a level may map to +// multiple types). The idea here is for the programmer to express Crst types +// and their dependencies (e.g. a CrstClassInit instance may be acquired +// while a CrstRemoting instance is already held) in a high level manner +// while an external script handles the mechanical process of assigning +// numerical levels to each type. See file:..\inc\CrstTypes.def for these high level +// type definitions. +// +// +// To create a crst: +// +// Crst *pcrst = new Crst(type); +// +// where "type" is one of the enums created in the auto-generated +// file:..\inc\CrstTypes.h header file (matching the definition in +// file:..\inc\CrstTypes.def). +// +// By default, crsts don't support nested enters by the same thread. If +// you need reentrancy, use the alternate form: +// +// Crst *pcrst = new Crst(type, TRUE); +// +// Since reentrancies never block the caller, they're allowed to +// "violate" the level ordering rule. +// +// +// To enter/leave a crst: +// ---------------------- +// +// +// pcrst->Enter(); +// pcrst->Leave(); +// +// An assertion will fire on Enter() if a thread attempts to take locks +// in the wrong order. +// +// Finally, a few DEBUG-only methods: +// +// To assert taking a crst won't violate level order: +// -------------------------------------------------- +// +// _ASSERTE(pcrst->IsSafeToTake()); +// +// This is a good line to put at the start of any function that +// enters a crst in some circumstances but not others. If it +// always enters the crst, it's not necessary to call IsSafeToTake() +// since Enter() does this for you. +// +// To assert that the current thread owns a crst: +// -------------------------------------------------- +// +// _ASSERTE(pcrst->OwnedByCurrentThread()); + + + +#ifndef __crst_h__ +#define __crst_h__ + +#include "util.hpp" +#include "debugmacros.h" +#include "log.h" + +#define ShutDown_Start 0x00000001 +#define ShutDown_Finalize1 0x00000002 +#define ShutDown_Finalize2 0x00000004 +#define ShutDown_Profiler 0x00000008 +#define ShutDown_COM 0x00000010 +#define ShutDown_SyncBlock 0x00000020 +#define ShutDown_IUnknown 0x00000040 +#define ShutDown_Phase2 0x00000080 + +#ifndef DACCESS_COMPILE +extern bool g_fProcessDetach; +extern DWORD g_fEEShutDown; +#endif +// Total count of Crst lock of the type (Shutdown) that are currently in use +extern Volatile<LONG> g_ShutdownCrstUsageCount; +extern Volatile<LONG> g_fForbidEnterEE; +extern bool g_fFinalizerRunOnShutDown; + +// The CRST. +class CrstBase +{ +// The following classes and methods violate the requirement that Crst usage be +// exception-safe, or they satisfy that requirement using techniques other than +// Holder objects: +friend class Thread; +friend class ThreadStore; +friend class ThreadSuspend; +friend class ListLock; +friend class ListLockEntry; +//friend class CExecutionEngine; +friend struct SavedExceptionInfo; +friend void EEEnterCriticalSection(CRITSEC_COOKIE cookie); +friend void EELeaveCriticalSection(CRITSEC_COOKIE cookie); +friend class ReJitPublishMethodHolder; +friend class ReJitPublishMethodTableHolder; + +friend class Debugger; +friend class Crst; + +#ifdef FEATURE_DBGIPC_TRANSPORT_VM + // The debugger transport code uses a holder for its Crst, but it needs to share the holder implementation + // with its right side code as well (which can't see the Crst implementation and actually uses a + // CRITICAL_SECTION as the base lock). So make DbgTransportSession a friend here so we can use Enter() and + // Leave() in order to build a shared holder class. + friend class DbgTransportLock; +#endif // FEATURE_DBGIPC_TRANSPORT_VM + + // PendingTypeLoadEntry acquires the lock during construction before anybody has a chance to see it to avoid + // level violations. + friend class PendingTypeLoadEntry; + +public: +#ifdef _DEBUG + enum NoLevelCheckFlag + { + CRST_NO_LEVEL_CHECK = 1, + CRST_LEVEL_CHECK = 0, + }; +#endif + +private: + // Some Crsts have a "shutdown" mode. + // A Crst in shutdown mode can only be taken / released by special + // (the helper / finalizer / shutdown) threads. Any other thread that tries to take + // the a "shutdown" crst will immediately release the Crst and instead just block forever. + // + // This prevents random threads from blocking the special threads from doing finalization on shutdown. + // + // Unfortunately, each Crst needs its own "shutdown" flag because we can't convert all the locks + // into shutdown locks at once. For eg, the TSL needs to suspend the runtime before + // converting to a shutdown lock. But it can't suspend the runtime while holding + // a UNSAFE_ANYMODE lock (such as the debugger-lock). So at least the debugger-lock + // and TSL need to be set separately. + // + // So for such Crsts, it's the caller's responsibility to detect if the crst is in + // shutdown mode, and if so, call this function after enter. + void ReleaseAndBlockForShutdownIfNotSpecialThread(); + + // Enter & Leave are deliberately private to force callers to use the + // Holder class. If you bypass the Holder class and access these members + // directly, your lock is not exception-safe. + // + // noLevelCheckFlag parameter lets you disable the crst level checking. This is + // very dangerous so it is only used when the constructor is the one performing + // the Enter (that attempt cannot possibly block since the current thread is + // the only one with a pointer to the crst.) + // + // For obvious reasons, this parameter must never be made public. + void Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag = CRST_LEVEL_CHECK)); + void Leave(); + + void SpinEnter(); + +#ifndef DACCESS_COMPILE + DEBUG_NOINLINE static void AcquireLock(CrstBase *c) PUB { + WRAPPER_NO_CONTRACT; + ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; + c->Enter(); + } + + DEBUG_NOINLINE static void ReleaseLock(CrstBase *c) PUB { + WRAPPER_NO_CONTRACT; + ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; + c->Leave(); + } + +#else // DACCESS_COMPILE + + // in DAC builds, we don't actually acquire the lock, we just determine whether the LS + // already holds it. If so, we assume the data is inconsistent and throw an exception. + // Argument: + // input: c - the lock to be checked. + // Note: Throws + static void AcquireLock(CrstBase * c) PUB + { + SUPPORTS_DAC; + if (c->GetEnterCount() != 0) + { + ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED); + } + }; + + static void ReleaseLock(CrstBase *c) PUB + { + SUPPORTS_DAC; + }; +#endif // DACCESS_COMPILE + +public: + //----------------------------------------------------------------- + // Clean up critical section + // Safe to call multiple times or on non-initialized critical section + //----------------------------------------------------------------- + void Destroy(); + +#ifdef _DEBUG + //----------------------------------------------------------------- + // Check if attempting to take the lock would violate level order. + //----------------------------------------------------------------- + BOOL IsSafeToTake(); + // Checks that the lock can be taken + BOOL Debug_CanTake() + { + WRAPPER_NO_CONTRACT; + // Actually take the lock and release it immediatelly, that will do all the necessary checks + Enter(); + Leave(); + return TRUE; + } + void SetCantLeave(BOOL bSet) + { + LIMITED_METHOD_CONTRACT; + if (bSet) + FastInterlockIncrement(&m_cannotLeave); + else + { + _ASSERTE(m_cannotLeave); + FastInterlockDecrement(&m_cannotLeave); + } + }; + //----------------------------------------------------------------- + // Is the current thread the owner? + //----------------------------------------------------------------- + BOOL OwnedByCurrentThread() + { + WRAPPER_NO_CONTRACT; +#ifdef CROSSGEN_COMPILE + return TRUE; +#else + return m_holderthreadid.IsCurrentThread(); +#endif + } + + CrstBase *GetThreadsOwnedCrsts(); + void SetThreadsOwnedCrsts(CrstBase *pCrst); + + __declspec(noinline) EEThreadId GetHolderThreadId() + { + LIMITED_METHOD_CONTRACT; + return m_holderthreadid; + } + +#endif //_DEBUG + + //----------------------------------------------------------------- + // For clients who want to assert whether they are in or out of the + // region. + //----------------------------------------------------------------- + UINT GetEnterCount() + { + LIMITED_METHOD_DAC_CONTRACT; +#ifdef _DEBUG + return m_entercount; +#else + return 0; +#endif //_DEBUG + } + +protected: + + VOID InitWorker(INDEBUG_COMMA(CrstType crstType) CrstFlags flags); + +#ifdef _DEBUG + void DebugInit(CrstType crstType, CrstFlags flags); + void DebugDestroy(); +#endif + + union { + CRITICAL_SECTION m_criticalsection; +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + IHostCrst *m_pHostCrst; +#endif // FEATURE_INCLUDE_ALL_INTERFACES + }; + + typedef enum + { + // Mask to indicate reserved flags + CRST_RESERVED_FLAGS_MASK = 0xC0000000, + // private flag to indicate initialized Crsts + CRST_INITIALIZED = 0x80000000, + // private flag to indicate Crst is OS Critical Section + CRST_OS_CRIT_SEC = 0x40000000, + // rest of the flags are CrstFlags + } CrstReservedFlags; + DWORD m_dwFlags; // Re-entrancy and same level +#ifdef _DEBUG + UINT m_entercount; // # of unmatched Enters. + CrstType m_crstType; // Type enum (should have a descriptive name for debugging) + const char *m_tag; // Stringized form of the tag for easy debugging + int m_crstlevel; // what level is the crst in? + EEThreadId m_holderthreadid; // current holder (or NULL) + CrstBase *m_next; // link for global linked list + CrstBase *m_prev; // link for global linked list + Volatile<LONG> m_cannotLeave; + + // Check for dead lock situation. + ULONG m_countNoTriggerGC; + + void PostEnter (); + void PreEnter (); + void PreLeave (); +#endif //_DEBUG + +private: + + void SetOSCritSec () + { + m_dwFlags |= CRST_OS_CRIT_SEC; + } + void ResetOSCritSec () + { + m_dwFlags &= ~CRST_OS_CRIT_SEC; + } + BOOL IsOSCritSec () + { + return m_dwFlags & CRST_OS_CRIT_SEC; + } + void SetCrstInitialized() + { + m_dwFlags |= CRST_INITIALIZED; + } + + BOOL IsCrstInitialized() + { + return m_dwFlags & CRST_INITIALIZED; + } + + BOOL CanBeTakenDuringShutdown() + { + return m_dwFlags & CRST_TAKEN_DURING_SHUTDOWN; + } + + void SetFlags(CrstFlags f) + { + WRAPPER_NO_CONTRACT; + _ASSERTE(((CrstFlags)(f & ~CRST_RESERVED_FLAGS_MASK)) == f); + m_dwFlags = (f & ~CRST_RESERVED_FLAGS_MASK) | (m_dwFlags & CRST_RESERVED_FLAGS_MASK); + } + + void ResetFlags() // resets the reserved and the CrstFlags + { + m_dwFlags = 0; + } + // ------------------------------- Holders ------------------------------ + public: + // + // CrstHolder is optimized for the common use that takes the lock in constructor + // and releases it in destructor. Users that require all Holder features + // can use CrstHolderWithState. + // + class CrstHolder + { + CrstBase * m_pCrst; + + public: + inline CrstHolder(CrstBase * pCrst) + : m_pCrst(pCrst) + { + WRAPPER_NO_CONTRACT; + AcquireLock(pCrst); + } + + inline ~CrstHolder() + { + WRAPPER_NO_CONTRACT; + + VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(HSV_ValidateMinimumStackReq); + ReleaseLock(m_pCrst); + } + }; + + // Note that the holders for CRSTs are used in extremely low stack conditions. Because of this, they + // aren't allowed to use more than HOLDER_CODE_MINIMUM_STACK_LIMIT pages of stack. + typedef DacHolder<CrstBase *, CrstBase::AcquireLock, CrstBase::ReleaseLock, 0, CompareDefault, HSV_ValidateMinimumStackReq> CrstHolderWithState; + + // We have some situations where we're already holding a lock, and we need to release and reacquire the lock across a window. + // This is a dangerous construct because the backout code can block. + // Generally, it's better to use a regular CrstHolder, and then use the Release() / Acquire() methods on it. + // This just exists to convert legacy OS Critical Section patterns over to holders. + typedef DacHolder<CrstBase *, CrstBase::ReleaseLock, CrstBase::AcquireLock, 0, CompareDefault, HSV_ValidateMinimumStackReq> UnsafeCrstInverseHolder; +}; + +typedef CrstBase::CrstHolder CrstHolder; +typedef CrstBase::CrstHolderWithState CrstHolderWithState; + + +// The CRST. +class Crst : public CrstBase +{ +public: + void *operator new(size_t size) + { + WRAPPER_NO_CONTRACT; + return new BYTE[size]; + } + +private: + // Do not use inplace operator new on Crst. A wrong destructor would be called if the constructor fails. + // Use CrstStatic or CrstExplicitInit instead of the inplace operator new. + void *operator new(size_t size, void *pInPlace); + +public: + +#ifndef DACCESS_COMPILE + + //----------------------------------------------------------------- + // Constructor. + //----------------------------------------------------------------- + Crst(CrstType crstType, CrstFlags flags = CRST_DEFAULT) + { + WRAPPER_NO_CONTRACT; + + // throw away the debug-only parameter in retail + InitWorker(INDEBUG_COMMA(crstType) flags); + } + + //----------------------------------------------------------------- + // Destructor. + //----------------------------------------------------------------- + ~Crst() + { + WRAPPER_NO_CONTRACT; + + Destroy(); + }; + +#else + + Crst(CrstType crstType, CrstFlags flags = CRST_DEFAULT) { + LIMITED_METHOD_CONTRACT; + }; + +#endif + + Crst() { + LIMITED_METHOD_CONTRACT; + } +}; + +typedef DPTR(Crst) PTR_Crst; + +/* to be used as static variable - no constructor/destructor, assumes zero + initialized memory */ +class CrstStatic : public CrstBase +{ +public: + VOID Init(CrstType crstType, CrstFlags flags = CRST_DEFAULT) + { + WRAPPER_NO_CONTRACT; + + _ASSERTE((flags & CRST_INITIALIZED) == 0); + + // throw away the debug-only parameter in retail + InitWorker(INDEBUG_COMMA(crstType) flags); + } + + bool InitNoThrow(CrstType crstType, CrstFlags flags = CRST_DEFAULT) + { + CONTRACTL { + NOTHROW; + } CONTRACTL_END; + + _ASSERTE((flags & CRST_INITIALIZED) == 0); + + bool fSuccess = false; + + EX_TRY + { + // throw away the debug-only parameter in retail + InitWorker(INDEBUG_COMMA(crstType) flags); + fSuccess = true; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + + return fSuccess; + } +}; + +/* to be used as regular variable when a explicit call to Init method is needed */ +class CrstExplicitInit : public CrstStatic +{ +public: + CrstExplicitInit() { + m_dwFlags = 0; + } + ~CrstExplicitInit() { +#ifndef DACCESS_COMPILE + Destroy(); +#endif + } +}; + +__inline BOOL IsOwnerOfCrst(LPVOID lock) +{ + WRAPPER_NO_CONTRACT; + +#ifdef _DEBUG + return ((Crst*)lock)->OwnedByCurrentThread(); +#else + // This function should not be called on free build. + DebugBreak(); + return TRUE; +#endif +} + +#ifdef TEST_DATA_CONSISTENCY +// used for test purposes. Determines if a crst is held. +void DebugTryCrst(CrstBase * pLock); +#endif +#endif // __crst_h__ + + |