the tcs.Task.Wait()
will "block until the task being completed is actually finished (not including the task’s synchronous continuations, just the task itself)". Why won't we block on any synchronous continuations? That is, what will prevent any synchronous continuations on any Tasks awaiting tcs.Task's completion from executing synchronously?
Your question is about Task
s in general, not about the TaskCompletionSource.Task
in particular. The answer is that when an incomplete Task
is waited, the Wait
is performed by a ManualResetEventSlim
, which is Set
by a task continuation, which is prioritized in front of all other continuations that might be attached previously. Here is a part of the private SpinThenBlockingWait
method, from the Task.cs
source code:
private bool SpinThenBlockingWait(int millisecondsTimeout,
CancellationToken cancellationToken)
{
bool infiniteWait = millisecondsTimeout == Timeout.Infinite;
uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount;
bool returnValue = SpinWait(millisecondsTimeout);
if (!returnValue)
{
var mres = new SetOnInvokeMres();
try
{
AddCompletionAction(mres, addBeforeOthers: true);
if (infiniteWait)
{
bool notifyWhenUnblocked = ThreadPool.NotifyThreadBlocked();
try
{
returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
//...
This method is invoked internally by the Wait
, when the task is not complete at the moment, and the current thread must be blocked. The SetOnInvokeMres
is a small class that inherits from the ManualResetEventSlim
:
private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
{
internal SetOnInvokeMres() : base(false, 0) { }
public void Invoke(Task completingTask) { Set(); }
public bool InvokeMayRunArbitraryCode => false;
}
The interesting point is the addBeforeOthers: true
argument. This ensures that the ManualResetEventSlim
will be signaled before invoking any continuation that is associated with user-supplied code. The Task Parallel Library exposes no public API that allows to attach a continuation with addBeforeOthers: true
. Actually this parameter seems to exist exclusively for supporting the signaling of ManualResetEventSlim
s with priority.