/// <summary> /// Get how many number of synchronous tasks in our tracking list. /// </summary> private int CountOfDependingSynchronousTasks() { int count = 0; DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { count++; existingTaskTracking = existingTaskTracking.Next; } return(count); }
/// <summary> /// Removes all synchronous tasks we applies to a dependent task, after the relationship is removed. /// </summary> /// <param name="child">The original dependent task</param> private void RemoveDependingSynchronousTaskFromChild(JoinableTask child) { Requires.NotNull(child, nameof(child)); Assumes.True(Monitor.IsEntered(this.owner.Context.SyncContextLock)); DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { child.RemoveDependingSynchronousTask(existingTaskTracking.SynchronousTask); existingTaskTracking = existingTaskTracking.Next; } }
/// <summary> /// Check whether a task is being tracked in our tracking list. /// </summary> private bool IsDependingSynchronousTask(JoinableTask syncTask) { DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { if (existingTaskTracking.SynchronousTask == syncTask) { return(true); } existingTaskTracking = existingTaskTracking.Next; } return(false); }
/// <summary> /// Gets a value indicating whether the main thread is waiting for the task's completion /// This method is expected to be used with the JTF lock. /// </summary> internal bool HasMainThreadSynchronousTaskWaiting() { DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { if ((existingTaskTracking.SynchronousTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) { return(true); } existingTaskTracking = existingTaskTracking.Next; } return(false); }
/// <summary> /// Remove all synchronous tasks tracked by the this task. /// This is called when this task is completed /// </summary> private void CleanupDependingSynchronousTask() { if (this.dependingSynchronousTaskTracking != null) { DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; this.dependingSynchronousTaskTracking = null; if (this.childOrJoinedJobs != null) { var childrenTasks = this.childOrJoinedJobs.Select(item => item.Key).ToList(); while (existingTaskTracking != null) { RemoveDependingSynchronousTaskFrom(childrenTasks, existingTaskTracking.SynchronousTask, false); existingTaskTracking = existingTaskTracking.Next; } } } }
/// <summary> /// Remove all synchronous tasks tracked by the this task. /// This is called when this task is completed. /// This method is expected to be used with the JTF lock. /// </summary> internal void OnTaskCompleted() { if (this.dependingSynchronousTaskTracking != null) { DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; this.dependingSynchronousTaskTracking = null; if (this.childDependentNodes != null) { var childrenTasks = new List <IJoinableTaskDependent>(this.childDependentNodes.Keys); while (existingTaskTracking != null) { RemoveDependingSynchronousTaskFrom(childrenTasks, existingTaskTracking.SynchronousTask, false); existingTaskTracking = existingTaskTracking.Next; } } } }
/// <summary> /// Calculate the collection of events we need trigger after we enqueue a request. /// </summary> /// <param name="forMainThread">True if we want to find tasks to process the main thread queue. Otherwise tasks to process the background queue.</param> /// <returns>The collection of synchronous tasks we need notify.</returns> private List <JoinableTask> GetDependingSynchronousTasks(bool forMainThread) { Assumes.True(Monitor.IsEntered(this.owner.Context.SyncContextLock)); var tasksNeedNotify = new List <JoinableTask>(this.CountOfDependingSynchronousTasks()); DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { var syncTask = existingTaskTracking.SynchronousTask; bool syncTaskInOnMainThread = (syncTask.state & JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTaskFlags.SynchronouslyBlockingMainThread; if (forMainThread == syncTaskInOnMainThread) { // Only synchronous tasks are in the list, so we don't need do further check for the CompletingSynchronously flag tasksNeedNotify.Add(syncTask); } existingTaskTracking = existingTaskTracking.Next; } return(tasksNeedNotify); }
/// <summary> /// Applies all synchronous tasks tracked by this task to a new child/dependent task. /// </summary> /// <param name="child">The new child task.</param> /// <returns>Pairs of synchronous tasks we need notify and the event source triggering it, plus the number of pending events.</returns> private List <PendingNotification> AddDependingSynchronousTaskToChild(JoinableTask child) { Requires.NotNull(child, nameof(child)); Assumes.True(Monitor.IsEntered(this.owner.Context.SyncContextLock)); var tasksNeedNotify = new List <PendingNotification>(this.CountOfDependingSynchronousTasks()); DependentSynchronousTask existingTaskTracking = this.dependingSynchronousTaskTracking; while (existingTaskTracking != null) { int totalEventNumber = 0; var eventTriggeringTask = child.AddDependingSynchronousTask(existingTaskTracking.SynchronousTask, ref totalEventNumber); if (eventTriggeringTask != null) { tasksNeedNotify.Add(new PendingNotification(existingTaskTracking.SynchronousTask, eventTriggeringTask, totalEventNumber)); } existingTaskTracking = existingTaskTracking.Next; } return(tasksNeedNotify); }
/// <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> /// 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); }