Skip to content

Commit

Permalink
Check for time-sensitive work when worker thread starvation is ongoing (
Browse files Browse the repository at this point in the history
#61930)

- Otherwise timer callbacks may not run when worker threads are continually starved
- Fix for #61804 in main
  • Loading branch information
kouvel authored Nov 23, 2021
1 parent f24b363 commit c158cb2
Showing 1 changed file with 35 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ public int Count
}

internal bool loggingEnabled;
private bool _dispatchTimeSensitiveWorkFirst;
internal readonly ConcurrentQueue<object> workItems = new ConcurrentQueue<object>(); // SOS's ThreadPool command depends on this name
internal readonly ConcurrentQueue<IThreadPoolWorkItem>? timeSensitiveWorkQueue =
ThreadPool.SupportsTimeSensitiveWorkItems ? new ConcurrentQueue<IThreadPoolWorkItem>() : null;
Expand Down Expand Up @@ -594,27 +595,46 @@ internal static bool Dispatch()
// Before dequeuing the first work item, acknowledge that the thread request has been satisfied
workQueue.MarkThreadRequestSatisfied();

object? workItem;
object? workItem = null;
{
bool missedSteal = false;
workItem = workQueue.Dequeue(tl, ref missedSteal);
#pragma warning disable CS0162 // Unreachable code detected. SupportsTimeSensitiveWorkItems may be a constant in some runtimes.
if (ThreadPool.SupportsTimeSensitiveWorkItems)
{
// Alternate between checking for time-sensitive work or other 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, if time-sensitive work is always checked last here, timer
// callbacks may not run when worker threads are continually starved.
bool dispatchTimeSensitiveWorkFirst = workQueue._dispatchTimeSensitiveWorkFirst;
workQueue._dispatchTimeSensitiveWorkFirst = !dispatchTimeSensitiveWorkFirst;
if (dispatchTimeSensitiveWorkFirst)
{
workItem = workQueue.TryDequeueTimeSensitiveWorkItem();
}
}
#pragma warning restore CS0162

if (workItem == null)
{
//
// No work.
// If we missed a steal, though, there may be more work in the queue.
// Instead of looping around and trying again, we'll just request another thread. Hopefully the thread
// that owns the contended work-stealing queue will pick up its own workitems in the meantime,
// which will be more efficient than this thread doing it anyway.
//
if (missedSteal)
bool missedSteal = false;
workItem = workQueue.Dequeue(tl, ref missedSteal);

if (workItem == null)
{
workQueue.EnsureThreadRequested();
}
//
// No work.
// If we missed a steal, though, there may be more work in the queue.
// Instead of looping around and trying again, we'll just request another thread. Hopefully the thread
// that owns the contended work-stealing queue will pick up its own workitems in the meantime,
// which will be more efficient than this thread doing it anyway.
//
if (missedSteal)
{
workQueue.EnsureThreadRequested();
}

// Tell the VM we're returning normally, not because Hill Climbing asked us to return.
return true;
// Tell the VM we're returning normally, not because Hill Climbing asked us to return.
return true;
}
}

// A work item was successfully dequeued, and there may be more work items to process. Request a thread to
Expand Down

0 comments on commit c158cb2

Please sign in to comment.