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.
4 /******************************************************************************
9 Purpose: Part of the utilities library for the VIPER project
11 Abstract : Implements the UTSemReadWrite class.
12 -------------------------------------------------------------------------------
16 *******************************************************************************/
24 // Consider replacing this with a #ifdef INTEROP_DEBUGGING
25 #if !defined(SELF_NO_HOST) && defined(_TARGET_X86_) && !defined(FEATURE_PAL)
26 // For Interop debugging, the UTSemReadWrite class must inform the debugger
27 // that this thread can't be suspended currently. See vm\util.hpp for the
28 // implementation of these methods.
29 void IncCantStopCount();
30 void DecCantStopCount();
32 #define IncCantStopCount()
33 #define DecCantStopCount()
34 #endif // !SELF_NO_HOST && _TARGET_X86_
36 /******************************************************************************
37 Definitions of the bit fields in UTSemReadWrite::m_dwFlag:
39 Warning: The code assume that READER_MASK is in the low-order bits of the DWORD.
40 ******************************************************************************/
42 const ULONG READERS_MASK = 0x000003FF; // field that counts number of readers
43 const ULONG READERS_INCR = 0x00000001; // amount to add to increment number of readers
45 // The following field is 2 bits long to make it easier to catch errors.
46 // (If the number of writers ever exceeds 1, we've got problems.)
47 const ULONG WRITERS_MASK = 0x00000C00; // field that counts number of writers
48 const ULONG WRITERS_INCR = 0x00000400; // amount to add to increment number of writers
50 const ULONG READWAITERS_MASK = 0x003FF000; // field that counts number of threads waiting to read
51 const ULONG READWAITERS_INCR = 0x00001000; // amount to add to increment number of read waiters
53 const ULONG WRITEWAITERS_MASK = 0xFFC00000; // field that counts number of threads waiting to write
54 const ULONG WRITEWAITERS_INCR = 0x00400000; // amount to add to increment number of write waiters
56 // ======================================================================================
59 // Copy of definition from file:..\VM\spinlock.h
60 #define CALLER_LIMITS_SPINNING 0
62 #if (defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)) || (defined(FEATURE_PAL) && defined(DACCESS_COMPILE))
64 // When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread
65 BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount)
67 // This is just simple implementation that does not support full dwSwitchCount arg
68 _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING);
69 return SwitchToThread();
72 Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE;
74 // Global System Information
75 SYSTEM_INFO g_SystemInfo;
77 // Configurable constants used across our spin locks
78 SpinConstants g_SpinConstants = {
79 50, // dwInitialDuration
80 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) ... updated in code:InitializeSpinConstants_NoHost
83 0 // dwMonitorSpinCount
86 inline void InitializeSpinConstants_NoHost()
88 g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000;
91 #else //!SELF_NO_HOST || CROSSGEN_COMPILE
93 // Use VM/CrossGen functions and variables
94 BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount);
95 extern SYSTEM_INFO g_SystemInfo;
96 extern SpinConstants g_SpinConstants;
98 #endif //!SELF_NO_HOST || CROSSGEN_COMPILE
100 /******************************************************************************
101 Function : UTSemReadWrite::UTSemReadWrite
103 Abstract: Constructor.
104 ******************************************************************************/
105 UTSemReadWrite::UTSemReadWrite()
114 #if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
115 if (!g_fInitializedGlobalSystemInfo)
117 GetSystemInfo(&g_SystemInfo);
118 InitializeSpinConstants_NoHost();
120 g_fInitializedGlobalSystemInfo = TRUE;
122 #endif //SELF_NO_HOST && !CROSSGEN_COMPILE
125 m_pReadWaiterSemaphore = NULL;
126 m_pWriteWaiterEvent = NULL;
130 /******************************************************************************
131 Function : UTSemReadWrite::~UTSemReadWrite
134 ******************************************************************************/
135 UTSemReadWrite::~UTSemReadWrite()
144 _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use");
146 if (m_pReadWaiterSemaphore != NULL)
147 delete m_pReadWaiterSemaphore;
149 if (m_pWriteWaiterEvent != NULL)
150 delete m_pWriteWaiterEvent;
153 //=======================================================================================
155 // Initialize the lock (its semaphore and event)
158 UTSemReadWrite::Init()
169 _ASSERTE(m_pReadWaiterSemaphore == NULL);
170 _ASSERTE(m_pWriteWaiterEvent == NULL);
174 CONTRACT_VIOLATION(ThrowsViolation);
176 m_pReadWaiterSemaphore = new Semaphore();
177 m_pReadWaiterSemaphore->Create(0, MAXLONG);
179 m_pWriteWaiterEvent = new Event();
180 m_pWriteWaiterEvent->CreateAutoEvent(FALSE);
186 EX_END_CATCH(SwallowAllExceptions)
191 } // UTSemReadWrite::Init
193 /******************************************************************************
194 Function : UTSemReadWrite::LockRead
196 Abstract: Obtain a shared lock
197 ******************************************************************************/
198 HRESULT UTSemReadWrite::LockRead()
208 // Inform CLR that the debugger shouldn't suspend this thread while
209 // holding this lock.
212 // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
213 for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
215 DWORD i = g_SpinConstants.dwInitialDuration;
219 DWORD dwFlag = m_dwFlag;
221 if (dwFlag < READERS_MASK)
222 { // There are just readers in the play, try to add one more
223 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
225 goto ReadLockAcquired;
229 if (g_SystemInfo.dwNumberOfProcessors <= 1)
230 { // We do not need to spin on a single processor
234 // Delay by approximately 2*i clock cycles (Pentium III).
235 YieldProcessorNormalizedForPreSkylakeCount(i);
237 // exponential backoff: wait a factor longer in the next iteration
238 i *= g_SpinConstants.dwBackoffFactor;
239 } while (i < g_SpinConstants.dwMaximumDuration);
241 __SwitchToThread(0, CALLER_LIMITS_SPINNING);
248 DWORD dwFlag = m_dwFlag;
250 if (dwFlag < READERS_MASK)
251 { // There are just readers in the play, try to add one more
252 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
257 else if ((dwFlag & READERS_MASK) == READERS_MASK)
258 { // The number of readers has reached the maximum (0x3ff), wait 1s
259 ClrSleepEx(1000, FALSE);
261 else if ((dwFlag & READWAITERS_MASK) == READWAITERS_MASK)
262 { // The number of readers waiting on semaphore has reached the maximum (0x3ff), wait 1s
263 ClrSleepEx(1000, FALSE);
266 { // Try to add waiting reader and then wait for signal
267 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag))
269 m_pReadWaiterSemaphore->Wait(INFINITE, FALSE);
276 _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero after acquiring read lock");
277 _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero after acquiring write lock");
281 } // UTSemReadWrite::LockRead
285 /******************************************************************************
286 Function : UTSemReadWrite::LockWrite
288 Abstract: Obtain an exclusive lock
289 ******************************************************************************/
290 HRESULT UTSemReadWrite::LockWrite()
300 // Inform CLR that the debugger shouldn't suspend this thread while
301 // holding this lock.
304 // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
305 for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
307 DWORD i = g_SpinConstants.dwInitialDuration;
311 DWORD dwFlag = m_dwFlag;
314 { // No readers/writers in play, try to add a writer
315 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
317 goto WriteLockAcquired;
321 if (g_SystemInfo.dwNumberOfProcessors <= 1)
322 { // We do not need to spin on a single processor
326 // Delay by approximately 2*i clock cycles (Pentium III).
327 YieldProcessorNormalizedForPreSkylakeCount(i);
329 // exponential backoff: wait a factor longer in the next iteration
330 i *= g_SpinConstants.dwBackoffFactor;
331 } while (i < g_SpinConstants.dwMaximumDuration);
333 __SwitchToThread(0, CALLER_LIMITS_SPINNING);
340 DWORD dwFlag = m_dwFlag;
343 { // No readers/writers in play, try to add a writer
344 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
349 else if ((dwFlag & WRITEWAITERS_MASK) == WRITEWAITERS_MASK)
350 { // The number of writers waiting on semaphore has reached the maximum (0x3ff), wait 1s
351 ClrSleepEx(1000, FALSE);
354 { // Try to add waiting writer and then wait for signal
355 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag))
357 m_pWriteWaiterEvent->Wait(INFINITE, FALSE);
365 _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero after acquiring write lock");
366 _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 after acquiring write lock");
370 } // UTSemReadWrite::LockWrite
374 /******************************************************************************
375 Function : UTSemReadWrite::UnlockRead
377 Abstract: Release a shared lock
378 ******************************************************************************/
379 void UTSemReadWrite::UnlockRead()
391 _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero before releasing read lock");
392 _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero before releasing read lock");
398 if (dwFlag == READERS_INCR)
399 { // we're the last reader, and nobody is waiting
400 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
406 else if ((dwFlag & READERS_MASK) > READERS_INCR)
407 { // we're not the last reader
408 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - READERS_INCR, dwFlag))
416 // here, there should be exactly 1 reader (us), and at least one waiting writer.
417 _ASSERTE ((dwFlag & READERS_MASK) == READERS_INCR && "UnlockRead consistency error 1");
418 _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockRead consistency error 2");
420 // one or more writers is waiting, do one of them next
421 // (remove a reader (us), remove a write waiter, add a writer
423 InterlockedCompareExchangeT(
425 dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR,
428 m_pWriteWaiterEvent->Set();
435 EE_LOCK_RELEASED(this);
436 } // UTSemReadWrite::UnlockRead
439 /******************************************************************************
440 Function : UTSemReadWrite::UnlockWrite
442 Abstract: Release an exclusive lock
443 ******************************************************************************/
444 void UTSemReadWrite::UnlockWrite()
456 _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero before releasing write lock");
457 _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 before releasing write lock");
463 if (dwFlag == WRITERS_INCR)
464 { // nobody is waiting
465 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
471 else if ((dwFlag & READWAITERS_MASK) != 0)
472 { // one or more readers are waiting, do them all next
473 count = (dwFlag & READWAITERS_MASK) / READWAITERS_INCR;
474 // remove a writer (us), remove all read waiters, turn them into readers
476 InterlockedCompareExchangeT(
478 dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR,
481 m_pReadWaiterSemaphore->Release(count, NULL);
487 { // one or more writers is waiting, do one of them next
488 _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockWrite consistency error");
489 // (remove a writer (us), remove a write waiter, add a writer
490 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - WRITEWAITERS_INCR, dwFlag))
492 m_pWriteWaiterEvent->Set();
499 EE_LOCK_RELEASED(this);
500 } // UTSemReadWrite::UnlockWrite
504 //=======================================================================================
506 UTSemReadWrite::Debug_IsLockedForRead()
508 return ((m_dwFlag & READERS_MASK) != 0);
511 //=======================================================================================
513 UTSemReadWrite::Debug_IsLockedForWrite()
515 return ((m_dwFlag & WRITERS_MASK) != 0);