Beispiel #1
0
        internal void RemoveDependency(JoinableTask joinChild)
        {
            Requires.NotNull(joinChild, nameof(joinChild));

            using (this.Factory.Context.NoMessagePumpSynchronizationContext.Apply())
            {
                lock (this.owner.Context.SyncContextLock)
                {
                    int refCount;
                    if (this.childOrJoinedJobs != null && this.childOrJoinedJobs.TryGetValue(joinChild, out refCount))
                    {
                        if (refCount == 1)
                        {
                            this.childOrJoinedJobs.Remove(joinChild);
                            this.RemoveDependingSynchronousTaskFromChild(joinChild);
                        }
                        else
                        {
                            this.childOrJoinedJobs[joinChild] = --refCount;
                        }
                    }
                }
            }
        }
Beispiel #2
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.owner.Context.IsOnMainThread)
                {
                    additionalFlags |= JoinableTaskFlags.SynchronouslyBlockingMainThread;
                    onMainThread     = true;
                }

                this.AddStateFlags(additionalFlags);

                if (!this.IsCompleteRequested)
                {
                    if (ThreadingEventSource.Instance.IsEnabled())
                    {
                        ThreadingEventSource.Instance.CompleteOnCurrentThreadStart(this.GetHashCode(), onMainThread);
                    }

                    using (this.Factory.Context.NoMessagePumpSynchronizationContext.Apply())
                    {
                        lock (this.owner.Context.SyncContextLock)
                        {
                            this.pendingEventCount = 0;

                            // Add the task to the depending tracking list of itself, so it will monitor the event queue.
                            this.pendingEventSource = this.AddDependingSynchronousTask(this, ref this.pendingEventCount)?.WeakSelf;
                        }
                    }

                    if (onMainThread)
                    {
                        this.owner.Context.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 <JoinableTask> 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);
                            }
                        }
                    }
Beispiel #3
0
        public Task <T> GetValueAsync(CancellationToken cancellationToken)
        {
            if (!((this.value != null && this.value.IsCompleted) || this.recursiveFactoryCheck.Value == null))
            {
                // PERF: we check the condition and *then* retrieve the string resource only on failure
                // because the string retrieval has shown up as significant on ETL traces.
                Verify.FailOperation(Strings.ValueFactoryReentrancy);
            }

            if (this.value == null)
            {
                if (Monitor.IsEntered(this.syncObject))
                {
                    // PERF: we check the condition and *then* retrieve the string resource only on failure
                    // because the string retrieval has shown up as significant on ETL traces.
                    Verify.FailOperation(Strings.ValueFactoryReentrancy);
                }

                InlineResumable resumableAwaiter = null;
                lock (this.syncObject)
                {
                    // Note that if multiple threads hit GetValueAsync() before
                    // the valueFactory has completed its synchronous execution,
                    // then only one thread will execute the valueFactory while the
                    // other threads synchronously block till the synchronous portion
                    // has completed.
                    if (this.value == null)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        resumableAwaiter = new InlineResumable();
                        Func <Task <T> > originalValueFactory = this.valueFactory;
                        this.valueFactory = null;
                        Func <Task <T> > valueFactory = async delegate
                        {
                            try
                            {
                                await resumableAwaiter;
                                return(await originalValueFactory().ConfigureAwaitRunInline());
                            }
                            finally
                            {
                                this.jobFactory   = null;
                                this.joinableTask = null;
                            }
                        };

                        this.recursiveFactoryCheck.Value = RecursiveCheckSentinel;
                        try
                        {
                            if (this.jobFactory != null)
                            {
                                // Wrapping with RunAsync allows a future caller
                                // to synchronously block the Main thread waiting for the result
                                // without leading to deadlocks.
                                this.joinableTask = this.jobFactory.RunAsync(valueFactory);
                                this.value        = this.joinableTask.Task;
                            }
                            else
                            {
                                this.value = valueFactory();
                            }
                        }
                        finally
                        {
                            this.recursiveFactoryCheck.Value = null;
                        }
                    }
                }

                // Allow the original value factory to actually run.
                resumableAwaiter?.Resume();
            }

            if (!this.value.IsCompleted)
            {
                this.joinableTask?.JoinAsync(cancellationToken).Forget();
            }

            return(this.value.WithCancellation(cancellationToken));
        }
 /// <summary>
 /// Check whether a task is being tracked in our tracking list.
 /// </summary>
 internal static bool IsDependingSynchronousTask(IJoinableTaskDependent taskItem, JoinableTask syncTask)
 {
     Requires.NotNull(taskItem, nameof(taskItem));
     return(taskItem.GetJoinableTaskDependentData().IsDependingSynchronousTask(syncTask));
 }
 /// <summary>
 /// When the current dependent node is a synchronous task, this method is called after the synchronous is completed, and the thread is no longer blocked.
 /// This removes the current task from the dependingSynchronousTaskTracking list of the task itself (and propergate through its dependencies.)
 /// It reverts the data structure change done in the <see cref="OnSynchronousTaskStartToBlockWaiting"/>.
 /// </summary>
 internal static void OnSynchronousTaskEndToBlockWaiting(JoinableTask taskItem)
 {
     Requires.NotNull(taskItem, nameof(taskItem));
     JoinableTaskDependentData.OnSynchronousTaskEndToBlockWaiting(taskItem);
 }
 /// <summary>
 /// When the current dependent node is a synchronous task, this method is called before the thread is blocked to wait it to complete.
 /// This adds the current task to the dependingSynchronousTaskTracking list of the task itself (which will propergate through its dependencies.)
 /// After the task is finished, <see cref="OnSynchronousTaskEndToBlockWaiting"/> is called to revert this change.
 /// This method is expected to be used with the JTF lock.
 /// </summary>
 /// <param name="taskItem">The current joinableTask or collection.</param>
 /// <param name="taskHasPendingRequests">Return the JoinableTask which has already had pending requests to be handled.</param>
 /// <param name="pendingRequestsCount">The number of pending requests.</param>
 internal static void OnSynchronousTaskStartToBlockWaiting(JoinableTask taskItem, out JoinableTask?taskHasPendingRequests, out int pendingRequestsCount)
 {
     Requires.NotNull(taskItem, nameof(taskItem));
     Assumes.True(Monitor.IsEntered(taskItem.Factory.Context.SyncContextLock));
     JoinableTaskDependentData.OnSynchronousTaskStartToBlockWaiting(taskItem, out taskHasPendingRequests, out pendingRequestsCount);
 }
Beispiel #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="JoinableTaskSynchronizationContext"/> class.
 /// </summary>
 /// <param name="joinableTask">The <see cref="JoinableTask"/> that owns this instance.</param>
 /// <param name="mainThreadAffinitized">A value indicating whether messages posted to this instance should execute on the main thread.</param>
 internal JoinableTaskSynchronizationContext(JoinableTask joinableTask, bool mainThreadAffinitized)
     : this(Requires.NotNull(joinableTask, "joinableTask").Factory)
 {
     this.job = joinableTask;
     this.mainThreadAffinitized = mainThreadAffinitized;
 }
 internal ExecutionQueue(JoinableTask owningJob)
 {
     Requires.NotNull(owningJob, nameof(owningJob));
     this.owningJob = owningJob;
 }
 public DependentSynchronousTask(JoinableTask task)
 {
     this.SynchronousTask = task;
     this.ReferenceCount  = 1;
 }
        /// <summary>
        /// Remove a synchronous task from the tracking list of this task.
        /// </summary>
        /// <param name="task">The synchronous task need be removed</param>
        /// <param name="reachableTasks">
        /// If it is not null, it will contain all task which can track the synchronous task. We will ignore reference count in that case.
        /// </param>
        /// <param name="remainingDependentTasks">This will retain the tasks which still tracks the synchronous task.</param>
        private void RemoveDependingSynchronousTask(JoinableTask task, HashSet <JoinableTask> reachableTasks, ref HashSet <JoinableTask> remainingDependentTasks)
        {
            Requires.NotNull(task, nameof(task));

            DependentSynchronousTask previousTaskTracking = null;
            DependentSynchronousTask currentTaskTracking  = this.dependingSynchronousTaskTracking;
            bool removed = false;

            while (currentTaskTracking != null)
            {
                if (currentTaskTracking.SynchronousTask == task)
                {
                    if (--currentTaskTracking.ReferenceCount > 0)
                    {
                        if (reachableTasks != null)
                        {
                            if (!reachableTasks.Contains(this))
                            {
                                currentTaskTracking.ReferenceCount = 0;
                            }
                        }
                    }

                    if (currentTaskTracking.ReferenceCount == 0)
                    {
                        removed = true;
                        if (previousTaskTracking != null)
                        {
                            previousTaskTracking.Next = currentTaskTracking.Next;
                        }
                        else
                        {
                            this.dependingSynchronousTaskTracking = currentTaskTracking.Next;
                        }
                    }

                    if (reachableTasks == null)
                    {
                        if (removed)
                        {
                            if (remainingDependentTasks != null)
                            {
                                remainingDependentTasks.Remove(this);
                            }
                        }
                        else
                        {
                            if (remainingDependentTasks == null)
                            {
                                remainingDependentTasks = new HashSet <JoinableTask>();
                            }

                            remainingDependentTasks.Add(this);
                        }
                    }

                    break;
                }

                previousTaskTracking = currentTaskTracking;
                currentTaskTracking  = currentTaskTracking.Next;
            }

            if (removed && this.childOrJoinedJobs != null)
            {
                foreach (var item in this.childOrJoinedJobs)
                {
                    item.Key.RemoveDependingSynchronousTask(task, reachableTasks, ref remainingDependentTasks);
                }
            }
        }
        /// <summary>
        /// Remove a synchronous task from the tracking list of a list of tasks.
        /// </summary>
        /// <param name="tasks">A list of tasks we need update the tracking list.</param>
        /// <param name="syncTask">The synchronous task we want to remove</param>
        /// <param name="force">We always remove it from the tracking list if it is true.  Otherwise, we keep tracking the reference count.</param>
        private static void RemoveDependingSynchronousTaskFrom(IReadOnlyList <JoinableTask> tasks, JoinableTask syncTask, bool force)
        {
            Requires.NotNull(tasks, nameof(tasks));
            Requires.NotNull(syncTask, nameof(syncTask));

            HashSet <JoinableTask> reachableTasks = null;
            HashSet <JoinableTask> remainTasks    = null;

            if (force)
            {
                reachableTasks = new HashSet <JoinableTask>();
            }

            foreach (var task in tasks)
            {
                task.RemoveDependingSynchronousTask(syncTask, reachableTasks, ref remainTasks);
            }

            if (!force && remainTasks != null && remainTasks.Count > 0)
            {
                // a set of tasks may form a dependent loop, so it will make the reference count system
                // not to work correctly when we try to remove the synchronous task.
                // To get rid of those loops, if a task still tracks the synchronous task after reducing
                // the reference count, we will calculate the entire reachable tree from the root.  That will
                // tell us the exactly tasks which need track the synchronous task, and we will clean up the rest.
                reachableTasks = new HashSet <JoinableTask>();
                syncTask.ComputeSelfAndDescendentOrJoinedJobsAndRemainTasks(reachableTasks, remainTasks);

                // force to remove all invalid items
                HashSet <JoinableTask> remainPlaceHold = null;
                foreach (var remainTask in remainTasks)
                {
                    remainTask.RemoveDependingSynchronousTask(syncTask, reachableTasks, ref remainPlaceHold);
                }
            }
        }
        /// <summary>
        /// Tracks a new synchronous task for this task.
        /// A synchronous task is a task blocking a thread and waits it to be completed.  We may want the blocking thread
        /// to process events from this task.
        /// </summary>
        /// <param name="task">The synchronous task</param>
        /// <param name="totalEventsPending">The total events need be processed</param>
        /// <returns>The task causes us to trigger the event of the synchronous task, so it can process new events.  Null means we don't need trigger any event</returns>
        private JoinableTask AddDependingSynchronousTask(JoinableTask task, ref int totalEventsPending)
        {
            Requires.NotNull(task, nameof(task));
            Assumes.True(Monitor.IsEntered(this.owner.Context.SyncContextLock));

            if (this.IsCompleted)
            {
                return(null);
            }

            if (this.IsCompleteRequested)
            {
                // A completed task might still have pending items in the queue.
                int pendingCount = this.GetPendingEventCountForTask(task);
                if (pendingCount > 0)
                {
                    totalEventsPending += pendingCount;
                    return(this);
                }

                return(null);
            }

            DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking;

            while (existingTaskTracking != null)
            {
                if (existingTaskTracking.SynchronousTask == task)
                {
                    existingTaskTracking.ReferenceCount++;
                    return(null);
                }

                existingTaskTracking = existingTaskTracking.Next;
            }

            int          pendingItemCount    = this.GetPendingEventCountForTask(task);
            JoinableTask eventTriggeringTask = null;

            if (pendingItemCount > 0)
            {
                totalEventsPending += pendingItemCount;
                eventTriggeringTask = this;
            }

            // For a new synchronous task, we need apply it to our child tasks.
            DependentSynchronousTask newTaskTracking = new DependentSynchronousTask(task)
            {
                Next = this.dependingSynchronousTaskTracking,
            };

            this.dependingSynchronousTaskTracking = newTaskTracking;

            if (this.childOrJoinedJobs != null)
            {
                foreach (var item in this.childOrJoinedJobs)
                {
                    var childTiggeringTask = item.Key.AddDependingSynchronousTask(task, ref totalEventsPending);
                    if (eventTriggeringTask == null)
                    {
                        eventTriggeringTask = childTiggeringTask;
                    }
                }
            }

            return(eventTriggeringTask);
        }
Beispiel #13
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.owner.Context.IsOnMainThread)
                {
                    additionalFlags |= JoinableTaskFlags.SynchronouslyBlockingMainThread;
                    onMainThread     = true;
                }

                this.AddStateFlags(additionalFlags);

                if (!this.IsCompleteRequested)
                {
                    if (ThreadingEventSource.Instance.IsEnabled())
                    {
                        ThreadingEventSource.Instance.CompleteOnCurrentThreadStart(this.GetHashCode(), onMainThread);
                    }

                    using (this.Factory.Context.NoMessagePumpSynchronizationContext.Apply())
                    {
                        lock (this.owner.Context.SyncContextLock)
                        {
                            this.pendingEventCount = 0;

                            // Add the task to the depending tracking list of itself, so it will monitor the event queue.
                            this.pendingEventSource = new WeakReference <JoinableTask>(this.AddDependingSynchronousTask(this, ref this.pendingEventCount));
                        }
                    }

                    if (onMainThread)
                    {
                        this.owner.Context.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 <JoinableTask> visited = null;
                        while (!this.IsCompleteRequested)
                        {
                            SingleExecuteProtector work;
                            Task tryAgainAfter;
                            if (this.TryDequeueSelfOrDependencies(onMainThread, ref visited, out work, out tryAgainAfter))
                            {
                                work.TryExecute();
                            }
                            else if (tryAgainAfter != null)
                            {
                                ThreadingEventSource.Instance.WaitSynchronouslyStart();
                                this.owner.WaitSynchronously(tryAgainAfter);
                                ThreadingEventSource.Instance.WaitSynchronouslyStop();
                                Assumes.True(tryAgainAfter.IsCompleted);
                            }
                        }
                    }
                    finally
                    {
                        using (this.Factory.Context.NoMessagePumpSynchronizationContext.Apply())
                        {
                            lock (this.owner.Context.SyncContextLock)
                            {
                                // Remove itself from the tracking list, after the task is completed.
                                this.RemoveDependingSynchronousTask(this, true);
                            }
                        }
                    }

                    if (ThreadingEventSource.Instance.IsEnabled())
                    {
                        ThreadingEventSource.Instance.CompleteOnCurrentThreadStop(this.GetHashCode());
                    }
                }
                else
                {
                    if (onMainThread)
                    {
                        this.owner.Context.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)
                {
                    SingleExecuteProtector executor;
                    while (this.threadPoolQueue.TryDequeue(out executor))
                    {
                        ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, executor);
                    }
                }

                Assumes.True(this.Task.IsCompleted);
                this.Task.GetAwaiter().GetResult(); // rethrow any exceptions
            }
            finally
            {
                CompletingTask.Value = priorCompletingTask;
            }
        }
Beispiel #14
0
 /// <summary>
 /// Raised whenever a joinable task has completed a transition to the main thread.
 /// </summary>
 /// <param name="joinableTask">The task whose request to transition to the main thread has completed.</param>
 /// <param name="canceled">A value indicating whether the transition was cancelled before it was fulfilled.</param>
 /// <remarks>
 /// This event is usually raised on the main thread, but can be on another thread when <paramref name="canceled"/> is <c>true</c>.
 /// </remarks>
 protected internal override void OnTransitionedToMainThread(JoinableTask joinableTask, bool canceled)
 {
     this.innerFactory.OnTransitionedToMainThread(joinableTask, canceled);
 }
Beispiel #15
0
 /// <summary>
 /// Raised when a joinable task has requested a transition to the main thread.
 /// </summary>
 /// <param name="joinableTask">The task requesting the transition to the main thread.</param>
 /// <remarks>
 /// This event may be raised on any thread, including the main thread.
 /// </remarks>
 protected internal override void OnTransitioningToMainThread(JoinableTask joinableTask)
 {
     this.innerFactory.OnTransitioningToMainThread(joinableTask);
 }