[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / utilcode / utsem.cpp
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 /******************************************************************************
5     FILE : UTSEM.CPP
6
7
8
9     Purpose: Part of the utilities library for the VIPER project
10
11     Abstract : Implements the UTSemReadWrite class.
12 -------------------------------------------------------------------------------
13 Revision History:
14
15  
16 *******************************************************************************/
17 #include "stdafx.h"
18 #include "clrhost.h"
19 #include "ex.h"
20
21 #include <utsem.h>
22 #include "contract.h"
23
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();
31 #else
32 #define IncCantStopCount()
33 #define DecCantStopCount()
34 #endif  // !SELF_NO_HOST && _TARGET_X86_
35
36 /******************************************************************************
37 Definitions of the bit fields in UTSemReadWrite::m_dwFlag:
38
39 Warning: The code assume that READER_MASK is in the low-order bits of the DWORD.
40 ******************************************************************************/
41
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
44
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
49
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
52
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
55
56 // ======================================================================================
57 // Spinning support
58
59 // Copy of definition from file:..\VM\spinlock.h
60 #define CALLER_LIMITS_SPINNING 0
61
62 #if (defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)) || (defined(FEATURE_PAL) && defined(DACCESS_COMPILE))
63
64 // When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread
65 BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount)
66 {
67     // This is just simple implementation that does not support full dwSwitchCount arg
68     _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING);
69     return SwitchToThread();
70 }
71
72 Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE;
73
74 // Global System Information
75 SYSTEM_INFO g_SystemInfo;
76
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
81     3,         // dwBackoffFactor
82     10,        // dwRepetitions
83     0          // dwMonitorSpinCount
84 };
85
86 inline void InitializeSpinConstants_NoHost()
87 {
88     g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000;
89 }
90
91 #else //!SELF_NO_HOST || CROSSGEN_COMPILE
92
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;
97
98 #endif //!SELF_NO_HOST || CROSSGEN_COMPILE
99
100 /******************************************************************************
101 Function : UTSemReadWrite::UTSemReadWrite
102
103 Abstract: Constructor.
104 ******************************************************************************/
105 UTSemReadWrite::UTSemReadWrite()
106 {
107     CONTRACTL
108     {
109         NOTHROW;
110         GC_NOTRIGGER;
111     }
112     CONTRACTL_END;
113     
114 #if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
115     if (!g_fInitializedGlobalSystemInfo)
116     {
117         GetSystemInfo(&g_SystemInfo);
118         InitializeSpinConstants_NoHost();
119
120         g_fInitializedGlobalSystemInfo = TRUE;
121     }
122 #endif //SELF_NO_HOST && !CROSSGEN_COMPILE
123     
124     m_dwFlag = 0;
125     m_pReadWaiterSemaphore = NULL;
126     m_pWriteWaiterEvent = NULL;
127 }
128
129
130 /******************************************************************************
131 Function : UTSemReadWrite::~UTSemReadWrite
132
133 Abstract: Destructor
134 ******************************************************************************/
135 UTSemReadWrite::~UTSemReadWrite()
136 {
137     CONTRACTL
138     {
139         NOTHROW;
140         GC_NOTRIGGER;
141     }
142     CONTRACTL_END;
143     
144     _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use");
145     
146     if (m_pReadWaiterSemaphore != NULL)
147         delete m_pReadWaiterSemaphore;
148     
149     if (m_pWriteWaiterEvent != NULL)
150         delete m_pWriteWaiterEvent;
151 }
152
153 //=======================================================================================
154 // 
155 // Initialize the lock (its semaphore and event)
156 // 
157 HRESULT 
158 UTSemReadWrite::Init()
159 {
160     CONTRACTL
161     {
162         NOTHROW;
163         GC_NOTRIGGER;
164     }
165     CONTRACTL_END;
166     
167     HRESULT hr = S_OK;
168     
169     _ASSERTE(m_pReadWaiterSemaphore == NULL);
170     _ASSERTE(m_pWriteWaiterEvent == NULL);
171     
172     EX_TRY
173     {
174         CONTRACT_VIOLATION(ThrowsViolation);
175         
176         m_pReadWaiterSemaphore = new Semaphore();
177         m_pReadWaiterSemaphore->Create(0, MAXLONG);
178         
179         m_pWriteWaiterEvent = new Event();
180         m_pWriteWaiterEvent->CreateAutoEvent(FALSE);
181     }
182     EX_CATCH
183     {
184         hr = E_OUTOFMEMORY;
185     }
186     EX_END_CATCH(SwallowAllExceptions)
187     IfFailGo(hr);
188     
189 ErrExit:
190     return hr;
191 } // UTSemReadWrite::Init
192
193 /******************************************************************************
194 Function : UTSemReadWrite::LockRead
195
196 Abstract: Obtain a shared lock
197 ******************************************************************************/
198 HRESULT UTSemReadWrite::LockRead()
199 {
200     CONTRACTL
201     {
202         NOTHROW;
203         GC_NOTRIGGER;
204         CAN_TAKE_LOCK;
205     }
206     CONTRACTL_END;
207     
208     // Inform CLR that the debugger shouldn't suspend this thread while 
209     // holding this lock.
210     IncCantStopCount();
211     
212     // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
213     for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
214     {
215         DWORD i = g_SpinConstants.dwInitialDuration;
216         
217         do
218         {
219             DWORD dwFlag = m_dwFlag;
220             
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))
224                 {
225                     goto ReadLockAcquired;
226                 }
227             }
228             
229             if (g_SystemInfo.dwNumberOfProcessors <= 1)
230             {   // We do not need to spin on a single processor
231                 break;
232             }
233             
234             // Delay by approximately 2*i clock cycles (Pentium III).
235             YieldProcessorNormalizedForPreSkylakeCount(i);
236
237             // exponential backoff: wait a factor longer in the next iteration
238             i *= g_SpinConstants.dwBackoffFactor;
239         } while (i < g_SpinConstants.dwMaximumDuration);
240         
241         __SwitchToThread(0, CALLER_LIMITS_SPINNING);
242     }
243     // Stop spinning
244     
245     // Start waiting
246     for (;;)
247     {
248         DWORD dwFlag = m_dwFlag;
249
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))
253             {
254                 break;
255             }
256         }
257         else if ((dwFlag & READERS_MASK) == READERS_MASK)
258         {   // The number of readers has reached the maximum (0x3ff), wait 1s
259             ClrSleepEx(1000, FALSE);
260         }
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);
264         }
265         else
266         {   // Try to add waiting reader and then wait for signal
267             if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag))
268             {
269                 m_pReadWaiterSemaphore->Wait(INFINITE, FALSE);
270                 break;
271             }
272         }
273     }
274     
275 ReadLockAcquired:
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");
278     EE_LOCK_TAKEN(this);
279     
280     return S_OK;
281 } // UTSemReadWrite::LockRead
282
283
284
285 /******************************************************************************
286 Function : UTSemReadWrite::LockWrite
287
288 Abstract: Obtain an exclusive lock
289 ******************************************************************************/
290 HRESULT UTSemReadWrite::LockWrite()
291 {
292     CONTRACTL
293     {
294         NOTHROW;
295         GC_NOTRIGGER;
296         CAN_TAKE_LOCK;
297     }
298     CONTRACTL_END;
299     
300     // Inform CLR that the debugger shouldn't suspend this thread while 
301     // holding this lock.
302     IncCantStopCount();
303
304     // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
305     for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
306     {
307         DWORD i = g_SpinConstants.dwInitialDuration;
308         
309         do
310         {
311             DWORD dwFlag = m_dwFlag;
312             
313             if (dwFlag == 0)
314             {   // No readers/writers in play, try to add a writer
315                 if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
316                 {
317                     goto WriteLockAcquired;
318                 }
319             }
320             
321             if (g_SystemInfo.dwNumberOfProcessors <= 1)
322             {   // We do not need to spin on a single processor
323                 break;
324             }
325             
326             // Delay by approximately 2*i clock cycles (Pentium III).
327             YieldProcessorNormalizedForPreSkylakeCount(i);
328
329             // exponential backoff: wait a factor longer in the next iteration
330             i *= g_SpinConstants.dwBackoffFactor;
331         } while (i < g_SpinConstants.dwMaximumDuration);
332         
333         __SwitchToThread(0, CALLER_LIMITS_SPINNING);
334     }
335     // Stop spinning
336     
337     // Start waiting
338     for (;;)
339     {
340         DWORD dwFlag = m_dwFlag;
341
342         if (dwFlag == 0)
343         {   // No readers/writers in play, try to add a writer
344             if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
345             {
346                 break;
347             }
348         }
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);
352         }
353         else
354         {   // Try to add waiting writer and then wait for signal
355             if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag))
356             {
357                 m_pWriteWaiterEvent->Wait(INFINITE, FALSE);
358                 break;
359             }
360         }
361
362     }
363
364 WriteLockAcquired:
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");
367     EE_LOCK_TAKEN(this);
368     
369     return S_OK;
370 } // UTSemReadWrite::LockWrite
371
372
373
374 /******************************************************************************
375 Function : UTSemReadWrite::UnlockRead
376
377 Abstract: Release a shared lock
378 ******************************************************************************/
379 void UTSemReadWrite::UnlockRead()
380 {
381     CONTRACTL
382     {
383         NOTHROW;
384         GC_NOTRIGGER;
385     }
386     CONTRACTL_END;
387     
388     ULONG dwFlag;
389
390
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");
393
394     for (;;)
395     {
396         dwFlag = m_dwFlag;
397
398         if (dwFlag == READERS_INCR)
399         {        // we're the last reader, and nobody is waiting
400             if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
401             {
402                 break;
403             }
404         }
405
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))
409             {
410                 break;
411             }
412         }
413
414         else
415         {
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");
419
420             // one or more writers is waiting, do one of them next
421             // (remove a reader (us), remove a write waiter, add a writer
422             if (dwFlag == 
423                     InterlockedCompareExchangeT(
424                         &m_dwFlag, 
425                         dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR, 
426                         dwFlag))
427             {
428                 m_pWriteWaiterEvent->Set();
429                 break;
430             }
431         }
432     }
433
434     DecCantStopCount();
435     EE_LOCK_RELEASED(this);
436 } // UTSemReadWrite::UnlockRead
437
438
439 /******************************************************************************
440 Function : UTSemReadWrite::UnlockWrite
441
442 Abstract: Release an exclusive lock
443 ******************************************************************************/
444 void UTSemReadWrite::UnlockWrite()
445 {
446     CONTRACTL
447     {
448         NOTHROW;
449         GC_NOTRIGGER;
450     }
451     CONTRACTL_END;
452     
453     ULONG dwFlag;
454     ULONG count;
455     
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");
458     
459     for (;;)
460     {
461         dwFlag = m_dwFlag;
462
463         if (dwFlag == WRITERS_INCR)
464         {        // nobody is waiting
465             if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
466             {
467                 break;
468             }
469         }
470
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
475             if (dwFlag == 
476                     InterlockedCompareExchangeT(
477                         &m_dwFlag, 
478                         dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR, 
479                         dwFlag))
480             {
481                 m_pReadWaiterSemaphore->Release(count, NULL);
482                 break;
483             }
484         }
485
486         else
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))
491             {
492                 m_pWriteWaiterEvent->Set();
493                 break;
494             }
495         }
496     }
497
498     DecCantStopCount();
499     EE_LOCK_RELEASED(this);
500 } // UTSemReadWrite::UnlockWrite
501
502 #ifdef _DEBUG
503
504 //=======================================================================================
505 BOOL 
506 UTSemReadWrite::Debug_IsLockedForRead()
507 {
508     return ((m_dwFlag & READERS_MASK) != 0);
509 }
510
511 //=======================================================================================
512 BOOL 
513 UTSemReadWrite::Debug_IsLockedForWrite()
514 {
515     return ((m_dwFlag & WRITERS_MASK) != 0);
516 }
517
518 #endif //_DEBUG
519