Skip to content

Commit d40e332

Browse files
committed
cooldown config
1 parent 0ed2522 commit d40e332

1 file changed

Lines changed: 15 additions & 7 deletions

File tree

src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,19 @@ namespace System.Threading
1515
/// </summary>
1616
internal sealed partial class LowLevelLifoSemaphore
1717
{
18+
// The spin count is chosen to be in the range of typical thread wake latency and some additional overhead,
19+
// all assuming a single spin is calibrated to around 35 nanoseconds.
20+
// The thread wake latency commonly measures at 2-10 microsecond (year 2026) and unlikely to drastically change.
1821
private const int DefaultSemaphoreSpinCountLimit = 256;
22+
// The cooldown roughly serves as detection that the thread did not spend time being blocked.
23+
// If it woke in under 4 microseconds, it was likely a fast/trivial wake without blocking.
24+
private const int DefaultWakeCooldown = 4;
1925

2026
private CacheLineSeparatedCounts _separated;
2127

2228
private readonly int _maximumSignalCount;
23-
private readonly int _maxSpinCount;
29+
private readonly short _maxSpinCount;
30+
private readonly short _threadWakeCooldownUsec;
2431
private readonly Action _onWait;
2532

2633
// When we need to block threads we use a linked list of per-thread blockers.
@@ -60,16 +67,17 @@ public LowLevelLifoSemaphore(int maximumSignalCount, Action onWait)
6067
_maximumSignalCount = maximumSignalCount;
6168
_onWait = onWait;
6269

63-
_maxSpinCount = AppContextConfigHelper.GetInt32ComPlusOrDotNetConfig(
70+
_maxSpinCount = AppContextConfigHelper.GetInt16ComPlusOrDotNetConfig(
6471
"System.Threading.ThreadPool.UnfairSemaphoreSpinLimit",
6572
"ThreadPool_UnfairSemaphoreSpinLimit",
6673
DefaultSemaphoreSpinCountLimit,
6774
false);
6875

69-
// Do not accept unreasonably huge _maxSpinCount value to prevent overflows.
70-
// Also, 1+ minute spins do not make sense.
71-
if (_maxSpinCount > 1000000)
72-
_maxSpinCount = DefaultSemaphoreSpinCountLimit;
76+
_threadWakeCooldownUsec = AppContextConfigHelper.GetInt16ComPlusOrDotNetConfig(
77+
"System.Threading.ThreadPool.UnfairSemaphoreWakeCooldown",
78+
"ThreadPool_UnfairSemaphoreWakeCooldown",
79+
DefaultWakeCooldown,
80+
false);
7381
}
7482

7583
public bool Wait(int timeoutMs)
@@ -214,7 +222,7 @@ private bool WaitAsWaiter(int timeoutMs)
214222
// and are hard to avoid completely.
215223
// So, if a fast wake happened when parking was desired, we hold up the thread a bit
216224
// before releasing.
217-
long cooldown = Stopwatch.Frequency * 4 / 1000000;
225+
long cooldown = Stopwatch.Frequency * _threadWakeCooldownUsec / 1000000;
218226
while (Stopwatch.GetTimestamp() - waitStartTick < cooldown)
219227
{
220228
Thread.UninterruptibleSleep0();

0 commit comments

Comments
 (0)