// 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. //---------------------------------------------------------------------------- // spinlock.h , defines the spin lock class and a profiler class // // //---------------------------------------------------------------------------- //#ifndef _H_UTIL //#error I am a part of util.hpp Please don't include me alone ! //#endif #ifndef _H_SPINLOCK_ #define _H_SPINLOCK_ #include // #SwitchToThreadSpinning // // If you call __SwitchToThread in a loop waiting for a condition to be met, // it is critical that you insert periodic sleeps. This is because the thread // you are waiting for to set that condition may need your CPU, and simply // calling __SwitchToThread(0) will NOT guarantee that it gets a chance to run. // If there are other runnable threads of higher priority, or even if there // aren't and it is in another processor's queue, you will be spinning a very // long time. // // To force all callers to consider this issue and to avoid each having to // duplicate the same backoff code, __SwitchToThread takes a required second // parameter. If you want it to handle backoff for you, this parameter should // be the number of successive calls you have made to __SwitchToThread (a loop // count). If you want to take care of backing off yourself, you can pass // CALLER_LIMITS_SPINNING. There are three valid cases for doing this: // // - You count iterations and induce a sleep periodically // - The number of consecutive __SwitchToThreads is limited // - Your call to __SwitchToThread includes a non-zero sleep duration // // Lastly, to simplify this requirement for the following common coding pattern: // // while (!condition) // SwitchToThread // // you can use the YIELD_WHILE macro. #define CALLER_LIMITS_SPINNING 0 #define YIELD_WHILE(condition) \ { \ DWORD __dwSwitchCount = 0; \ while (condition) \ { \ __SwitchToThread(0, ++__dwSwitchCount); \ } \ } // non-zero return value if this function causes the OS to switch to another thread BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount); BOOL __DangerousSwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount, BOOL goThroughOS); //---------------------------------------------------------------------------- // class: DangerousNonHostedSpinLock // // PURPOSE: // A simple wrapper around the spinloop without host interactions. To be // used for short-time locking in the VM, in particular when the runtime // has not been started yet. // //---------------------------------------------------------------------------- class DangerousNonHostedSpinLock { public: FORCEINLINE DangerousNonHostedSpinLock() { LIMITED_METHOD_CONTRACT; m_value = 0; } private: // Intentionally unimplemented - prevents the compiler from generating default copy ctor. DangerousNonHostedSpinLock(DangerousNonHostedSpinLock const & other); FORCEINLINE void Acquire() { WRAPPER_NO_CONTRACT; YIELD_WHILE(FastInterlockExchange(&m_value, 1) == 1); } FORCEINLINE BOOL TryAcquire() { WRAPPER_NO_CONTRACT; return (FastInterlockExchange(&m_value, 1) == 0); } FORCEINLINE void Release() { LIMITED_METHOD_CONTRACT; m_value = 0; } inline static void AcquireLock(DangerousNonHostedSpinLock *pLock) { WRAPPER_NO_CONTRACT; pLock->Acquire(); } inline static BOOL TryAcquireLock(DangerousNonHostedSpinLock *pLock) { WRAPPER_NO_CONTRACT; return pLock->TryAcquire(); } inline static void ReleaseLock(DangerousNonHostedSpinLock *pLock) { WRAPPER_NO_CONTRACT; pLock->Release(); } Volatile m_value; public: BOOL IsHeld() { LIMITED_METHOD_CONTRACT; return (BOOL)m_value; } typedef Holder Holder; typedef ConditionalStateHolder TryHolder; }; typedef DangerousNonHostedSpinLock::Holder DangerousNonHostedSpinLockHolder; typedef DangerousNonHostedSpinLock::TryHolder DangerousNonHostedSpinLockTryHolder; class SpinLock; // Lock Types, used in profiling // enum LOCK_TYPE { LOCK_PLUSWRAPPER_CACHE = 1, // change LOCK_FCALL = 2, // leave, but rank to tip LOCK_COMCTXENTRYCACHE = 3, // creates events, allocs memory, SEH, etc. #ifdef FEATURE_COMINTEROP LOCK_COMCALL = 4, #endif LOCK_REFLECTCACHE = 5, LOCK_CORMAP = 7, LOCK_TYPE_DEFAULT = 8 }; //---------------------------------------------------------------------------- // class: Spinlock // // PURPOSE: // spinlock class that contains constructor and out of line spinloop. // //---------------------------------------------------------------------------- class SpinLock { private: union { // m_lock has to be the fist data member in the class LONG m_lock; // LONG used in interlocked exchange }; enum SpinLockState { UnInitialized, BeingInitialized, Initialized }; Volatile m_Initialized; // To verify initialized // And initialize once #ifdef _DEBUG LOCK_TYPE m_LockType; // lock type to track statistics // Check for dead lock situation. bool m_requireCoopGCMode; EEThreadId m_holdingThreadId; #endif public: SpinLock (); ~SpinLock (); //Init method, initialize lock and _DEBUG flags void Init(LOCK_TYPE type, bool RequireCoopGC = FALSE); //----------------------------------------------------------------- // Is the current thread the owner? //----------------------------------------------------------------- #ifdef _DEBUG BOOL OwnedByCurrentThread(); #endif private: void SpinToAcquire (); // out of line call spins #ifdef _DEBUG void dbg_PreEnterLock(); void dbg_EnterLock(); void dbg_LeaveLock(); #endif // The following 5 APIs must remain private. We want all entry/exit code to // occur via holders, so that exceptions will be sure to release the lock. private: void GetLock(Thread * pThread); // Acquire lock, blocks if unsuccessful BOOL GetLockNoWait(); // Acquire lock, fail-fast void FreeLock(Thread * pThread); // Release lock public: static void AcquireLock(SpinLock *s, Thread * pThread); static void ReleaseLock(SpinLock *s, Thread * pThread); #define SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS NULL class Holder { SpinLock * m_pSpinLock; public: Holder(SpinLock * s) : m_pSpinLock(s) { SCAN_SCOPE_BEGIN; STATIC_CONTRACT_GC_NOTRIGGER; m_pSpinLock->GetLock(SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); } ~Holder() { SCAN_SCOPE_END; m_pSpinLock->FreeLock(SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); } }; }; typedef SpinLock::Holder SpinLockHolder; #define TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(lock) \ SpinLockHolder __spinLockHolder(lock);\ GCX_NOTRIGGER (); #define ACQUIRE_SPINLOCK_NO_HOLDER(lock, thread)\ { \ SpinLock::AcquireLock(lock, thread); \ GCX_NOTRIGGER(); \ CANNOTTHROWCOMPLUSEXCEPTION(); \ STATIC_CONTRACT_NOTHROW; \ #define RELEASE_SPINLOCK_NO_HOLDER(lock, thread)\ SpinLock::ReleaseLock(lock, thread); \ } \ __inline BOOL IsOwnerOfSpinLock (LPVOID lock) { WRAPPER_NO_CONTRACT; #ifdef _DEBUG return ((SpinLock*)lock)->OwnedByCurrentThread(); #else // This function should not be called on free build. DebugBreak(); return TRUE; #endif } #ifdef _DEBUG //---------------------------------------------------------------------------- // class SpinLockProfiler // to track contention, useful for profiling // //---------------------------------------------------------------------------- class SpinLockProfiler { // Pointer to spinlock names. // static ULONG s_ulBackOffs; static ULONG s_ulCollisons [LOCK_TYPE_DEFAULT + 1]; static ULONG s_ulSpins [LOCK_TYPE_DEFAULT + 1]; public: static void InitStatics (); static void IncrementSpins (LOCK_TYPE type, ULONG value); static void IncrementCollisions (LOCK_TYPE type); static void IncrementBackoffs (ULONG value); static void DumpStatics(); }; #endif // ifdef _DEBUG #endif // ifndef _H_SPINLOCK_