[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / vm / synch.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
6 // 
7
8 #include "common.h"
9
10 #include "corhost.h"
11 #include "synch.h"
12
13 void CLREventBase::CreateAutoEvent (BOOL bInitialState  // If TRUE, initial state is signalled
14                                 )
15 {
16     CONTRACTL
17     {
18         THROWS;           
19         GC_NOTRIGGER;
20         // disallow creation of Crst before EE starts
21         // Can not assert here. ASP.NET uses our Threadpool before EE is started.
22         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));        
23         PRECONDITION((!IsOSEvent()));
24     }
25     CONTRACTL_END;
26
27     SetAutoEvent();
28
29     {
30         HANDLE h = WszCreateEvent(NULL,FALSE,bInitialState,NULL);
31         if (h == NULL) {
32             ThrowOutOfMemory();
33         }
34         m_handle = h;
35     }
36     
37 }
38
39 BOOL CLREventBase::CreateAutoEventNoThrow (BOOL bInitialState  // If TRUE, initial state is signalled
40                                 )
41 {
42     CONTRACTL
43     {
44         NOTHROW;
45         GC_NOTRIGGER;
46         // disallow creation of Crst before EE starts
47         // Can not assert here. ASP.NET uses our Threadpool before EE is started.
48         PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); 
49         PRECONDITION((!IsOSEvent()));
50     }
51     CONTRACTL_END;
52
53     EX_TRY
54     {
55         CreateAutoEvent(bInitialState);
56     }
57     EX_CATCH
58     {
59     }
60     EX_END_CATCH(SwallowAllExceptions);
61
62     return IsValid();
63 }
64
65 void CLREventBase::CreateManualEvent (BOOL bInitialState  // If TRUE, initial state is signalled
66                                 )
67 {
68     CONTRACTL
69     {
70         THROWS;           
71         GC_NOTRIGGER;
72         // disallow creation of Crst before EE starts
73         // Can not assert here. ASP.NET uses our Threadpool before EE is started.
74         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));        
75         PRECONDITION((!IsOSEvent()));
76     }
77     CONTRACTL_END;
78
79     {
80         HANDLE h = WszCreateEvent(NULL,TRUE,bInitialState,NULL);
81         if (h == NULL) {
82             ThrowOutOfMemory();
83         }
84         m_handle = h;
85     }
86 }
87
88 BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState  // If TRUE, initial state is signalled
89                                 )
90 {
91     CONTRACTL
92     {
93         NOTHROW;
94         GC_NOTRIGGER;
95         // disallow creation of Crst before EE starts
96         // Can not assert here. ASP.NET uses our Threadpool before EE is started.
97         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
98         PRECONDITION((!IsOSEvent()));
99     }
100     CONTRACTL_END;
101
102     EX_TRY
103     {
104         CreateManualEvent(bInitialState);
105     }
106     EX_CATCH
107     {
108     }
109     EX_END_CATCH(SwallowAllExceptions);
110
111     return IsValid();
112 }
113
114 void CLREventBase::CreateMonitorEvent(SIZE_T Cookie)
115 {
116     CONTRACTL
117     {
118         THROWS;
119         GC_NOTRIGGER;
120         // disallow creation of Crst before EE starts
121         PRECONDITION((g_fEEStarted));
122         PRECONDITION((GetThread() != NULL));
123         PRECONDITION((!IsOSEvent()));
124     }
125     CONTRACTL_END;
126
127     // thread-safe SetAutoEvent
128     FastInterlockOr(&m_dwFlags, CLREVENT_FLAGS_AUTO_EVENT);
129
130     {
131         HANDLE h = WszCreateEvent(NULL,FALSE,FALSE,NULL);
132         if (h == NULL) {
133             ThrowOutOfMemory();
134         }
135         if (FastInterlockCompareExchangePointer(&m_handle,
136                                                 h,
137                                                 INVALID_HANDLE_VALUE) != INVALID_HANDLE_VALUE)
138         {
139             // We lost the race
140             CloseHandle(h);
141         }
142     }
143     
144     // thread-safe SetInDeadlockDetection
145     FastInterlockOr(&m_dwFlags, CLREVENT_FLAGS_IN_DEADLOCK_DETECTION);
146
147     for (;;)
148     {
149         LONG oldFlags = m_dwFlags;
150
151         if (oldFlags & CLREVENT_FLAGS_MONITOREVENT_ALLOCATED)
152         {
153             // Other thread has set the flag already. Nothing left for us to do.
154             break;
155         }
156
157         LONG newFlags = oldFlags | CLREVENT_FLAGS_MONITOREVENT_ALLOCATED;
158         if (FastInterlockCompareExchange((LONG*)&m_dwFlags, newFlags, oldFlags) != oldFlags)
159         {
160             // We lost the race
161             continue;
162         }
163
164         // Because we set the allocated bit, we are the ones to do the signalling
165         if (oldFlags & CLREVENT_FLAGS_MONITOREVENT_SIGNALLED)
166         {
167             // We got the honour to signal the event
168             Set();
169         }
170         break;
171     }
172 }
173
174
175 void CLREventBase::SetMonitorEvent()
176 {
177     CONTRACTL
178     {
179         NOTHROW;
180         GC_NOTRIGGER;
181     }
182     CONTRACTL_END;
183
184     // SetMonitorEvent is robust against initialization races. It is possible to
185     // call CLREvent::SetMonitorEvent on event that has not been initialialized yet by CreateMonitorEvent.
186     // CreateMonitorEvent will signal the event once it is created if it happens.
187
188     for (;;)
189     {
190         LONG oldFlags = m_dwFlags;
191
192         if (oldFlags & CLREVENT_FLAGS_MONITOREVENT_ALLOCATED)
193         {
194             // Event has been allocated already. Use the regular codepath.
195             Set();
196             break;
197         }
198
199         LONG newFlags = oldFlags | CLREVENT_FLAGS_MONITOREVENT_SIGNALLED;
200         if (FastInterlockCompareExchange((LONG*)&m_dwFlags, newFlags, oldFlags) != oldFlags)
201         {
202             // We lost the race
203             continue;
204         }
205         break;
206     }
207 }
208
209
210
211 void CLREventBase::CreateOSAutoEvent (BOOL bInitialState  // If TRUE, initial state is signalled
212                                 )
213 {
214     CONTRACTL
215     {
216         THROWS;           
217         GC_NOTRIGGER;
218         // disallow creation of Crst before EE starts
219         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));        
220     }
221     CONTRACTL_END;
222
223     // Can not assert here. ASP.NET uses our Threadpool before EE is started.
224     //_ASSERTE (g_fEEStarted);
225
226     SetOSEvent();
227     SetAutoEvent();
228
229     HANDLE h = WszCreateEvent(NULL,FALSE,bInitialState,NULL);
230     if (h == NULL) {
231         ThrowOutOfMemory();
232     }
233     m_handle = h;
234 }
235
236 BOOL CLREventBase::CreateOSAutoEventNoThrow (BOOL bInitialState  // If TRUE, initial state is signalled
237                                 )
238 {
239     CONTRACTL
240     {
241         NOTHROW;
242         GC_NOTRIGGER;
243         // disallow creation of Crst before EE starts
244         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));        
245     }
246     CONTRACTL_END;
247
248     EX_TRY
249     {
250         CreateOSAutoEvent(bInitialState);
251     }
252     EX_CATCH
253     {
254     }
255     EX_END_CATCH(SwallowAllExceptions);
256
257     return IsValid();
258 }
259
260 void CLREventBase::CreateOSManualEvent (BOOL bInitialState  // If TRUE, initial state is signalled
261                                 )
262 {
263     CONTRACTL
264     {
265         THROWS;           
266         GC_NOTRIGGER;
267         // disallow creation of Crst before EE starts
268         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));        
269     }
270     CONTRACTL_END;
271
272     // Can not assert here. ASP.NET uses our Threadpool before EE is started.
273     //_ASSERTE (g_fEEStarted);
274
275     SetOSEvent();
276
277     HANDLE h = WszCreateEvent(NULL,TRUE,bInitialState,NULL);
278     if (h == NULL) {
279         ThrowOutOfMemory();
280     }
281     m_handle = h;
282 }
283
284 BOOL CLREventBase::CreateOSManualEventNoThrow (BOOL bInitialState  // If TRUE, initial state is signalled
285                                 )
286 {
287     CONTRACTL
288     {
289         NOTHROW; 
290         GC_NOTRIGGER;
291         // disallow creation of Crst before EE starts
292         PRECONDITION((m_handle == INVALID_HANDLE_VALUE));
293     }
294     CONTRACTL_END;
295
296     EX_TRY
297     {
298         CreateOSManualEvent(bInitialState);
299     }
300     EX_CATCH
301     {
302     }
303     EX_END_CATCH(SwallowAllExceptions);
304
305     return IsValid();
306 }
307
308 void CLREventBase::CloseEvent()
309 {
310     CONTRACTL
311     {
312       NOTHROW;
313       if (IsInDeadlockDetection()) {GC_TRIGGERS;} else {GC_NOTRIGGER;}
314     }
315     CONTRACTL_END;
316
317     GCX_MAYBE_PREEMP(IsInDeadlockDetection() && IsValid());
318
319     _ASSERTE(Thread::Debug_AllowCallout());
320
321     if (m_handle != INVALID_HANDLE_VALUE) {
322         {
323             CloseHandle(m_handle);
324         }
325
326         m_handle = INVALID_HANDLE_VALUE;
327     }
328     m_dwFlags = 0;
329 }
330
331
332 BOOL CLREventBase::Set()
333 {
334     CONTRACTL
335     {
336       NOTHROW;
337       GC_NOTRIGGER;
338       PRECONDITION((m_handle != INVALID_HANDLE_VALUE));
339     }
340     CONTRACTL_END;
341
342     _ASSERTE(Thread::Debug_AllowCallout());
343
344     {    
345         return SetEvent(m_handle);
346     }
347
348 }
349
350
351 BOOL CLREventBase::Reset()
352 {
353     CONTRACTL
354     {
355       NOTHROW;
356       GC_NOTRIGGER;
357       PRECONDITION((m_handle != INVALID_HANDLE_VALUE));
358     }
359     CONTRACTL_END;
360
361     _ASSERTE(Thread::Debug_AllowCallout());
362
363     // We do not allow Reset on AutoEvent
364     _ASSERTE (!IsAutoEvent() ||
365               !"Can not call Reset on AutoEvent");
366
367     {
368         return ResetEvent(m_handle);
369     }
370 }
371
372
373 static DWORD CLREventWaitHelper2(HANDLE handle, DWORD dwMilliseconds, BOOL alertable)
374 {
375     STATIC_CONTRACT_THROWS;
376
377     return WaitForSingleObjectEx(handle,dwMilliseconds,alertable);
378 }
379
380 static DWORD CLREventWaitHelper(HANDLE handle, DWORD dwMilliseconds, BOOL alertable)
381 {
382     STATIC_CONTRACT_NOTHROW;
383
384     struct Param
385     {
386         HANDLE handle;
387         DWORD dwMilliseconds;
388         BOOL alertable;
389         DWORD result;
390     } param;
391     param.handle = handle;
392     param.dwMilliseconds = dwMilliseconds;
393     param.alertable = alertable;
394     param.result = WAIT_FAILED;
395
396     // Can not use EX_TRY/CATCH.  EX_CATCH toggles GC mode.  This function is called
397     // through RareDisablePreemptiveGC.  EX_CATCH breaks profiler callback.
398     PAL_TRY(Param *, pParam, &param)
399     {
400         // Need to move to another helper (cannot have SEH and C++ destructors
401         // on automatic variables in one function)
402         pParam->result = CLREventWaitHelper2(pParam->handle, pParam->dwMilliseconds, pParam->alertable);
403     }
404     PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
405     {
406         param.result = WAIT_FAILED;
407     }
408     PAL_ENDTRY;
409
410     return param.result;
411 }
412
413
414 DWORD CLREventBase::Wait(DWORD dwMilliseconds, BOOL alertable, PendingSync *syncState) 
415 {
416     WRAPPER_NO_CONTRACT;
417     return WaitEx(dwMilliseconds, alertable?WaitMode_Alertable:WaitMode_None,syncState);
418 }
419
420
421 DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode, PendingSync *syncState) 
422 {
423     BOOL alertable = (mode & WaitMode_Alertable)!=0;
424     CONTRACTL
425     {
426         if (alertable)
427         {
428             THROWS;               // Thread::DoAppropriateWait can throw   
429         }
430         else
431         {
432             NOTHROW;
433         }
434         if (GetThread())
435         {
436             if (alertable)
437                 GC_TRIGGERS;
438             else 
439                 GC_NOTRIGGER;
440         }
441         else
442         {
443             DISABLED(GC_TRIGGERS);        
444         }
445         PRECONDITION(m_handle != INVALID_HANDLE_VALUE); // Handle has to be valid
446     }
447     CONTRACTL_END;
448
449
450     _ASSERTE(Thread::Debug_AllowCallout());
451
452     Thread * pThread = GetThread();    
453     
454 #ifdef _DEBUG
455     // If a CLREvent is OS event only, we can not wait for the event on a managed thread
456     if (IsOSEvent())
457         _ASSERTE (pThread == NULL);
458 #endif
459     _ASSERTE((pThread != NULL) || !g_fEEStarted || dbgOnly_IsSpecialEEThread());
460
461     {
462         if (pThread && alertable) {
463             DWORD dwRet = WAIT_FAILED;
464             dwRet = pThread->DoAppropriateWait(1, &m_handle, FALSE, dwMilliseconds, 
465                                               mode, 
466                                               syncState);
467             return dwRet;
468         }
469         else {
470             _ASSERTE (syncState == NULL);
471             return CLREventWaitHelper(m_handle,dwMilliseconds,alertable);
472         }
473     }
474 }
475
476 void CLRSemaphore::Create (DWORD dwInitial, DWORD dwMax)
477 {
478     CONTRACTL
479     {
480       THROWS;
481       GC_NOTRIGGER;
482       PRECONDITION(m_handle == INVALID_HANDLE_VALUE);
483     }
484     CONTRACTL_END;
485
486     {
487         HANDLE h = WszCreateSemaphore(NULL,dwInitial,dwMax,NULL);
488         if (h == NULL) {
489             ThrowOutOfMemory();
490         }
491         m_handle = h;
492     }
493 }
494
495
496 void CLRSemaphore::Close()
497 {
498     LIMITED_METHOD_CONTRACT;
499
500     if (m_handle != INVALID_HANDLE_VALUE) {
501         CloseHandle(m_handle);
502         m_handle = INVALID_HANDLE_VALUE;
503     }
504 }
505
506 BOOL CLRSemaphore::Release(LONG lReleaseCount, LONG *lpPreviousCount)
507 {
508     CONTRACTL
509     {
510       NOTHROW;
511       GC_NOTRIGGER;
512       PRECONDITION(m_handle != INVALID_HANDLE_VALUE);
513     }
514     CONTRACTL_END;
515
516     {
517         return ::ReleaseSemaphore(m_handle, lReleaseCount, lpPreviousCount);
518     }
519 }
520
521
522 DWORD CLRSemaphore::Wait(DWORD dwMilliseconds, BOOL alertable)
523 {
524     CONTRACTL
525     {
526         if (GetThread() && alertable)
527         {
528             THROWS;               // Thread::DoAppropriateWait can throw       
529         }
530         else
531         {
532             NOTHROW;
533         }
534         if (GetThread())
535         {
536             if (alertable)
537                 GC_TRIGGERS;
538             else 
539                 GC_NOTRIGGER;
540         }
541         else
542         {
543             DISABLED(GC_TRIGGERS);        
544         }
545         PRECONDITION(m_handle != INVALID_HANDLE_VALUE); // Invalid to have invalid handle
546     }
547     CONTRACTL_END;
548
549     
550     Thread *pThread = GetThread();
551     _ASSERTE (pThread || !g_fEEStarted || dbgOnly_IsSpecialEEThread());
552
553     {
554         // TODO wwl: if alertable is FALSE, do we support a host to break a deadlock?
555         // Currently we can not call through DoAppropriateWait because of CannotThrowComplusException.
556         // We should re-consider this after our code is exception safe.
557         if (pThread && alertable) {
558             return pThread->DoAppropriateWait(1, &m_handle, FALSE, dwMilliseconds, 
559                                               alertable?WaitMode_Alertable:WaitMode_None,
560                                               NULL);
561         }
562         else {
563             DWORD result = WAIT_FAILED;
564             EX_TRY
565             {
566                 result = WaitForSingleObjectEx(m_handle,dwMilliseconds,alertable);
567             }
568             EX_CATCH
569             {
570                 result = WAIT_FAILED;
571             }
572             EX_END_CATCH(SwallowAllExceptions);
573             return result;
574         }
575     }
576 }
577
578 void CLRLifoSemaphore::Create(INT32 initialSignalCount, INT32 maximumSignalCount)
579 {
580     CONTRACTL
581     {
582         THROWS;
583         GC_NOTRIGGER;
584     }
585     CONTRACTL_END;
586
587     _ASSERTE(maximumSignalCount > 0);
588     _ASSERTE(initialSignalCount <= maximumSignalCount);
589     _ASSERTE(m_handle == nullptr);
590
591 #ifdef FEATURE_PAL
592     HANDLE h = WszCreateSemaphore(nullptr, 0, maximumSignalCount, nullptr);
593 #else // !FEATURE_PAL
594     HANDLE h = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, maximumSignalCount);
595 #endif // FEATURE_PAL
596     if (h == nullptr)
597     {
598         ThrowOutOfMemory();
599     }
600
601     m_handle = h;
602     m_counts.signalCount = initialSignalCount;
603     INDEBUG(m_maximumSignalCount = maximumSignalCount);
604 }
605
606 void CLRLifoSemaphore::Close()
607 {
608     LIMITED_METHOD_CONTRACT;
609
610     if (m_handle == nullptr)
611     {
612         return;
613     }
614
615     CloseHandle(m_handle);
616     m_handle = nullptr;
617 }
618
619 bool CLRLifoSemaphore::WaitForSignal(DWORD timeoutMs)
620 {
621     CONTRACTL
622     {
623         NOTHROW;
624         GC_NOTRIGGER;
625     }
626     CONTRACTL_END;
627
628     _ASSERTE(timeoutMs != 0);
629     _ASSERTE(m_handle != nullptr);
630     _ASSERTE(m_counts.VolatileLoadWithoutBarrier().waiterCount != (UINT16)0);
631
632     while (true)
633     {
634         // Wait for a signal
635         BOOL waitSuccessful;
636         {
637 #ifdef FEATURE_PAL
638             // Do a prioritized wait to get LIFO waiter release order
639             DWORD waitResult = PAL_WaitForSingleObjectPrioritized(m_handle, timeoutMs);
640             _ASSERTE(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_TIMEOUT);
641             waitSuccessful = waitResult == WAIT_OBJECT_0;
642 #else // !FEATURE_PAL
643             // I/O completion ports release waiters in LIFO order, see
644             // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx
645             DWORD numberOfBytes;
646             ULONG_PTR completionKey;
647             LPOVERLAPPED overlapped;
648             waitSuccessful = GetQueuedCompletionStatus(m_handle, &numberOfBytes, &completionKey, &overlapped, timeoutMs);
649             _ASSERTE(waitSuccessful || GetLastError() == WAIT_TIMEOUT);
650             _ASSERTE(overlapped == nullptr);
651 #endif // FEATURE_PAL
652         }
653
654         if (!waitSuccessful)
655         {
656             // Unregister the waiter. The wait subsystem used above guarantees that a thread that wakes due to a timeout does
657             // not observe a signal to the object being waited upon.
658             Counts toSubtract;
659             ++toSubtract.waiterCount;
660             Counts countsBeforeUpdate = m_counts.ExchangeAdd(-toSubtract);
661             _ASSERTE(countsBeforeUpdate.waiterCount != (UINT16)0);
662             return false;
663         }
664
665         // Unregister the waiter if this thread will not be waiting anymore, and try to acquire the semaphore
666         Counts counts = m_counts.VolatileLoadWithoutBarrier();
667         while (true)
668         {
669             _ASSERTE(counts.waiterCount != (UINT16)0);
670             Counts newCounts = counts;
671             if (counts.signalCount != 0)
672             {
673                 --newCounts.signalCount;
674                 --newCounts.waiterCount;
675             }
676
677             // This waiter has woken up and this needs to be reflected in the count of waiters signaled to wake
678             if (counts.countOfWaitersSignaledToWake != (UINT8)0)
679             {
680                 --newCounts.countOfWaitersSignaledToWake;
681             }
682
683             Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
684             if (countsBeforeUpdate == counts)
685             {
686                 if (counts.signalCount != 0)
687                 {
688                     return true;
689                 }
690                 break;
691             }
692
693             counts = countsBeforeUpdate;
694         }
695     }
696 }
697
698 bool CLRLifoSemaphore::Wait(DWORD timeoutMs)
699 {
700     WRAPPER_NO_CONTRACT;
701
702     _ASSERTE(m_handle != nullptr);
703
704     // Acquire the semaphore or register as a waiter
705     Counts counts = m_counts.VolatileLoadWithoutBarrier();
706     while (true)
707     {
708         _ASSERTE(counts.signalCount <= m_maximumSignalCount);
709         Counts newCounts = counts;
710         if (counts.signalCount != 0)
711         {
712             --newCounts.signalCount;
713         }
714         else if (timeoutMs != 0)
715         {
716             ++newCounts.waiterCount;
717             _ASSERTE(newCounts.waiterCount != (UINT16)0); // overflow check, this many waiters is currently not supported
718         }
719
720         Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
721         if (countsBeforeUpdate == counts)
722         {
723             return counts.signalCount != 0 || (timeoutMs != 0 && WaitForSignal(timeoutMs));
724         }
725
726         counts = countsBeforeUpdate;
727     }
728 }
729
730 bool CLRLifoSemaphore::Wait(DWORD timeoutMs, UINT32 spinCount, UINT32 processorCount)
731 {
732     CONTRACTL
733     {
734         NOTHROW;
735         GC_NOTRIGGER;
736     }
737     CONTRACTL_END;
738
739     _ASSERTE(m_handle != nullptr);
740
741     if (timeoutMs == 0 || spinCount == 0)
742     {
743         return Wait(timeoutMs);
744     }
745
746     // Try to acquire the semaphore or register as a spinner
747     Counts counts = m_counts.VolatileLoadWithoutBarrier();
748     while (true)
749     {
750         Counts newCounts = counts;
751         if (counts.signalCount != 0)
752         {
753             --newCounts.signalCount;
754         }
755         else
756         {
757             ++newCounts.spinnerCount;
758             if (newCounts.spinnerCount == (UINT8)0)
759             {
760                 // Maximum number of spinners reached, register as a waiter instead
761                 --newCounts.spinnerCount;
762                 ++newCounts.waiterCount;
763                 _ASSERTE(newCounts.waiterCount != (UINT16)0); // overflow check, this many waiters is currently not supported
764             }
765         }
766
767         Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
768         if (countsBeforeUpdate == counts)
769         {
770             if (counts.signalCount != 0)
771             {
772                 return true;
773             }
774             if (newCounts.waiterCount != counts.waiterCount)
775             {
776                 return WaitForSignal(timeoutMs);
777             }
778             break;
779         }
780
781         counts = countsBeforeUpdate;
782     }
783
784 #ifdef _TARGET_ARM64_
785     // For now, the spinning changes are disabled on ARM64. The spin loop below replicates how UnfairSemaphore used to spin.
786     // Once more tuning is done on ARM64, it should be possible to come up with a spinning scheme that works well everywhere.
787     int spinCountPerProcessor = spinCount;
788     for (UINT32 i = 1; ; ++i)
789     {
790         // Wait
791         ClrSleepEx(0, false);
792
793         // Try to acquire the semaphore and unregister as a spinner
794         counts = m_counts.VolatileLoadWithoutBarrier();
795         while (true)
796         {
797             _ASSERTE(counts.spinnerCount != (UINT8)0);
798             if (counts.signalCount == 0)
799             {
800                 break;
801             }
802
803             Counts newCounts = counts;
804             --newCounts.signalCount;
805             --newCounts.spinnerCount;
806
807             Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
808             if (countsBeforeUpdate == counts)
809             {
810                 return true;
811             }
812
813             counts = countsBeforeUpdate;
814         }
815
816         // Determine whether to spin further
817         double spinnersPerProcessor = (double)counts.spinnerCount / processorCount;
818         UINT32 spinLimit = (UINT32)(spinCountPerProcessor / spinnersPerProcessor + 0.5);
819         if (i >= spinLimit)
820         {
821             break;
822         }
823     }
824 #else // !_TARGET_ARM64_
825     const UINT32 Sleep0Threshold = 10;
826     YieldProcessorNormalizationInfo normalizationInfo;
827 #ifdef FEATURE_PAL
828     // The PAL's wait subsystem is quite slow, spin more to compensate for the more expensive wait
829     spinCount *= 2;
830 #endif // FEATURE_PAL
831     for (UINT32 i = 0; i < spinCount; ++i)
832     {
833         // Wait
834         //
835         // (i - Sleep0Threshold) % 2 != 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with
836         // Thread.SpinWait. Otherwise, the following issues occur:
837         //   - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a
838         //     busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state. Completing the
839         //     spin loop too early can cause excessive context switcing from the wait.
840         //   - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to contention),
841         //     they may switch between one another, delaying work that can make progress.
842         if (i < Sleep0Threshold || (i - Sleep0Threshold) % 2 != 0)
843         {
844             YieldProcessorWithBackOffNormalized(normalizationInfo, i);
845         }
846         else
847         {
848             // Not doing SwitchToThread(), it does not seem to have any benefit over Sleep(0)
849             ClrSleepEx(0, false);
850         }
851
852         // Try to acquire the semaphore and unregister as a spinner
853         counts = m_counts.VolatileLoadWithoutBarrier();
854         while (true)
855         {
856             _ASSERTE(counts.spinnerCount != (UINT8)0);
857             if (counts.signalCount == 0)
858             {
859                 break;
860             }
861
862             Counts newCounts = counts;
863             --newCounts.signalCount;
864             --newCounts.spinnerCount;
865
866             Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
867             if (countsBeforeUpdate == counts)
868             {
869                 return true;
870             }
871
872             counts = countsBeforeUpdate;
873         }
874     }
875 #endif // _TARGET_ARM64_
876
877     // Unregister as a spinner, and acquire the semaphore or register as a waiter
878     counts = m_counts.VolatileLoadWithoutBarrier();
879     while (true)
880     {
881         _ASSERTE(counts.spinnerCount != (UINT8)0);
882         Counts newCounts = counts;
883         --newCounts.spinnerCount;
884         if (counts.signalCount != 0)
885         {
886             --newCounts.signalCount;
887         }
888         else
889         {
890             ++newCounts.waiterCount;
891             _ASSERTE(newCounts.waiterCount != (UINT16)0); // overflow check, this many waiters is currently not supported
892         }
893
894         Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
895         if (countsBeforeUpdate == counts)
896         {
897             return counts.signalCount != 0 || WaitForSignal(timeoutMs);
898         }
899
900         counts = countsBeforeUpdate;
901     }
902 }
903
904 void CLRLifoSemaphore::Release(INT32 releaseCount)
905 {
906     CONTRACTL
907     {
908         NOTHROW;
909         GC_NOTRIGGER;
910     }
911     CONTRACTL_END;
912
913     _ASSERTE(releaseCount > 0);
914     _ASSERTE((UINT32)releaseCount <= m_maximumSignalCount);
915     _ASSERTE(m_handle != INVALID_HANDLE_VALUE);
916
917     INT32 countOfWaitersToWake;
918     Counts counts = m_counts.VolatileLoadWithoutBarrier();
919     while (true)
920     {
921         Counts newCounts = counts;
922
923         // Increase the signal count. The addition doesn't overflow because of the limit on the max signal count in Create.
924         newCounts.signalCount += releaseCount;
925         _ASSERTE(newCounts.signalCount > counts.signalCount);
926
927         // Determine how many waiters to wake, taking into account how many spinners and waiters there are and how many waiters
928         // have previously been signaled to wake but have not yet woken
929         countOfWaitersToWake =
930             (INT32)min(newCounts.signalCount, (UINT32)newCounts.waiterCount + newCounts.spinnerCount) -
931             newCounts.spinnerCount -
932             newCounts.countOfWaitersSignaledToWake;
933         if (countOfWaitersToWake > 0)
934         {
935             // Ideally, limiting to a maximum of releaseCount would not be necessary and could be an assert instead, but since
936             // WaitForSignal() does not have enough information to tell whether a woken thread was signaled, and due to the cap
937             // below, it's possible for countOfWaitersSignaledToWake to be less than the number of threads that have actually
938             // been signaled to wake.
939             if (countOfWaitersToWake > releaseCount)
940             {
941                 countOfWaitersToWake = releaseCount;
942             }
943
944             // Cap countOfWaitersSignaledToWake to its max value. It's ok to ignore some woken threads in this count, it just
945             // means some more threads will be woken next time. Typically, it won't reach the max anyway.
946             newCounts.countOfWaitersSignaledToWake += (UINT8)min(countOfWaitersToWake, (INT32)UINT8_MAX);
947             if (newCounts.countOfWaitersSignaledToWake <= counts.countOfWaitersSignaledToWake)
948             {
949                 newCounts.countOfWaitersSignaledToWake = UINT8_MAX;
950             }
951         }
952
953         Counts countsBeforeUpdate = m_counts.CompareExchange(newCounts, counts);
954         if (countsBeforeUpdate == counts)
955         {
956             _ASSERTE((UINT32)releaseCount <= m_maximumSignalCount - counts.signalCount);
957             if (countOfWaitersToWake <= 0)
958             {
959                 return;
960             }
961             break;
962         }
963
964         counts = countsBeforeUpdate;
965     }
966
967     // Wake waiters
968 #ifdef FEATURE_PAL
969     BOOL released = ReleaseSemaphore(m_handle, countOfWaitersToWake, nullptr);
970     _ASSERTE(released);
971 #else // !FEATURE_PAL
972     while (--countOfWaitersToWake >= 0)
973     {
974         while (!PostQueuedCompletionStatus(m_handle, 0, 0, nullptr))
975         {
976             // Probably out of memory. It's not valid to stop and throw here, so try again after a delay.
977             ClrSleepEx(1, false);
978         }
979     }
980 #endif // FEATURE_PAL
981 }
982
983 void CLRMutex::Create(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName)
984 {
985     CONTRACTL
986     {
987         THROWS;
988         GC_NOTRIGGER;
989         PRECONDITION(m_handle == INVALID_HANDLE_VALUE && m_handle != NULL);
990     }
991     CONTRACTL_END;
992
993     m_handle = WszCreateMutex(lpMutexAttributes,bInitialOwner,lpName);
994     if (m_handle == NULL)
995     {
996         ThrowOutOfMemory();
997     }
998 }
999
1000 void CLRMutex::Close()
1001 {
1002     LIMITED_METHOD_CONTRACT;
1003
1004     if (m_handle != INVALID_HANDLE_VALUE)
1005     {
1006         CloseHandle(m_handle);
1007         m_handle = INVALID_HANDLE_VALUE;
1008     }
1009 }
1010
1011 BOOL CLRMutex::Release()
1012 {
1013     CONTRACTL
1014     {
1015       NOTHROW;
1016       GC_NOTRIGGER;
1017       PRECONDITION(m_handle != INVALID_HANDLE_VALUE && m_handle != NULL);
1018     }
1019     CONTRACTL_END;
1020
1021     BOOL fRet = ReleaseMutex(m_handle);
1022     if (fRet)
1023     {
1024         EE_LOCK_RELEASED(this);
1025     }
1026     return fRet;
1027 }
1028
1029 DWORD CLRMutex::Wait(DWORD dwMilliseconds, BOOL bAlertable)
1030 {
1031     CONTRACTL {
1032         NOTHROW;
1033         GC_NOTRIGGER;
1034         CAN_TAKE_LOCK;
1035         PRECONDITION(m_handle != INVALID_HANDLE_VALUE && m_handle != NULL);
1036     }
1037     CONTRACTL_END;
1038
1039     DWORD fRet = WaitForSingleObjectEx(m_handle, dwMilliseconds, bAlertable);
1040
1041     if (fRet == WAIT_OBJECT_0)
1042     {
1043         EE_LOCK_TAKEN(this);
1044     }
1045
1046     return fRet;
1047 }