Esempio n. 1
0
        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;
            }
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 5
0
        /// <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();
                }
            }
        }
Esempio n. 6
0
        /// <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;
            }
        }
Esempio n. 7
0
        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);
                        }
                    }
                }
            }
        }
Esempio n. 8
0
        /// <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);
                            }
                        }
                    }