Skip to content

Commit

Permalink
Use RunContinuationsAsynchronously in SemaphoreSlim.WaitAsync (dotnet…
Browse files Browse the repository at this point in the history
…/coreclr#22686)

SemaphoreSlim.Release shouldn't invoke arbitrary continuations as part of its invocation, so when it dequeues a task waiter and goes to complete it, rather than just calling TrySetResult, it queues the task to the thread pool to have TrySetResult invoked there.  Now that we have RunContinuationsAsynchronously, though, we can just use that functionality instead.  This has two main benefits:
1. We avoid queueing a work item entirely if there are no continuations from the task.  This might happen, for example, if the semaphore is released so quickly after waiting on it that the WaitAsync caller hasn't yet hooked up a continuation, in which case the await on the WaitAsync task will just see IsCompleted as true and continue running.
2. We avoid queueing a work item when the task represents a synchronous Wait, which happens if there's already a pending WaitAsync when Wait goes to block.  The main benefit here is avoiding potential thread pool starvation, if threads in the pool are blocked in such Waits, and previously another thread pool thread would have been needed to run the queued work item to complete the synchronous Wait.

Commit migrated from dotnet/coreclr@e2081d0
  • Loading branch information
stephentoub authored Feb 19, 2019
1 parent fd52651 commit c5a6c24
Showing 1 changed file with 2 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,7 @@ public class SemaphoreSlim : IDisposable
private sealed class TaskNode : Task<bool>
{
internal TaskNode Prev, Next;
internal TaskNode() : base() { }

internal override void ExecuteFromThreadPool(Thread threadPoolThread)
{
bool setSuccessfully = TrySetResult(true);
Debug.Assert(setSuccessfully, "Should have been able to complete task");
}
internal TaskNode() : base((object)null, TaskCreationOptions.RunContinuationsAsynchronously) { }
}
#endregion

Expand Down Expand Up @@ -847,7 +841,7 @@ public int Release(int releaseCount)
// Get the next async waiter to release and queue it to be completed
var waiterTask = m_asyncHead;
RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
ThreadPool.UnsafeQueueUserWorkItemInternal(waiterTask, preferLocal: true);
waiterTask.TrySetResult(result: true);
}
}
m_currentCount = currentCount;
Expand Down

0 comments on commit c5a6c24

Please sign in to comment.