Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
Reorganize the uniprocessor spinning code paths
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Feb 15, 2019
1 parent 4678e25 commit cd0979d
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ internal sealed partial class LowLevelLifoSemaphore : IDisposable

private const int SpinSleep0Threshold = 10;

private static int s_processorCount = Environment.ProcessorCount;

public LowLevelLifoSemaphore(int initialSignalCount, int maximumSignalCount, int spinCount)
{
Debug.Assert(initialSignalCount >= 0);
Expand Down Expand Up @@ -91,25 +89,12 @@ public bool Wait(int timeoutMs)
counts = countsBeforeUpdate;
}

int spinIndex = s_processorCount > 1 ? 0 : SpinSleep0Threshold;
int processorCount = PlatformHelper.ProcessorCount;
int spinIndex = processorCount > 1 ? 0 : SpinSleep0Threshold;
while (spinIndex < _spinCount)
{
// TODO: Unify this with LowLevelSpinWaiter
if (s_processorCount > 1)
{
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold);
spinIndex++;
}
else
{
// On uniprocessor system we only Yield because there cannot be other
// threads executing in parallel and satisfying the condition. We also
// compensate for the unnecessary spins by incrementing spinIndex by 2
// and doing the same number of Yields as in the multi-processor code
// above.
RuntimeThread.Yield();
spinIndex += 2;
}
LowLevelSpinWaiter.Wait(spinIndex, SpinSleep0Threshold, processorCount);
spinIndex++;

// Try to acquire the semaphore and unregister as a spinner
counts = _separated._counts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public LowLevelLock()
#endif

_spinWaiter = new LowLevelSpinWaiter();
_spinWaiter.Initialize();
_spinWaitTryAcquireCallback = SpinWaitTryAcquireCallback;
_monitor = new LowLevelMonitor();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,13 @@ internal struct LowLevelSpinWaiter
private const int SpinCount = 8;
private const int SpinSleep0Threshold = 4;

private static int s_processorCount;

private int _spinningThreadCount;

public void Initialize()
{
if (s_processorCount == 0)
{
s_processorCount = Environment.ProcessorCount;
}
}

public bool SpinWaitForCondition(Func<bool> condition)
{
Debug.Assert(condition != null);
Debug.Assert(s_processorCount > 0);

int processorCount = s_processorCount;
int processorCount = PlatformHelper.ProcessorCount;
int spinningThreadCount = Interlocked.Increment(ref _spinningThreadCount);
try
{
Expand All @@ -50,15 +39,8 @@ public bool SpinWaitForCondition(Func<bool> condition)
// prior to that threshold would not help other threads make progress
for (int spinIndex = processorCount > 1 ? 0 : SpinSleep0Threshold; spinIndex < SpinCount; ++spinIndex)
{
if (processorCount > 1)
{
// The caller should check the condition in a fast path before calling this method, so wait first
Wait(spinIndex, SpinSleep0Threshold);
}
else
{
RuntimeThread.Yield();
}
// The caller should check the condition in a fast path before calling this method, so wait first
Wait(spinIndex, SpinSleep0Threshold, processorCount);

if (condition())
{
Expand All @@ -75,7 +57,7 @@ public bool SpinWaitForCondition(Func<bool> condition)
return false;
}

public static void Wait(int spinIndex, int sleep0Threshold)
public static void Wait(int spinIndex, int sleep0Threshold, int processorCount)
{
Debug.Assert(spinIndex >= 0);
Debug.Assert(sleep0Threshold >= 0);
Expand All @@ -89,7 +71,7 @@ public static void Wait(int spinIndex, int sleep0Threshold)
// spin loop too early can cause excessive context switcing from the wait.
// - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to contention),
// they may switch between one another, delaying work that can make progress.
if (spinIndex < sleep0Threshold || (spinIndex - sleep0Threshold) % 2 != 0)
if (processorCount > 1 && (spinIndex < sleep0Threshold || (spinIndex - sleep0Threshold) % 2 != 0))
{
// Cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing
// the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to
Expand Down

0 comments on commit cd0979d

Please sign in to comment.