1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
20 BACKOFF_LIMIT = 1000 // used in spin to acquire
25 // profile information
26 ULONG SpinLockProfiler::s_ulBackOffs = 0;
27 ULONG SpinLockProfiler::s_ulCollisons [LOCK_TYPE_DEFAULT + 1] = { 0 };
28 ULONG SpinLockProfiler::s_ulSpins [LOCK_TYPE_DEFAULT + 1] = { 0 };
34 // Global SpinLock variables will cause the constructor to be
35 // called during DllInit, which means we cannot use full contracts
36 // because we have not called InitUtilCode yet.
37 STATIC_CONTRACT_NOTHROW;
38 STATIC_CONTRACT_GC_NOTRIGGER;
40 m_Initialized = UnInitialized;
55 void SpinLock::Init(LOCK_TYPE type, bool RequireCoopGC)
64 if (m_Initialized == Initialized)
66 _ASSERTE (type == m_LockType);
67 _ASSERTE (RequireCoopGC == m_requireCoopGCMode);
69 // We have initialized this spinlock.
75 LONG curValue = FastInterlockCompareExchange((LONG*)&m_Initialized, BeingInitialized, UnInitialized);
76 if (curValue == Initialized)
80 else if (curValue == UnInitialized)
82 // We are the first to initialize the lock
87 __SwitchToThread(10, CALLER_LIMITS_SPINNING);
97 m_requireCoopGCMode = RequireCoopGC;
100 _ASSERTE (m_Initialized == BeingInitialized);
101 m_Initialized = Initialized;
105 BOOL SpinLock::OwnedByCurrentThread()
115 return m_holdingThreadId.IsCurrentThread();
119 DEBUG_NOINLINE void SpinLock::AcquireLock(SpinLock *s, Thread * pThread)
122 STATIC_CONTRACT_GC_NOTRIGGER;
127 DEBUG_NOINLINE void SpinLock::ReleaseLock(SpinLock *s, Thread * pThread)
131 s->FreeLock(pThread);
135 void SpinLock::GetLock(Thread* pThread)
139 DISABLED(THROWS); // need to rewrite spin locks to no-throw.
145 _ASSERTE(m_Initialized == Initialized);
152 // Not CLR Sync hosted, so we use interlocked operations on
153 // m_lock to acquire the lock. This will automatically cause
154 // us to call EE_LOCK_TAKEN(this);
155 if (!GetLockNoWait())
161 INCTHREADLOCKCOUNTTHREAD(pThread);
163 m_holdingThreadId.SetToCurrentThread();
168 //----------------------------------------------------------------------------
169 // SpinLock::GetLockNoWait
170 // used interlocked exchange and fast lock acquire
172 BOOL SpinLock::GetLockNoWait()
183 if (VolatileLoad(&m_lock) == 0 && FastInterlockExchange (&m_lock, 1) == 0)
192 //----------------------------------------------------------------------------
193 // SpinLock::FreeLock
194 // Release the spinlock
196 void SpinLock::FreeLock(Thread* pThread)
205 _ASSERTE(m_Initialized == Initialized);
208 _ASSERTE(OwnedByCurrentThread());
209 m_holdingThreadId.Clear();
214 VolatileStore(&m_lock, (LONG)0);
217 DECTHREADLOCKCOUNTTHREAD(pThread);
218 EE_LOCK_RELEASED(this);
220 } // SpinLock::FreeLock ()
223 //----------------------------------------------------------------------------
224 // SpinLock::SpinToAcquire , non-inline function, called from inline Acquire
226 // Spin waiting for a spinlock to become free.
230 SpinLock::SpinToAcquire()
242 YieldProcessorNormalizationInfo normalizationInfo;
246 for (ULONG i = ulSpins + 10000;
250 YieldProcessorNormalized(normalizationInfo); // indicate to the processor that we are spinning
252 // Note: Must use Volatile to ensure the lock is
253 // refetched from memory.
255 if (VolatileLoad(&m_lock) == 0)
261 // Try the inline atomic test again.
265 // EE_LOCK_TAKEN(this) has already been called by GetLockNoWait
270 __SwitchToThread(0, backoffs++);
275 SpinLockProfiler::IncrementCollisions (m_LockType);
276 SpinLockProfiler::IncrementSpins (m_LockType, ulSpins);
277 SpinLockProfiler::IncrementBackoffs (backoffs);
280 } // SpinLock::SpinToAcquire ()
283 // If a GC is not allowed when we enter the lock, we'd better not do anything inside
284 // the lock that could provoke a GC. Otherwise other threads attempting to block
285 // (which are presumably in the same GC mode as this one) will block. This will cause
286 // a deadlock if we do attempt a GC because we can't suspend blocking threads and we
287 // can't release the spin lock.
288 void SpinLock::dbg_PreEnterLock()
298 Thread* pThread = GetThread();
301 // SpinLock can not be nested.
302 _ASSERTE ((pThread->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0);
304 pThread->SetThreadStateNC(Thread::TSNC_OwnsSpinLock);
306 if (!pThread->PreemptiveGCDisabled())
307 _ASSERTE(!m_requireCoopGCMode);
311 void SpinLock::dbg_EnterLock()
321 Thread* pThread = GetThread();
324 INCONTRACT(pThread->BeginNoTriggerGC(__FILE__, __LINE__));
328 void SpinLock::dbg_LeaveLock()
338 Thread* pThread = GetThread();
341 _ASSERTE ((pThread->m_StateNC & Thread::TSNC_OwnsSpinLock) != 0);
342 pThread->ResetThreadStateNC(Thread::TSNC_OwnsSpinLock);
343 INCONTRACT(pThread->EndNoTriggerGC());
348 void SpinLockProfiler::InitStatics ()
359 memset (s_ulCollisons, 0, sizeof (s_ulCollisons));
360 memset (s_ulSpins, 0, sizeof (s_ulSpins));
363 void SpinLockProfiler::IncrementSpins (LOCK_TYPE type, ULONG value)
373 _ASSERTE(type <= LOCK_TYPE_DEFAULT);
374 s_ulSpins [type] += value;
377 void SpinLockProfiler::IncrementCollisions (LOCK_TYPE type)
387 ++s_ulCollisons [type];
390 void SpinLockProfiler::IncrementBackoffs (ULONG value)
400 s_ulBackOffs += value;
403 void SpinLockProfiler::DumpStatics()
418 // End of file: spinlock.cpp