in src/Microsoft.VisualStudio.Threading/JoinableTask.cs [886:988]
internal void CompleteOnCurrentThread()
{
Assumes.NotNull(this.wrappedTask);
// "Push" this task onto the TLS field's virtual stack so that on hang reports we know which task to 'blame'.
JoinableTask? priorCompletingTask = CompletingTask.Value;
CompletingTask.Value = this;
try
{
bool onMainThread = false;
JoinableTaskFlags additionalFlags = JoinableTaskFlags.CompletingSynchronously;
if (this.JoinableTaskContext.IsOnMainThread)
{
additionalFlags |= JoinableTaskFlags.SynchronouslyBlockingMainThread;
onMainThread = true;
}
this.AddStateFlags(additionalFlags);
if (!this.IsCompleteRequested)
{
if (ThreadingEventSource.Instance.IsEnabled())
{
ThreadingEventSource.Instance.CompleteOnCurrentThreadStart(this.GetHashCode(), onMainThread);
}
using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply())
{
lock (this.JoinableTaskContext.SyncContextLock)
{
JoinableTaskDependencyGraph.OnSynchronousTaskStartToBlockWaiting(this, out JoinableTask? pendingRequestTask, out this.pendingEventCount);
// Add the task to the depending tracking list of itself, so it will monitor the event queue.
this.pendingEventSource = pendingRequestTask?.WeakSelf;
}
}
if (onMainThread)
{
this.JoinableTaskContext.OnSynchronousJoinableTaskToCompleteOnMainThread(this);
}
try
{
// Don't use IsCompleted as the condition because that
// includes queues of posted work that don't have to complete for the
// JoinableTask to be ready to return from the JTF.Run method.
HashSet<IJoinableTaskDependent>? visited = null;
while (!this.IsCompleteRequested)
{
if (this.TryDequeueSelfOrDependencies(onMainThread, ref visited, out SingleExecuteProtector? work, out Task? tryAgainAfter))
{
work.TryExecute();
}
else if (tryAgainAfter is object)
{
// prevent referencing tasks which may be GCed during the waiting cycle.
visited?.Clear();
ThreadingEventSource.Instance.WaitSynchronouslyStart();
this.owner.WaitSynchronously(tryAgainAfter);
ThreadingEventSource.Instance.WaitSynchronouslyStop();
Assumes.True(tryAgainAfter.IsCompleted);
}
}
}
finally
{
JoinableTaskDependencyGraph.OnSynchronousTaskEndToBlockWaiting(this);
}
if (ThreadingEventSource.Instance.IsEnabled())
{
ThreadingEventSource.Instance.CompleteOnCurrentThreadStop(this.GetHashCode());
}
}
else
{
if (onMainThread)
{
this.JoinableTaskContext.OnSynchronousJoinableTaskToCompleteOnMainThread(this);
}
}
// Now that we're about to stop blocking a thread, transfer any work
// that was queued but evidently not required to complete this task
// back to the threadpool so it still gets done.
if (this.threadPoolQueue?.Count > 0)
{
while (this.threadPoolQueue.TryDequeue(out SingleExecuteProtector? executor))
{
ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, executor);
}
}
Assumes.True(this.Task.IsCompleted);
this.Task.GetAwaiter().GetResult(); // rethrow any exceptions
}
finally
{
CompletingTask.Value = priorCompletingTask;
}
}