@@ -37,7 +37,15 @@ public sealed partial class Lock
37
37
38
38
private uint _state ; // see State for layout
39
39
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.
40
47
private short _spinCount ;
48
+
41
49
private ushort _waiterStartTimeMs ;
42
50
private AutoResetEvent ? _waitEvent ;
43
51
@@ -297,7 +305,7 @@ private void ExitImpl()
297
305
}
298
306
}
299
307
300
- private static bool IsAdaptiveSpinEnabled ( short minSpinCount ) => minSpinCount <= 0 ;
308
+ private static bool IsAdaptiveSpinEnabled ( short minSpinCountForAdaptiveSpin ) => minSpinCountForAdaptiveSpin <= 0 ;
301
309
302
310
[ MethodImpl ( MethodImplOptions . NoInlining ) ]
303
311
private ThreadId TryEnterSlow ( int timeoutMs , ThreadId currentThreadId )
@@ -350,20 +358,19 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
350
358
goto Locked ;
351
359
}
352
360
353
- bool isSingleProcessor = IsSingleProcessor ;
354
361
short maxSpinCount = s_maxSpinCount ;
355
362
if ( maxSpinCount == 0 )
356
363
{
357
364
goto Wait ;
358
365
}
359
366
360
- short minSpinCount = s_minSpinCount ;
367
+ short minSpinCountForAdaptiveSpin = s_minSpinCountForAdaptiveSpin ;
361
368
short spinCount = _spinCount ;
362
369
if ( spinCount < 0 )
363
370
{
364
371
// When negative, the spin count serves as a counter for contentions such that a spin-wait can be attempted
365
372
// 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 ) ) ;
367
374
_spinCount = ( short ) ( spinCount + 1 ) ;
368
375
goto Wait ;
369
376
}
@@ -388,7 +395,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
388
395
389
396
for ( short spinIndex = 0 ; ; )
390
397
{
391
- LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor ) ;
398
+ LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor : false ) ;
392
399
393
400
if ( ++ spinIndex >= spinCount )
394
401
{
@@ -405,7 +412,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
405
412
406
413
if ( tryLockResult == TryLockResult . Locked )
407
414
{
408
- if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCount ) )
415
+ if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCountForAdaptiveSpin ) )
409
416
{
410
417
// Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the
411
418
// spin count more balanced, only the first spinner adjusts the spin count
@@ -426,7 +433,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
426
433
427
434
// Unregister the spinner and try to acquire the lock
428
435
tryLockResult = State . TryLockAfterSpinLoop ( this ) ;
429
- if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCount ) )
436
+ if ( isFirstSpinner && IsAdaptiveSpinEnabled ( minSpinCountForAdaptiveSpin ) )
430
437
{
431
438
// Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the
432
439
// spin count more balanced, only the first spinner adjusts the spin count
@@ -444,7 +451,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
444
451
// number of contentions, the first spinner will attempt a spin-wait again to see if it is effective.
445
452
Debug . Assert ( tryLockResult == TryLockResult . Wait ) ;
446
453
spinCount = _spinCount ;
447
- _spinCount = spinCount > 0 ? ( short ) ( spinCount - 1 ) : minSpinCount ;
454
+ _spinCount = spinCount > 0 ? ( short ) ( spinCount - 1 ) : minSpinCountForAdaptiveSpin ;
448
455
}
449
456
}
450
457
@@ -517,7 +524,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId)
517
524
break ;
518
525
}
519
526
520
- LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor ) ;
527
+ LowLevelSpinWaiter . Wait ( spinIndex , SpinSleep0Threshold , isSingleProcessor : false ) ;
521
528
}
522
529
523
530
if ( acquiredLock )
@@ -661,14 +668,25 @@ internal nint LockIdForEvents
661
668
662
669
internal ulong OwningThreadId => _owningThreadId ;
663
670
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
+ }
670
685
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 ( )
672
690
{
673
691
// The config var can be set to -1 to disable adaptive spin
674
692
short adaptiveSpinPeriod =
0 commit comments