-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Lock spin-waiting on single-proc machines #101513
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -297,7 +297,7 @@ private void ExitImpl() | |
} | ||
} | ||
|
||
private static bool IsAdaptiveSpinEnabled(short minSpinCount) => minSpinCount <= 0; | ||
private static bool IsAdaptiveSpinEnabled(short minSpinCountForAdaptiveSpin) => minSpinCountForAdaptiveSpin <= 0; | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | ||
|
@@ -339,20 +339,19 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
goto Locked; | ||
} | ||
|
||
bool isSingleProcessor = IsSingleProcessor; | ||
short maxSpinCount = s_maxSpinCount; | ||
if (maxSpinCount == 0) | ||
{ | ||
goto Wait; | ||
} | ||
|
||
short minSpinCount = s_minSpinCount; | ||
short minSpinCountForAdaptiveSpin = s_minSpinCountForAdaptiveSpin; | ||
short spinCount = _spinCount; | ||
if (spinCount < 0) | ||
{ | ||
// When negative, the spin count serves as a counter for contentions such that a spin-wait can be attempted | ||
// periodically to see if it would be beneficial. Increment the spin count and skip spin-waiting. | ||
Debug.Assert(IsAdaptiveSpinEnabled(minSpinCount)); | ||
Debug.Assert(IsAdaptiveSpinEnabled(minSpinCountForAdaptiveSpin)); | ||
_spinCount = (short)(spinCount + 1); | ||
goto Wait; | ||
} | ||
|
@@ -377,7 +376,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
|
||
for (short spinIndex = 0; ;) | ||
{ | ||
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, isSingleProcessor); | ||
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, isSingleProcessor: false); | ||
|
||
if (++spinIndex >= spinCount) | ||
{ | ||
|
@@ -394,7 +393,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
|
||
if (tryLockResult == TryLockResult.Locked) | ||
{ | ||
if (isFirstSpinner && IsAdaptiveSpinEnabled(minSpinCount)) | ||
if (isFirstSpinner && IsAdaptiveSpinEnabled(minSpinCountForAdaptiveSpin)) | ||
{ | ||
// Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the | ||
// spin count more balanced, only the first spinner adjusts the spin count | ||
|
@@ -415,7 +414,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
|
||
// Unregister the spinner and try to acquire the lock | ||
tryLockResult = State.TryLockAfterSpinLoop(this); | ||
if (isFirstSpinner && IsAdaptiveSpinEnabled(minSpinCount)) | ||
if (isFirstSpinner && IsAdaptiveSpinEnabled(minSpinCountForAdaptiveSpin)) | ||
{ | ||
// Since the first spinner does a full-length spin-wait, and to keep upward and downward changes to the | ||
// spin count more balanced, only the first spinner adjusts the spin count | ||
|
@@ -433,7 +432,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
// number of contentions, the first spinner will attempt a spin-wait again to see if it is effective. | ||
Debug.Assert(tryLockResult == TryLockResult.Wait); | ||
spinCount = _spinCount; | ||
_spinCount = spinCount > 0 ? (short)(spinCount - 1) : minSpinCount; | ||
_spinCount = spinCount > 0 ? (short)(spinCount - 1) : minSpinCountForAdaptiveSpin; | ||
} | ||
} | ||
|
||
|
@@ -504,7 +503,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) | |
break; | ||
} | ||
|
||
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, isSingleProcessor); | ||
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, isSingleProcessor: false); | ||
} | ||
|
||
if (acquiredLock) | ||
|
@@ -648,14 +647,22 @@ internal nint LockIdForEvents | |
|
||
internal ulong OwningThreadId => _owningThreadId; | ||
|
||
private static short DetermineMaxSpinCount() => | ||
AppContextConfigHelper.GetInt16Config( | ||
"System.Threading.Lock.SpinCount", | ||
"DOTNET_Lock_SpinCount", | ||
DefaultMaxSpinCount, | ||
allowNegative: false); | ||
private static short DetermineMaxSpinCount() | ||
{ | ||
if (IsSingleProcessor) | ||
{ | ||
return 0; | ||
} | ||
|
||
return | ||
AppContextConfigHelper.GetInt16Config( | ||
"System.Threading.Lock.SpinCount", | ||
"DOTNET_Lock_SpinCount", | ||
DefaultMaxSpinCount, | ||
allowNegative: false); | ||
} | ||
|
||
private static short DetermineMinSpinCount() | ||
private static short DetermineMinSpinCountForAdaptiveSpin() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is a Min and Max spin count - the actual spincount is obviously computed to be somewhere in the range. Not sure the name change makes it more or less clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The min spin count with adaptive spin is just used as a limit for how low the _spinCount field can go, it can be negative, in which case it's just used as a counter for contentions while skipping spin-waiting until it reaches a value where it tries spin-waiting again. Maybe there's a better name, I can add a comment as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is ok either way.
|
||
{ | ||
// The config var can be set to -1 to disable adaptive spin | ||
short adaptiveSpinPeriod = | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the actual fix? - to force "no spin" in singleproc case, even if user specified something max spin count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea that's the actual fix