summaryrefslogtreecommitdiff
path: root/src/vm/spinlock.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/spinlock.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/spinlock.h')
-rw-r--r--src/vm/spinlock.h307
1 files changed, 307 insertions, 0 deletions
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 <stddef.h>
+
+
+// #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<LONG> m_value;
+
+public:
+ BOOL IsHeld()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (BOOL)m_value;
+ }
+
+ typedef Holder<DangerousNonHostedSpinLock *, DangerousNonHostedSpinLock::AcquireLock, DangerousNonHostedSpinLock::ReleaseLock> Holder;
+ typedef ConditionalStateHolder<DangerousNonHostedSpinLock *, DangerousNonHostedSpinLock::TryAcquireLock, DangerousNonHostedSpinLock::ReleaseLock> 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<SpinLockState> 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_