From ef1e2ab328087c61a6878c1e84f4fc5d710aebce Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 30 Jan 2015 14:14:42 -0800 Subject: Initial commit to populate CoreCLR repo [tfs-changeset: 1407945] --- src/vm/spinlock.h | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 src/vm/spinlock.h (limited to 'src/vm/spinlock.h') diff --git a/src/vm/spinlock.h b/src/vm/spinlock.h new file mode 100644 index 0000000000..b3aced393a --- /dev/null +++ b/src/vm/spinlock.h @@ -0,0 +1,307 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license 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 +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + IHostCrst *m_hostLock; +#endif // FEATURE_INCLUDE_ALL_INTERFACES + }; + + 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); + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +#define SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS m_pThread +#else +#define SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS NULL +#endif + + class Holder + { + SpinLock * m_pSpinLock; +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + Thread * m_pThread; +#endif + public: + Holder(SpinLock * s) : + m_pSpinLock(s) +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + , m_pThread(GetThread()) +#endif + { + 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_ -- cgit v1.2.3