Suppress wake-up preemption on Linux in LowLevelLifoSemaphore (parallel to Windows SetThreadPriorityBoost)#129395
Suppress wake-up preemption on Linux in LowLevelLifoSemaphore (parallel to Windows SetThreadPriorityBoost)#129395Copilot wants to merge 4 commits into
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @VSadov |
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR extends LowLevelLifoSemaphore.Block to suppress OS “wakeup advantage” during the blocking wait window, generalizing the existing Windows behavior (priority-boost suppression) and adding a Unix/Linux-native mechanism via System.Native.
Changes:
- Added
SystemNative_SuppressWakePreemption/SystemNative_RestoreWakePreemptiontoSystem.Nativeand wired them intoentrypoints.c. - Introduced Unix interop (
Interop.WakePreemption.cs) and a managed wrapper (LowLevelLifoSemaphore.WakePreemption.cs) to scope suppression/restoration. - Updated
LowLevelLifoSemaphore.Blockto use the new platform-agnostic suppression helpers instead of Windows-onlySetThreadPriorityBoostdirectly.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/libs/System.Native/pal_threading.h | Declares new wake-preemption suppression/restoration exports. |
| src/native/libs/System.Native/pal_threading.c | Implements suppression/restoration (Linux uses sched_*; other targets are stubbed). |
| src/native/libs/System.Native/entrypoints.c | Registers new exports in the System.Native entrypoint table. |
| src/libraries/Common/src/Interop/Unix/System.Native/Interop.WakePreemption.cs | Adds LibraryImport declarations for the new System.Native APIs. |
| src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.WakePreemption.cs | Adds managed WakePreemptionScope + suppress/restore helpers (Windows/Linux/no-op). |
| src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs | Uses the new suppression scope around _blocker.TimedWait(...) and updates the explanatory comment. |
| src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems | Wires the new interop + managed helper file into the build. |
| // A transient wake-up advantage provides no benefit here and can result in | ||
| // the woken thread preempting already-working threads. On Windows this | ||
| // disables SetThreadPriorityBoost. On Linux this temporarily switches the | ||
| // thread to SCHED_BATCH, which sched(7) documents as applying "a small | ||
| // scheduling penalty with respect to wakeup behavior". |
| // disables SetThreadPriorityBoost. On Linux this temporarily switches the | ||
| // thread to SCHED_BATCH, which sched(7) documents as applying "a small | ||
| // scheduling penalty with respect to wakeup behavior". | ||
| WakePreemptionScope wakePreemptionScope = SuppressWakePreemption(); |
There was a problem hiding this comment.
These are threadpool threads under control of the runtime. We do not need to optimize/anticipate cases where these syscalls do not work since not working is both unexpected and benign.
@copilot Add an assert to make sure that the syscalls work in common/typical scenarios.
There was a problem hiding this comment.
Added Debug.Assert(result == 0, ...) after each call to SuppressWakePreemption and RestoreWakePreemption in LowLevelLifoSemaphore.WakePreemption.cs. These will fire in debug builds if either syscall unexpectedly fails. Commit: 6ed4b5d.
| { | ||
| internal sealed partial class LowLevelLifoSemaphore | ||
| { | ||
| private readonly struct WakePreemptionScope |
There was a problem hiding this comment.
@copilot these are threadpool threads. The user is not expected to change varioaus parameters like scheduler or priority from defaults. Even if user does so, it would be ok and perhaps even a mildly good thing if suppressing/restoring would revert thread state to defaults.
I do not think we need to capture and revert to the original state. We should revert to default.
There was a problem hiding this comment.
Simplified as requested. RestoreWakePreemption now unconditionally sets SCHED_OTHER (priority 0) instead of capturing and restoring the previous state. The native API is now parameterless — suppress sets SCHED_BATCH, restore sets SCHED_OTHER. WakePreemptionScope on Linux carries only a bool Suppressed to guard the restore call. Commit: b7de4e3.
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
Note
AI-generated draft.
This change implements the Linux counterpart to the existing Windows wake-preemption suppression used around
LowLevelLifoSemaphore.Block. The goal is the same on both platforms: a worker waking from park should rejoin queue processing without receiving a transient wake-up scheduling advantage over already-running workers.LowLevelLifoSemaphore integration
SetThreadPriorityBoostblock inBlockwith platform-agnosticSuppressWakePreemption()/RestoreWakePreemption(...)helpers._blocker.TimedWait(...)window.Linux wake-preemption mechanism (System.Native)
SystemNative_SuppressWakePreemption()— sets the calling thread toSCHED_BATCHbefore the wait.SystemNative_RestoreWakePreemption()— restores the thread to the defaultSCHED_OTHER(priority 0) after the wait.Managed interop and wrapper
Interop.WakePreemption.cs.LowLevelLifoSemaphore.WakePreemption.cswithWakePreemptionScope:SetThreadPriorityBoost(true/false).SCHED_BATCHon suppress and restoresSCHED_OTHERon restore;WakePreemptionScopecarries a singlebool Suppressedfield.Debug.Asserton the return values of bothSuppressWakePreemptionandRestoreWakePreemptioninterop calls to catch unexpected syscall failures in debug builds. Failures are otherwise ignored as benign.Entry point and project plumbing
entrypoints.c(unconditional).System.Private.CoreLib.Shared.projitems.