private bool TryDequeueSelfOrDependencies(bool onMainThread, ref HashSet <JoinableTask> visited, out SingleExecuteProtector work, out Task tryAgainAfter) { using (this.Factory.Context.NoMessagePumpSynchronizationContext.Apply()) { lock (this.owner.Context.SyncContextLock) { if (this.IsCompleted) { work = null; tryAgainAfter = null; return(false); } if (this.pendingEventCount > 0) { this.pendingEventCount--; if (this.pendingEventSource != null) { JoinableTask pendingSource; if (this.pendingEventSource.TryGetTarget(out pendingSource) && pendingSource.IsDependingSynchronousTask(this)) { var queue = onMainThread ? pendingSource.mainThreadQueue : pendingSource.threadPoolQueue; if (queue != null && !queue.IsCompleted && queue.TryDequeue(out work)) { if (queue.Count == 0) { this.pendingEventSource = null; } tryAgainAfter = null; return(true); } } this.pendingEventSource = null; } if (visited == null) { visited = new HashSet <JoinableTask>(); } else { visited.Clear(); } if (this.TryDequeueSelfOrDependencies(onMainThread, visited, out work)) { tryAgainAfter = null; return(true); } } this.pendingEventCount = 0; work = null; tryAgainAfter = this.QueueNeedProcessEvent; return(false); } } }
internal void Post(SendOrPostCallback d, object state, bool mainThreadAffinitized) { using (this.Factory.Context.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.owner.Context.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.owner.Context.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 = this.GetDependingSynchronousTasks(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); } } } } }
private bool TryDequeueSelfOrDependencies(bool onMainThread, HashSet <JoinableTask> visited, out SingleExecuteProtector work) { Requires.NotNull(visited, nameof(visited)); Report.IfNot(Monitor.IsEntered(this.owner.Context.SyncContextLock)); // We only need find the first work item. work = null; if (visited.Add(this)) { var queue = onMainThread ? this.mainThreadQueue : this.threadPoolQueue; if (queue != null && !queue.IsCompleted) { queue.TryDequeue(out work); } if (work == null) { if (this.childOrJoinedJobs != null && !this.IsCompleted) { foreach (var item in this.childOrJoinedJobs) { if (item.Key.TryDequeueSelfOrDependencies(onMainThread, visited, out work)) { break; } } } } } return(work != null); }