/// <summary>Runs a loop to process all queued work items, returning only when the task is completed.</summary> 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; var 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 != null) { 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; } }
/// <summary>Runs a loop to process all queued work items, returning only when the task is completed.</summary> 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; var 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 != null) { ThreadingEventSource.Instance.WaitSynchronouslyStart(); this.owner.WaitSynchronously(tryAgainAfter); ThreadingEventSource.Instance.WaitSynchronouslyStop(); Assumes.True(tryAgainAfter.IsCompleted); } } }