From fd818656eadcc515467a9348bc6c2ea30fb50388 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Mon, 12 Jan 2026 16:56:13 -0800 Subject: [PATCH 1/2] Fix Thread pool priority alternation --- .../src/System/Threading/ThreadPoolWorkQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 0480f93dfa35b1..db65a53724f475 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -849,9 +849,9 @@ public long GlobalCount // take over the thread, sustaining starvation. For example, when worker threads are continually starved, // high-priority work items may always be queued and normal-priority work items may not get a chance to run. bool dispatchNormalPriorityWorkFirst = workQueue._dispatchNormalPriorityWorkFirst; + workQueue._dispatchNormalPriorityWorkFirst = !dispatchNormalPriorityWorkFirst; if (dispatchNormalPriorityWorkFirst && !tl.workStealingQueue.CanSteal) { - workQueue._dispatchNormalPriorityWorkFirst = !dispatchNormalPriorityWorkFirst; WorkQueue queue = s_assignableWorkItemQueueCount > 0 ? tl.assignedGlobalWorkItemQueue : workQueue.workItems; if (!queue.TryDequeue(out workItem) && s_assignableWorkItemQueueCount > 0) From 382cd7891bfdb26c5861eeeceb990a15f184daf9 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 23 Jan 2026 00:22:42 -0800 Subject: [PATCH 2/2] Remove dequeue with priority alternation --- .../System/Threading/ThreadPoolWorkQueue.cs | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index db65a53724f475..d6c50b305d1e06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -421,7 +421,6 @@ public int Count (Environment.ProcessorCount + (ProcessorsPerAssignableWorkItemQueue - 1)) / ProcessorsPerAssignableWorkItemQueue; private bool _loggingEnabled; - private bool _dispatchNormalPriorityWorkFirst; // SOS's ThreadPool command depends on the following names internal readonly WorkQueue workItems = new WorkQueue(); @@ -840,32 +839,6 @@ public long GlobalCount // Dispatch (if YieldFromDispatchLoop is true), or performing periodic activities public const uint DispatchQuantumMs = 30; - private static object? DequeueWithPriorityAlternation(ThreadPoolWorkQueue workQueue, ThreadPoolWorkQueueThreadLocals tl, out bool missedSteal) - { - object? workItem = null; - - // Alternate between checking for high-prioriy and normal-priority work first, that way both sets of work - // items get a chance to run in situations where worker threads are starved and work items that run also - // take over the thread, sustaining starvation. For example, when worker threads are continually starved, - // high-priority work items may always be queued and normal-priority work items may not get a chance to run. - bool dispatchNormalPriorityWorkFirst = workQueue._dispatchNormalPriorityWorkFirst; - workQueue._dispatchNormalPriorityWorkFirst = !dispatchNormalPriorityWorkFirst; - if (dispatchNormalPriorityWorkFirst && !tl.workStealingQueue.CanSteal) - { - WorkQueue queue = - s_assignableWorkItemQueueCount > 0 ? tl.assignedGlobalWorkItemQueue : workQueue.workItems; - if (!queue.TryDequeue(out workItem) && s_assignableWorkItemQueueCount > 0) - { - workQueue.workItems.TryDequeue(out workItem); - } - } - - missedSteal = false; - workItem ??= workQueue.Dequeue(tl, ref missedSteal); - - return workItem; - } - /// /// Dispatches work items to this thread. /// @@ -877,7 +850,8 @@ internal static bool Dispatch() { ThreadPoolWorkQueue workQueue = ThreadPool.s_workQueue; ThreadPoolWorkQueueThreadLocals tl = workQueue.GetOrCreateThreadLocals(); - object? workItem = DequeueWithPriorityAlternation(workQueue, tl, out bool missedSteal); + bool missedSteal = false; + object? workItem = workQueue.Dequeue(tl, ref missedSteal); if (workItem == null) { // Missing a steal means there may be an item that we were unable to get.