/// <summary> /// Removes a <see cref="IJoinableTaskDependent"/> instance as one that is no longer relevant to the async operation. /// </summary> /// <param name="parentTaskOrCollection">The current joinableTask or collection contains to remove a dependency.</param> /// <param name="joinChild">The <see cref="IJoinableTaskDependent"/> to join as a child.</param> internal static void RemoveDependency(IJoinableTaskDependent parentTaskOrCollection, IJoinableTaskDependent joinChild) { Requires.NotNull(parentTaskOrCollection, nameof(parentTaskOrCollection)); Requires.NotNull(joinChild, nameof(joinChild)); using (parentTaskOrCollection.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { ref JoinableTaskDependentData data = ref parentTaskOrCollection.GetJoinableTaskDependentData(); lock (parentTaskOrCollection.JoinableTaskContext.SyncContextLock) { if (data.childDependentNodes is object && data.childDependentNodes.TryGetValue(joinChild, out int refCount)) { if (refCount == 1) { joinChild.OnRemovedFromDependency(parentTaskOrCollection); data.childDependentNodes.Remove(joinChild); data.RemoveDependingSynchronousTaskFromChild(joinChild); parentTaskOrCollection.OnDependencyRemoved(joinChild); } else { data.childDependentNodes[joinChild] = --refCount; } } } }
/// <summary> /// Removes a <see cref="IJoinableTaskDependent"/> instance as one that is no longer relevant to the async operation. /// </summary> /// <param name="taskItem">The current joinableTask or collection.</param> /// <param name="child">The <see cref="IJoinableTaskDependent"/> to join as a child.</param> internal static void RemoveDependency(IJoinableTaskDependent taskItem, IJoinableTaskDependent child) { Requires.NotNull(taskItem, nameof(taskItem)); JoinableTaskDependentData.RemoveDependency(taskItem, child); }
/// <summary> /// Adds a <see cref="JoinableTaskDependentData"/> instance as one that is relevant to the async operation. /// </summary> /// <param name="taskItem">The current joinableTask or collection.</param> /// <param name="joinChild">The <see cref="IJoinableTaskDependent"/> to join as a child.</param> internal static JoinableTaskCollection.JoinRelease AddDependency(IJoinableTaskDependent taskItem, IJoinableTaskDependent joinChild) { Requires.NotNull(taskItem, nameof(taskItem)); return(JoinableTaskDependentData.AddDependency(taskItem, joinChild)); }
/// <summary> /// Adds a <see cref="JoinableTaskDependentData"/> instance as one that is relevant to the async operation. /// </summary> /// <param name="parentTaskOrCollection">The current joinableTask or collection contains to add a dependency.</param> /// <param name="joinChild">The <see cref="IJoinableTaskDependent"/> to join as a child.</param> internal static JoinableTaskCollection.JoinRelease AddDependency(IJoinableTaskDependent parentTaskOrCollection, IJoinableTaskDependent joinChild) { Requires.NotNull(parentTaskOrCollection, nameof(parentTaskOrCollection)); Requires.NotNull(joinChild, nameof(joinChild)); if (parentTaskOrCollection == joinChild) { // Joining oneself would be pointless. return(default(JoinableTaskCollection.JoinRelease)); } using (parentTaskOrCollection.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { List <AsyncManualResetEvent>?eventsNeedNotify = null; lock (parentTaskOrCollection.JoinableTaskContext.SyncContextLock) { var joinableTask = joinChild as JoinableTask; if (joinableTask?.IsFullyCompleted == true) { return(default(JoinableTaskCollection.JoinRelease)); } ref JoinableTaskDependentData data = ref parentTaskOrCollection.GetJoinableTaskDependentData(); if (data.childDependentNodes is null) { data.childDependentNodes = new WeakKeyDictionary <IJoinableTaskDependent, int>(capacity: 2); } if (data.childDependentNodes.TryGetValue(joinChild, out int refCount) && !parentTaskOrCollection.NeedRefCountChildDependencies) { return(default(JoinableTaskCollection.JoinRelease)); } data.childDependentNodes[joinChild] = ++refCount; if (refCount == 1) { // This constitutes a significant change, so we should apply synchronous task tracking to the new child. joinChild.OnAddedToDependency(parentTaskOrCollection); IReadOnlyCollection <PendingNotification>?tasksNeedNotify = AddDependingSynchronousTaskToChild(parentTaskOrCollection, joinChild); if (tasksNeedNotify.Count > 0) { eventsNeedNotify = new List <AsyncManualResetEvent>(tasksNeedNotify.Count); foreach (PendingNotification taskToNotify in tasksNeedNotify) { AsyncManualResetEvent?notifyEvent = taskToNotify.SynchronousTask.RegisterPendingEventsForSynchrousTask(taskToNotify.TaskHasPendingMessages, taskToNotify.NewPendingMessagesCount); if (notifyEvent is object) { eventsNeedNotify.Add(notifyEvent); } } } parentTaskOrCollection.OnDependencyAdded(joinChild); } } // We explicitly do this outside our lock. if (eventsNeedNotify is object) { foreach (AsyncManualResetEvent?queueEvent in eventsNeedNotify) { queueEvent.PulseAll(); } } return(new JoinableTaskCollection.JoinRelease(parentTaskOrCollection, joinChild)); }
/// <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); }
/// <summary> /// Recursively adds this joinable and all its dependencies to the specified set, that are not yet completed. /// </summary> internal static void AddSelfAndDescendentOrJoinedJobs(IJoinableTaskDependent taskItem, HashSet <JoinableTask> joinables) { Requires.NotNull(taskItem, nameof(taskItem)); JoinableTaskDependentData.AddSelfAndDescendentOrJoinedJobs(taskItem, joinables); }
/// <summary> /// Gets a snapshot of all joined tasks. /// FOR DIAGNOSTICS COLLECTION ONLY. /// This method is expected to be used with the JTF lock. /// </summary> internal static IEnumerable <JoinableTask> GetAllDirectlyDependentJoinableTasks(IJoinableTaskDependent taskItem) { Requires.NotNull(taskItem, nameof(taskItem)); return(JoinableTaskDependentData.GetAllDirectlyDependentJoinableTasks(taskItem)); }