@@ -37,7 +37,15 @@ public sealed partial class Lock
3737
3838 private uint _state ; // see State for layout
3939 private uint _recursionCount ;
40+
41+ // This field serves a few purposes currently:
42+ // - When positive, it indicates the number of spin-wait iterations that most threads would do upon contention
43+ // - When zero, it indicates that spin-waiting is to be attempted by a thread to test if it is successful
44+ // - When negative, it serves as a rough counter for contentions that would increment it towards zero
45+ //
46+ // See references to this field and "AdaptiveSpin" in TryEnterSlow for more information.
4047 private short _spinCount ;
48+
4149 private ushort _waiterStartTimeMs ;
4250 private AutoResetEvent ? _waitEvent ;
4351
@@ -297,7 +305,7 @@ private void ExitImpl()
297305 }
298306 }
299307
300- private static bool IsAdaptiveSpinEnabled ( short minSpinCount ) => minSpinCount <= 0 ;
308+ private static bool IsAdaptiveSpinEnabled ( short minSpinCountForAdaptiveSpin ) => minSpinCountForAdaptiveSpin <= 0 ;
301309
302310 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
303311 private ThreadId TryEnterSlow ( int timeoutMs , ThreadId currentThreadId )
@@ -350,20 +358,19 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
350358 goto Locked ;
351359 }
352360
353- bool isSingleProcessor = IsSingleProcessor ;
354361 short maxSpinCount = s_maxSpinCount ;
355362 if ( maxSpinCount == 0 )
356363 {
357364 goto Wait ;
358365 }
359366
360- short minSpinCount = s_minSpinCount ;
367+ short minSpinCountForAdaptiveSpin = s_minSpinCountForAdaptiveSpin ;
361368 short spinCount = _spinCount ;
362369 if ( spinCount < 0 )
363370 {
364371 // When negative, the spin count serves as a counter for contentions such that a spin-wait can be attempted
365372 // periodically to see if it would be beneficial. Increment the spin count and skip spin-waiting.
366- Debug . Assert ( IsAdaptiveSpinEnabled ( minSpinCount ) ) ;
373+ Debug . Assert ( IsAdaptiveSpinEnabled ( minSpinCountForAdaptiveSpin ) ) ;
367374 _spinCount = ( short ) ( spinCount + 1 ) ;
368375 goto Wait ;
369376 }
@@ -388,7 +395,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
388395
389396 for ( short spinIndex = 0 ; ; )
390397 {
391- LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor ) ;
398+ LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor : false ) ;
392399
393400 if ( ++ spinIndex >= spinCount )
394401 {
@@ -405,7 +412,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
405412
406413 if ( tryLockResult == TryLockResult . Locked )
407414 {
408- if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCount ) )
415+ if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCountForAdaptiveSpin ) )
409416 {
410417 // Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the
411418 // spin count more balanced, only the first spinner adjusts the spin count
@@ -426,7 +433,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
426433
427434 // Unregister the spinner and try to acquire the lock
428435 tryLockResult = State . TryLockAfterSpinLoop ( this ) ;
429- if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCount ) )
436+ if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCountForAdaptiveSpin ) )
430437 {
431438 // Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the
432439 // spin count more balanced, only the first spinner adjusts the spin count
@@ -444,7 +451,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
444451 // number of contentions, the first spinner will attempt a spin-wait again to see if it is effective.
445452 Debug . Assert ( tryLockResult == TryLockResult . Wait ) ;
446453 spinCount = _spinCount ;
447- _spinCount = spinCount > 0 ? ( short ) ( spinCount - 1 ) : minSpinCount ;
454+ _spinCount = spinCount > 0 ? ( short ) ( spinCount - 1 ) : minSpinCountForAdaptiveSpin ;
448455 }
449456 }
450457
@@ -517,7 +524,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
517524 break ;
518525 }
519526
520- LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor ) ;
527+ LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor : false ) ;
521528 }
522529
523530 if ( acquiredLock )
@@ -661,14 +668,25 @@ internal nint LockIdForEvents
661668
662669 internal ulong OwningThreadId => _owningThreadId ;
663670
664- private static short DetermineMaxSpinCount ( ) =>
665- AppContextConfigHelper . GetInt16Config (
666- "System.Threading.Lock.SpinCount" ,
667- "DOTNET_Lock_SpinCount" ,
668- DefaultMaxSpinCount ,
669- allowNegative : false ) ;
671+ private static short DetermineMaxSpinCount ( )
672+ {
673+ if ( IsSingleProcessor )
674+ {
675+ return 0 ;
676+ }
677+
678+ return
679+ AppContextConfigHelper . GetInt16Config (
680+ "System.Threading.Lock.SpinCount" ,
681+ "DOTNET_Lock_SpinCount" ,
682+ DefaultMaxSpinCount ,
683+ allowNegative : false ) ;
684+ }
670685
671- private static short DetermineMinSpinCount ( )
686+ // When the returned value is zero or negative, indicates the lowest value that the _spinCount field will have when
687+ // adaptive spin chooses to pause spin-waiting, see the comment on the _spinCount field for more information. When the
688+ // returned value is positive, adaptive spin is disabled.
689+ private static short DetermineMinSpinCountForAdaptiveSpin ( )
672690 {
673691 // The config var can be set to -1 to disable adaptive spin
674692 short adaptiveSpinPeriod =
0 commit comments