internal void OnQueueCompleted() { if (this.IsCompleted) { // Note this code may execute more than once, as multiple queue completion // notifications come in. this.JoinableTaskContext.OnJoinableTaskCompleted(this); foreach (var collection in this.dependencyParents) { JoinableTaskDependencyGraph.RemoveDependency(collection, this); } if (this.mainThreadJobSyncContext != null) { this.mainThreadJobSyncContext.OnCompleted(); } if (this.threadPoolJobSyncContext != null) { this.threadPoolJobSyncContext.OnCompleted(); } this.nestingFactories = default(ListOfOftenOne <JoinableTaskFactory>); this.initialDelegate = null; this.state |= JoinableTaskFlags.CompleteFinalized; } }
/// <summary> /// Fires when the underlying Task is completed. /// </summary> /// <param name="wrappedTask">The actual result from <see cref="initialDelegate"/>.</param> internal void Complete(Task wrappedTask) { Assumes.NotNull(this.wrappedTask); // If we had to synthesize a Task earlier, then wrappedTask is a TaskCompletionSource, // which we should now complete. if (!(this.wrappedTask is Task)) { this.CompleteTaskSourceFromWrappedTask(wrappedTask, this.wrappedTask); } using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { AsyncManualResetEvent?queueNeedProcessEvent = null; lock (this.JoinableTaskContext.SyncContextLock) { if (!this.IsCompleteRequested) { this.IsCompleteRequested = true; if (this.mainThreadQueue != null) { this.mainThreadQueue.Complete(); } if (this.threadPoolQueue != null) { this.threadPoolQueue.Complete(); } this.OnQueueCompleted(); // Always arrange to pulse the event since folks waiting // will likely want to know that the JoinableTask has completed. queueNeedProcessEvent = this.queueNeedProcessEvent; JoinableTaskDependencyGraph.OnTaskCompleted(this); } } if (queueNeedProcessEvent != null) { // We explicitly do this outside our lock. queueNeedProcessEvent.PulseAll(); } } }
/// <summary> /// Get all tasks inside the candidate sets tasks, which are depended by one or more task in the source tasks list. /// </summary> /// <param name="sourceTasks">A collection of JoinableTasks represents source tasks.</param> /// <param name="candidateTasks">A collection of JoinableTasks which represents candidates.</param> /// <returns>A set of tasks matching the condition.</returns> internal static HashSet <JoinableTask> GetDependentTasksFromCandidates(IEnumerable <JoinableTask> sourceTasks, IEnumerable <JoinableTask> candidateTasks) { Requires.NotNull(sourceTasks, nameof(sourceTasks)); Requires.NotNull(candidateTasks, nameof(candidateTasks)); var candidates = new HashSet <JoinableTask>(candidateTasks); if (candidates.Count == 0) { return(candidates); } var results = new HashSet <JoinableTask>(); var visited = new HashSet <IJoinableTaskDependent>(); var queue = new Queue <IJoinableTaskDependent>(); foreach (JoinableTask task in sourceTasks) { if (task != null && visited.Add(task)) { queue.Enqueue(task); } } while (queue.Count > 0) { IJoinableTaskDependent startDepenentNode = queue.Dequeue(); if (candidates.Contains(startDepenentNode)) { results.Add((JoinableTask)startDepenentNode); } lock (startDepenentNode.JoinableTaskContext.SyncContextLock) { foreach (IJoinableTaskDependent?dependentItem in JoinableTaskDependencyGraph.GetDirectDependentNodes(startDepenentNode)) { if (visited.Add(dependentItem)) { queue.Enqueue(dependentItem); } } } } return(results); }
private static ICollection <XElement> CreatesLinksBetweenNodes(Dictionary <JoinableTask, XElement> pendingTasksElements) { Requires.NotNull(pendingTasksElements, nameof(pendingTasksElements)); var links = new List <XElement>(); foreach (KeyValuePair <JoinableTask, XElement> joinableTaskAndElement in pendingTasksElements) { foreach (JoinableTask?joinedTask in JoinableTaskDependencyGraph.GetAllDirectlyDependentJoinableTasks(joinableTaskAndElement.Key)) { if (pendingTasksElements.TryGetValue(joinedTask, out XElement? joinedTaskElement)) { links.Add(Dgml.Link(joinableTaskAndElement.Value, joinedTaskElement)); } } } return(links); }
/// <summary> /// Fires when the underlying Task is completed. /// </summary> internal void Complete() { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { AsyncManualResetEvent queueNeedProcessEvent = null; lock (this.JoinableTaskContext.SyncContextLock) { if (!this.IsCompleteRequested) { this.IsCompleteRequested = true; if (this.mainThreadQueue != null) { this.mainThreadQueue.Complete(); } if (this.threadPoolQueue != null) { this.threadPoolQueue.Complete(); } this.OnQueueCompleted(); // Always arrange to pulse the event since folks waiting // will likely want to know that the JoinableTask has completed. queueNeedProcessEvent = this.queueNeedProcessEvent; JoinableTaskDependencyGraph.OnTaskCompleted(this); } } if (queueNeedProcessEvent != null) { // We explicitly do this outside our lock. queueNeedProcessEvent.PulseAll(); } } }
/// <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; } }
internal void Post(SendOrPostCallback d, object?state, bool mainThreadAffinitized) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { SingleExecuteProtector? wrapper = null; List <AsyncManualResetEvent>?eventsNeedNotify = null; // initialized if we should pulse it at the end of the method bool postToFactory = false; bool isCompleteRequested; bool synchronouslyBlockingMainThread; lock (this.JoinableTaskContext.SyncContextLock) { isCompleteRequested = this.IsCompleteRequested; synchronouslyBlockingMainThread = this.SynchronouslyBlockingMainThread; } if (isCompleteRequested) { // This job has already been marked for completion. // We need to forward the work to the fallback mechanisms. postToFactory = true; } else { bool mainThreadQueueUpdated = false; bool backgroundThreadQueueUpdated = false; wrapper = SingleExecuteProtector.Create(this, d, state); if (ThreadingEventSource.Instance.IsEnabled()) { ThreadingEventSource.Instance.PostExecutionStart(wrapper.GetHashCode(), mainThreadAffinitized); } if (mainThreadAffinitized && !synchronouslyBlockingMainThread) { wrapper.RaiseTransitioningEvents(); } lock (this.JoinableTaskContext.SyncContextLock) { if (mainThreadAffinitized) { if (this.mainThreadQueue == null) { this.mainThreadQueue = new ExecutionQueue(this); } // Try to post the message here, but we'll also post to the underlying sync context // so if this fails (because the operation has completed) we'll still get the work // done eventually. this.mainThreadQueue.TryEnqueue(wrapper); mainThreadQueueUpdated = true; } else { if (this.SynchronouslyBlockingThreadPool) { if (this.threadPoolQueue == null) { this.threadPoolQueue = new ExecutionQueue(this); } backgroundThreadQueueUpdated = this.threadPoolQueue.TryEnqueue(wrapper); if (!backgroundThreadQueueUpdated) { ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, wrapper); } } else { ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, wrapper); } } if (mainThreadQueueUpdated || backgroundThreadQueueUpdated) { var tasksNeedNotify = JoinableTaskDependencyGraph.GetDependingSynchronousTasks(this, mainThreadQueueUpdated); if (tasksNeedNotify.Count > 0) { eventsNeedNotify = new List <AsyncManualResetEvent>(tasksNeedNotify.Count); foreach (var taskToNotify in tasksNeedNotify) { if (taskToNotify.pendingEventSource == null || taskToNotify == this) { taskToNotify.pendingEventSource = this.WeakSelf; } taskToNotify.pendingEventCount++; if (taskToNotify.queueNeedProcessEvent != null) { eventsNeedNotify.Add(taskToNotify.queueNeedProcessEvent); } } } } } } // Notify tasks which can process the event queue. if (eventsNeedNotify != null) { foreach (var queueEvent in eventsNeedNotify) { queueEvent.PulseAll(); } } // We deferred this till after we release our lock earlier in this method since we're calling outside code. if (postToFactory) { Assumes.Null(wrapper); // we avoid using a wrapper in this case because this job transferring ownership to the factory. this.Factory.Post(d, state, mainThreadAffinitized); } else if (mainThreadAffinitized) { Assumes.NotNull(wrapper); // this should have been initialized in the above logic. this.owner.PostToUnderlyingSynchronizationContextOrThreadPool(wrapper); foreach (var nestingFactory in this.nestingFactories) { if (nestingFactory != this.owner) { nestingFactory.PostToUnderlyingSynchronizationContextOrThreadPool(wrapper); } } } } }
/// <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); } } }