/// <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));
 }