@@ -22,7 +22,7 @@ public sealed partial class Lock
22
22
private const short DefaultMaxSpinCount = 22 ;
23
23
private const short DefaultAdaptiveSpinPeriod = 100 ;
24
24
private const short SpinSleep0Threshold = 10 ;
25
- private const int MaxDurationMsForPreemptingWaiters = 100 ;
25
+ private const ushort MaxDurationMsForPreemptingWaiters = 100 ;
26
26
27
27
private static long s_contentionCount ;
28
28
@@ -38,10 +38,7 @@ public sealed partial class Lock
38
38
private uint _state ; // see State for layout
39
39
private uint _recursionCount ;
40
40
private short _spinCount ;
41
-
42
- // The lowest bit is a flag, when set it indicates that the lock should use trivial waits
43
- private ushort _waiterStartTimeMsAndFlags ;
44
-
41
+ private ushort _waiterStartTimeMs ;
45
42
private AutoResetEvent ? _waitEvent ;
46
43
47
44
#if NATIVEAOT // The method needs to be public in NativeAOT so that other private libraries can access it
@@ -51,10 +48,7 @@ internal Lock(bool useTrivialWaits)
51
48
#endif
52
49
: this ( )
53
50
{
54
- if ( useTrivialWaits )
55
- {
56
- _waiterStartTimeMsAndFlags = 1 ;
57
- }
51
+ State . InitializeUseTrivialWaits ( this , useTrivialWaits ) ;
58
52
}
59
53
60
54
/// <summary>
@@ -488,7 +482,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
488
482
int remainingTimeoutMs = timeoutMs ;
489
483
while ( true )
490
484
{
491
- if ( ! waitEvent . WaitOneNoCheck ( remainingTimeoutMs , UseTrivialWaits ) )
485
+ if ( ! waitEvent . WaitOneNoCheck ( remainingTimeoutMs , new State ( this ) . UseTrivialWaits ) )
492
486
{
493
487
break ;
494
488
}
@@ -567,43 +561,18 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
567
561
return new ThreadId ( 0 ) ;
568
562
}
569
563
570
- // Trivial waits are:
571
- // - Not interruptible by Thread.Interrupt
572
- // - Don't allow reentrance through APCs or message pumping
573
- // - Not forwarded to SynchronizationContext wait overrides
574
- private bool UseTrivialWaits => ( _waiterStartTimeMsAndFlags & 1 ) != 0 ;
575
-
576
- // The stored waiter start time (ms) only includes the lower 15 bits since the field is also used for a flag. This mask
577
- // should be used when recording and comparing waiter times.
578
- private const int WaiterTimeMsMask = 0x7fff ;
579
-
580
- // Only the lower 15 bits are stored/retrieved. Callers should use WaiterTimeMsMask when recording and comparing waiter
581
- // times.
582
- private int WaiterStartTimeMs
583
- {
584
- get => _waiterStartTimeMsAndFlags >> 1 ;
585
- set
586
- {
587
- Debug . Assert ( ( value & WaiterTimeMsMask ) == value ) ;
588
- _waiterStartTimeMsAndFlags = ( ushort ) ( ( value << 1 ) | ( _waiterStartTimeMsAndFlags & 1 ) ) ;
589
- }
590
- }
591
-
592
- private void ResetWaiterStartTime ( ) => WaiterStartTimeMs = 0 ;
564
+ private void ResetWaiterStartTime ( ) => _waiterStartTimeMs = 0 ;
593
565
594
566
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
595
567
private void RecordWaiterStartTime ( )
596
568
{
597
- int currentTimeMs = Environment . TickCount & WaiterTimeMsMask ;
598
-
599
- // Don't record zero, that value is reserved for indicating that a time is not recorded
569
+ ushort currentTimeMs = ( ushort ) Environment . TickCount ;
600
570
if ( currentTimeMs == 0 )
601
571
{
602
- currentTimeMs = ( currentTimeMs - 1 ) & WaiterTimeMsMask ;
572
+ // Don't record zero, that value is reserved for indicating that a time is not recorded
573
+ currentTimeMs -- ;
603
574
}
604
-
605
- Debug . Assert ( currentTimeMs != 0 ) ;
606
- WaiterStartTimeMs = currentTimeMs ;
575
+ _waiterStartTimeMs = currentTimeMs ;
607
576
}
608
577
609
578
private bool ShouldStopPreemptingWaiters
@@ -612,10 +581,10 @@ private bool ShouldStopPreemptingWaiters
612
581
get
613
582
{
614
583
// If the recorded time is zero, a time has not been recorded yet
615
- int waiterStartTimeMs = WaiterStartTimeMs ;
584
+ ushort waiterStartTimeMs = _waiterStartTimeMs ;
616
585
return
617
586
waiterStartTimeMs != 0 &&
618
- ( ( Environment . TickCount - waiterStartTimeMs ) & WaiterTimeMsMask ) >= MaxDurationMsForPreemptingWaiters ;
587
+ ( ushort ) ( Environment . TickCount - waiterStartTimeMs ) >= MaxDurationMsForPreemptingWaiters ;
619
588
}
620
589
}
621
590
@@ -720,8 +689,8 @@ private struct State : IEquatable<State>
720
689
private const uint SpinnerCountIncrement = ( uint ) 1 << 2 ; // bits 2-4
721
690
private const uint SpinnerCountMask = ( uint ) 0x7 << 2 ;
722
691
private const uint IsWaiterSignaledToWakeMask = ( uint ) 1 << 5 ; // bit 5
723
- private const byte WaiterCountShift = 6 ;
724
- private const uint WaiterCountIncrement = ( uint ) 1 << WaiterCountShift ; // bits 6 -31
692
+ private const uint UseTrivialWaitsMask = ( uint ) 1 << 6 ; // bit 6
693
+ private const uint WaiterCountIncrement = ( uint ) 1 << 7 ; // bits 7 -31
725
694
726
695
private uint _state ;
727
696
@@ -800,6 +769,22 @@ private void ClearIsWaiterSignaledToWake()
800
769
_state -= IsWaiterSignaledToWakeMask ;
801
770
}
802
771
772
+ // Trivial waits are:
773
+ // - Not interruptible by Thread.Interrupt
774
+ // - Don't allow reentrance through APCs or message pumping
775
+ // - Not forwarded to SynchronizationContext wait overrides
776
+ public bool UseTrivialWaits => ( _state & UseTrivialWaitsMask ) != 0 ;
777
+
778
+ public static void InitializeUseTrivialWaits ( Lock lockObj , bool useTrivialWaits )
779
+ {
780
+ Debug . Assert ( lockObj . _state == 0 ) ;
781
+
782
+ if ( useTrivialWaits )
783
+ {
784
+ lockObj . _state = UseTrivialWaitsMask ;
785
+ }
786
+ }
787
+
803
788
public bool HasAnyWaiters => _state >= WaiterCountIncrement ;
804
789
805
790
private bool TryIncrementWaiterCount ( )
0 commit comments