') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Reapply "A few fixes in the threadpool semaphore. Unify Windows/Unix implementation of LIFO policy." (#125193) by VSadov · Pull Request #125596 · dotnet/runtime · GitHub
Skip to content

Reapply "A few fixes in the threadpool semaphore. Unify Windows/Unix implementation of LIFO policy." (#125193) - #125596

Closed
VSadov wants to merge 9 commits into
dotnet:mainfrom
VSadov:rereTp
Closed

Reapply "A few fixes in the threadpool semaphore. Unify Windows/Unix implementation of LIFO policy." (#125193)#125596
VSadov wants to merge 9 commits into
dotnet:mainfrom
VSadov:rereTp

Conversation

@VSadov

Copy link
Copy Markdown
Member

Re: #125193

TODO: need to confirm that NuGet restore performance is ok with the updated change.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reapplies and reworks the ThreadPool LIFO semaphore changes (previously reverted due to NuGet restore regressions) by unifying the blocking/wake implementation across Windows and Unix using OS compare-and-wait primitives (WaitOnAddress / futex) with a monitor fallback.

Changes:

  • Adds low-level compare-and-wait interop for Windows (WaitOnAddress) and Linux (futex) and wires them through System.Native/CoreLib.
  • Replaces the prior per-OS LowLevelLifoSemaphore implementations with a unified managed implementation using a LIFO stack of per-thread blockers plus updated spin/backoff behavior.
  • Adjusts worker dispatch heuristics (missed-steal handling) and configuration plumbing (cooperative blocking env var alias).

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
FileDescription
src/native/libs/System.Native/pal_threading.hAdds exported futex-related entrypoints to the System.Native PAL surface.
src/native/libs/System.Native/pal_threading.cImplements Linux futex wait/wake wrappers; provides non-Linux stubs.
src/native/libs/System.Native/entrypoints.cRegisters the new futex entrypoints for managed interop.
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.csAdds a 1ms sleep before requesting workers when a steal was missed.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.csSwitches the worker wait to the new LowLevelLifoSemaphore.Wait(timeout, activeThreadCount) signature; removes old spin-limit wiring at the call site.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.csAdds the DOTNET_ThreadPool_CooperativeBlocking env var alias for cooperative blocking.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelThreadBlocker.csIntroduces a portable blocker abstraction (futex/WaitOnAddress or LowLevelMonitor fallback).
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.csReplaces OS-specific semaphore core with a unified managed LIFO implementation + updated spin heuristic and wake accounting.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.csRemoves the prior Windows IOCP-based LIFO semaphore implementation.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.csRemoves the prior Unix WaitSubsystem-based semaphore implementation.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Windows.csAdds Windows WaitOnAddress-based compare-and-wait wrapper.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Unix.csAdds Unix futex wrapper (currently Linux-only per comments).
src/libraries/System.Private.CoreLib/src/System/Threading/Backoff.csUpdates exponential backoff to return spin count and reduces max backoff.
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitemsWires new threading and interop files into CoreLib build (adds/removes Compile items).
src/libraries/Common/src/Interop/Windows/Mincore/Interop.WaitOnAddress.csAdds LibraryImport declarations for WaitOnAddress/WakeByAddressSingle.
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CriticalSection.csAdds SuppressGCTransition on LeaveCriticalSection.
src/libraries/Common/src/Interop/Windows/Interop.Libraries.csAdds the Synch API-set library constant for WaitOnAddress imports.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.LowLevelMonitor.csAdds SuppressGCTransition on LowLevelMonitor_Release.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.Futex.csAdds LibraryImport declarations for futex wait/wake entrypoints.
src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxprojAdds Synchronization.lib to link set for NativeAOT repro project.
src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txtAllows WaitOnAddress/WakeByAddressSingle through the NativeAOT Windows API allowlist.
src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targetsAdds Synchronization.lib to NativeAOT SDK library list.
docs/coding-guidelines/interop-guidelines.mdUpdates interop guideline examples to match casing/structure and adds Synch library mention.

You can also share your feedback on Copilot code review. Take the survey.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reapplies and extends the threadpool semaphore/LIFO-policy changes that were previously reverted due to NuGet restore performance regression, by unifying the Windows/Unix implementation around a shared managed LIFO waiter stack and adding low-level wait/wake primitives (Linux futex, Windows WaitOnAddress) plus supporting interop/AOT wiring.

Changes:

  • Add Linux futex exports in System.Native and corresponding managed interop; add Windows WaitOnAddress interop and link inputs for NativeAOT.
  • Replace platform-specific LowLevelLifoSemaphore implementations with a unified managed implementation built on LowLevelThreadBlocker.
  • Adjust threadpool behavior around missed steals (including a brief delay) and tweak a blocking config switch plumbing.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/native/libs/System.Native/pal_threading.hAdds futex-related PALEXPORT declarations.
src/native/libs/System.Native/pal_threading.cImplements Linux futex wait/wake syscalls (with non-Linux stubs).
src/native/libs/System.Native/entrypoints.cExposes futex entrypoints via DllImportEntry.
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.csAdds a delay before requesting a worker when missed steals occur.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.csSwitches semaphore construction/Wait signature; minor comment fix.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.csAdds env-var name to cooperative blocking config lookup.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelThreadBlocker.csIntroduces a portable blocker using futex/WaitOnAddress or monitor fallback.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.csReworks semaphore into a single managed implementation with LIFO waiter stack + spin heuristic.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.csRemoves prior Windows IOCP-based implementation.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.csRemoves prior Unix WaitSubsystem-based implementation.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Windows.csAdds Windows WaitOnAddress/WakeByAddressSingle wrapper.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Unix.csAdds Linux futex wrapper (Linux-only).
src/libraries/System.Private.CoreLib/src/System/Threading/Backoff.csChanges exponential backoff to return spin count and reduces cap.
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitemsWires new threading/interop files into CoreLib build and removes old semaphore OS-specific files.
src/libraries/Common/src/Interop/Windows/Mincore/Interop.WaitOnAddress.csAdds LibraryImport for WaitOnAddress/WakeByAddressSingle.
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CriticalSection.csAdds SuppressGCTransition to LeaveCriticalSection.
src/libraries/Common/src/Interop/Windows/Interop.Libraries.csAdds Libraries.Synch constant for the synch api-set.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.LowLevelMonitor.csAdds SuppressGCTransition to LowLevelMonitor_Release.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.Futex.csAdds LibraryImport declarations for System.Native futex exports.
src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxprojLinks Synchronization.lib for WaitOnAddress/WakeByAddressSingle.
src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txtAdds WaitOnAddress/WakeByAddressSingle to the NativeAOT Windows API list.
src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targetsAdds Synchronization.lib to SDK native libraries for NativeAOT.
docs/coding-guidelines/interop-guidelines.mdUpdates interop naming/examples (e.g., Mincore, Synch).

You can also share your feedback on Copilot code review. Take the survey.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR re-applies and extends prior thread pool semaphore/LIFO work distribution changes by introducing a unified, portable blocking/wake mechanism (futex/WaitOnAddress where available) and reworking the CoreLib LowLevelLifoSemaphore implementation to use it.

Changes:

  • Add low-level futex-style wait/wake exports on Linux in System.Native and corresponding managed interop (plus Windows WaitOnAddress interop).
  • Replace the platform-specific LowLevelLifoSemaphore implementations with a unified CoreLib implementation based on a LIFO stack of per-thread blockers.
  • Adjust thread pool worker dispatch/wait logic to differentiate spurious dispatches from regular dispatch and change spin/parking behavior accordingly.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/native/libs/System.Native/pal_threading.hAdds new exported futex wait/wake APIs.
src/native/libs/System.Native/pal_threading.cImplements Linux futex syscalls and non-Linux stubs.
src/native/libs/System.Native/entrypoints.cExposes the new futex entrypoints to managed code.
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.csChanges dispatch return type to a new DispatchResult enum.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.csUpdates worker loop to use DispatchResult and to park without spin when idle.
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.csAdds DOTNET_ env-var alias for cooperative blocking config.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelThreadBlocker.csNew portable thread blocker abstraction using futex/WaitOnAddress/monitor.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.csReplaces prior OS-based semaphore with unified LIFO blocker-stack implementation + new spin heuristic.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Windows.csNew Windows WaitOnAddress wrapper for futex-like operations.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelFutex.Unix.csNew Unix futex wrapper (Linux-backed via System.Native).
src/libraries/System.Private.CoreLib/src/System/Threading/Backoff.csChanges exponential backoff to return spin count and reduces max cap.
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.csDeleted (replaced by unified implementation).
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.csDeleted (replaced by unified implementation).
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitemsWires in new threading + interop sources.
src/libraries/Common/src/Interop/Windows/Mincore/Interop.WaitOnAddress.csAdds P/Invokes for WaitOnAddress/WakeByAddressSingle.
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CriticalSection.csAdds SuppressGCTransition to LeaveCriticalSection import.
src/libraries/Common/src/Interop/Windows/Interop.Libraries.csAdds Libraries.Synch constant for the synch API-set DLL.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.LowLevelMonitor.csAdds SuppressGCTransition to LowLevelMonitor_Release import.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.Futex.csAdds System.Native futex interop declarations.
src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxprojAdds Synchronization.lib to native repro project libs.
src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txtAdds WaitOnAddress/WakeByAddressSingle to allowed API list.
src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targetsEnsures Synchronization.lib is linked for NativeAOT.
docs/coding-guidelines/interop-guidelines.mdUpdates documentation for Mincore casing/folder layout and Synch library constant.

CopilotAI review requested due to automatic review settings April 7, 2026 01:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

CopilotAI review requested due to automatic review settings May 15, 2026 18:08

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.

if (null != workStealingQueue)
{
TransferLocalWork();
ThreadPoolWorkQueue.TransferAllLocalWorkItemsToHighPriorityGlobalQueue();
Comment on lines 1148 to 1150
// Currently where this type is used, queued work is expected to be processed
// at high priority. The implementation could be modified to support different
// priorities if necessary.
if (!HasWaitersToWake(countsBeforeUpdate))
break;

// CAS collision, but still have waters to wake, try again.
public void Signal()
{
// Increment signal count. This enables one-shot acquire.
Counts counts = _separated._counts.InterlockedIncrementSignalCount();

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.

{
return counts.SignalCount != 0 || WaitForSignal(timeoutMs);
WakeOne();
break;
Comment on lines +314 to +315
// LowLevelLock release is a full fence thus ordinary read of _pendingWake is ok
if (_pendingWake > 0)
if (!HasWaitersToWake(countsBeforeUpdate))
break;

// CAS collision, but still have waters to wake, try again.
CopilotAI review requested due to automatic review settings May 25, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

if (!HasWaitersToWake(countsBeforeUpdate))
break;

// CAS collision, but still have waters to wake, try again.
Comment on lines 47 to 53
internal const string Ucrtbase = "ucrtbase.dll";
internal const string Xolehlp = "xolehlp.dll";
internal const string Comdlg32 = "comdlg32.dll";
internal const string Gdiplus = "gdiplus.dll";
internal const string Oleaut32 = "oleaut32.dll";
internal const string Winspool = "winspool.drv";
internal const string Synch = "api-ms-win-core-synch-l1-2-0.dll";
}
Comment on lines 91 to 104
@@ -94,12 +96,9 @@ internal static partial class Interop // contents of Common\src\Interop\Windows\
private static class Libraries
{
internal const string Kernel32 = "kernel32.dll";
internal const string OleAut32 = "oleaut32.dll";
internal const string Localization = "api-ms-win-core-localization-l1-2-0.dll";
internal const string Handle = "api-ms-win-core-handle-l1-1-0.dll";
internal const string ProcessThreads = "api-ms-win-core-processthreads-l1-1-0.dll";
internal const string File = "api-ms-win-core-file-l1-1-0.dll";
internal const string NamedPipe = "api-ms-win-core-namedpipe-l1-1-0.dll";
internal const string IO = "api-ms-win-core-io-l1-1-0.dll";
internal const string Synch = "api-ms-win-core-synch-l1-2-0.dll";
...
}
}
CopilotAI review requested due to automatic review settings May 26, 2026 06:35

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.

private readonly short _maxSpinCount;
private readonly short _threadWakeCooldownUsec;
private readonly Action _onWait;

Comment on lines +18 to +25
// The spin count is chosen to be in the range of typical thread wake latency and some additional overhead,
// all assuming a single spin is calibrated to around 35 nanoseconds.
// The thread wake latency commonly measures at 2-10 microsecond (year 2026) and unlikely to drastically change.
private const int DefaultSemaphoreSpinCountLimit = 256;
// The cooldown roughly serves as detection that the thread did not spend time being blocked.
// If it woke in under 4 microseconds, it was likely a fast/trivial wake without blocking.
private const int DefaultWakeCooldown = 4;

\Interop.DuplicateHandle_SafeTokenHandle.cs
\Interop.DuplicateHandle_IntPtr.cs
\Kernel32
\Interop.DuplicateHandle_SafeFileHandle.cs
Comment on lines +259 to +265
#else// TARGET_LINUX

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-noreturn"
void SystemNative_LowLevelFutex_WaitOnAddress(int32_t* address, int32_t comparand)
{
@VSadov

VSadov commented May 26, 2026

Copy link
Copy Markdown
MemberAuthor

closing this as there is too much of now irrelevant comments/context. Will open a new one.

New: #128606

@VSadovVSadov closed this May 26, 2026
@build-analysisbuild-analysisBot mentioned this pull request May 26, 2026
3 tasks
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 26, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@VSadov