/// <summary>Adds a target to the registry.</summary> /// <param name="target">The target to add.</param> /// <param name="linkOptions">The link options.</param> internal void Add(ref ITargetBlock <T> target, DataflowLinkOptions linkOptions) { Contract.Requires(target != null, "The target that is supposed to be linked must not be null."); Contract.Requires(linkOptions != null, "The link options must not be null."); LinkedTargetInfo targetInfo; // If the target already exists in the registry, replace it with a new NopLinkPropagator to maintain uniqueness if (_targetInformation.TryGetValue(target, out targetInfo)) { target = new NopLinkPropagator(_owningSource, target); } // Add the target to both stores, the list and the dictionary, which are used for different purposes var node = new LinkedTargetInfo(target, linkOptions); AddToList(node, linkOptions.Append); _targetInformation.Add(target, node); // Increment the optimization counter if needed Contract.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time."); if (node.RemainingMessages > 0) { _linksWithRemainingMessages++; } #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.DataflowBlockLinking(_owningSource, target); } #endif }
/// <summary>Removes the LinkedTargetInfo node from the doubly-linked list.</summary> /// <param name="node">The node to be removed.</param> internal void RemoveFromList(LinkedTargetInfo node) { Contract.Requires(node != null, "Node to remove is required."); Contract.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must be non-null before RemoveFromList."); LinkedTargetInfo previous = node.Previous; LinkedTargetInfo next = node.Next; // Remove the node by linking the adjacent nodes if (node.Previous != null) { node.Previous.Next = next; node.Previous = null; } if (node.Next != null) { node.Next.Previous = previous; node.Next = null; } // Adjust the list ends if (_firstTarget == node) { _firstTarget = next; } if (_lastTarget == node) { _lastTarget = previous; } Contract.Assert((_firstTarget != null) == (_lastTarget != null), "Both first and last node must either be null or non-null after RemoveFromList."); }
/// <summary>Adds a LinkedTargetInfo node to the doubly-linked list.</summary> /// <param name="node">The node to be added.</param> /// <param name="append">Whether to append or to prepend the node.</param> internal void AddToList(LinkedTargetInfo node, bool append) { Contract.Requires(node != null, "Requires a node to be added."); // If the list is empty, assign the ends to point to the new node and we are done if (_firstTarget == null && _lastTarget == null) { _firstTarget = _lastTarget = node; } else { Contract.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must either be null or non-null."); Contract.Assert(_lastTarget.Next == null, "The last node must not have a successor."); Contract.Assert(_firstTarget.Previous == null, "The first node must not have a predecessor."); if (append) { // Link the new node to the end of the existing list node.Previous = _lastTarget; _lastTarget.Next = node; _lastTarget = node; } else { // Link the new node to the front of the existing list node.Next = _firstTarget; _firstTarget.Previous = node; _firstTarget = node; } } Contract.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must be non-null after AddToList."); }
/// <summary>Clears the target registry entry points while allowing subsequent traversals of the linked list.</summary> internal LinkedTargetInfo ClearEntryPoints() { // Save _firstTarget so we can return it LinkedTargetInfo firstTarget = _firstTarget; // Clear out the entry points _firstTarget = _lastTarget = null; _targetInformation.Clear(); Contract.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time."); _linksWithRemainingMessages = 0; return(firstTarget); }
/// <summary>Propagated completion to the targets of the given linked list.</summary> /// <param name="firstTarget">The head of a saved linked list.</param> internal void PropagateCompletion(LinkedTargetInfo firstTarget) { Contract.Assert(_owningSource.Completion.IsCompleted, "The owning source must have completed before propagating completion."); // Cache the owning source's completion task to avoid calling the getter many times Task owningSourceCompletion = _owningSource.Completion; // Propagate completion to those targets that have requested it for (LinkedTargetInfo node = firstTarget; node != null; node = node.Next) { if (node.PropagateCompletion) { Common.PropagateCompletion(owningSourceCompletion, node.Target, Common.AsyncExceptionHandler); } } }