summaryrefslogtreecommitdiff
path: root/src/vm/crst.h
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/crst.h
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/crst.h')
-rw-r--r--src/vm/crst.h566
1 files changed, 566 insertions, 0 deletions
diff --git a/src/vm/crst.h b/src/vm/crst.h
new file mode 100644
index 0000000000..da50066ccc
--- /dev/null
+++ b/src/vm/crst.h
@@ -0,0 +1,566 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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
+{
+#ifndef CLR_STANDALONE_BINDER
+
+// 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.IsSameThread();
+#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
+
+#endif // CLR_STANDALONE_BINDER
+
+ 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
+
+#ifndef CLR_STANDALONE_BINDER
+
+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;
+
+#endif // CLR_STANDALONE_BINDER
+};
+
+#ifndef CLR_STANDALONE_BINDER
+typedef CrstBase::CrstHolder CrstHolder;
+typedef CrstBase::CrstHolderWithState CrstHolderWithState;
+#endif // CLR_STANDALONE_BINDER
+
+
+// The CRST.
+class Crst : public CrstBase
+{
+#ifndef CLR_STANDALONE_BINDER
+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;
+ };
+
+ Crst() {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+#endif
+#endif // CLR_STANDALONE_BINDER
+};
+
+typedef DPTR(Crst) PTR_Crst;
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+class CrstStatic : public CrstBase
+{
+#ifndef CLR_STANDALONE_BINDER
+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;
+ }
+#endif // CLR_STANDALONE_BINDER
+};
+
+/* to be used as regular variable when a explicit call to Init method is needed */
+class CrstExplicitInit : public CrstStatic
+{
+#ifndef CLR_STANDALONE_BINDER
+public:
+ CrstExplicitInit() {
+ m_dwFlags = 0;
+ }
+ ~CrstExplicitInit() {
+#ifndef DACCESS_COMPILE
+ Destroy();
+#endif
+ }
+#endif // CLR_STANDALONE_BINDER
+};
+
+#ifndef CLR_STANDALONE_BINDER
+__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
+}
+
+#endif // CLR_STANDALONE_BINDER
+
+#ifdef TEST_DATA_CONSISTENCY
+// used for test purposes. Determines if a crst is held.
+void DebugTryCrst(CrstBase * pLock);
+#endif
+#endif // __crst_h__
+
+