Esempio n. 1
0
        /// <summary>Called when postponed messages may need to be consumed.</summary>
        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
        internal void ConsumeAsyncIfNecessary(bool isReplacementReplica = false)
        {
            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
            Debug.Assert(_boundingState != null, "Must be in bounded mode.");

            if (!_targetDecliningPermanently &&
                _boundingState.TaskForInputProcessing == null &&
                _boundingState.PostponedMessages.Count > 0 &&
                _boundingState.CountIsLessThanBound)
            {
                // Create task and store into _taskForInputProcessing prior to scheduling the task
                // so that _taskForInputProcessing will be visibly set in the task loop.
                _boundingState.TaskForInputProcessing =
                    new Task(state => ((BufferBlock <T>)state !).ConsumeMessagesLoopCore(), this,
                             Common.GetCreationOptionsForTask(isReplacementReplica));

                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.TaskLaunchedForMessageHandling(
                        this, _boundingState.TaskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
                        _boundingState.PostponedMessages.Count);
                }

                // Start the task handling scheduling exceptions
                Exception?exception = Common.StartTaskSafe(_boundingState.TaskForInputProcessing, _source.DataflowBlockOptions.TaskScheduler);
                if (exception != null)
                {
                    // Get out from under currently held locks. CompleteCore re-acquires the locks it needs.
                    Task.Factory.StartNew(exc => CompleteCore(exception: (Exception)exc !, storeExceptionEvenIfAlreadyCompleting: true, revertProcessingState: true),
                                          exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
                }
            }
        }
Esempio n. 2
0
        /// <summary>Initializes the <see cref="BufferBlock{T}"/> with the specified <see cref="DataflowBlockOptions"/>.</summary>
        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BufferBlock{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
        public BufferBlock(DataflowBlockOptions dataflowBlockOptions)
        {
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException("dataflowBlockOptions");
            }
            Contract.EndContractBlock();

            // Ensure we have options that can't be changed by the caller
            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // Initialize bounding state if necessary
            Action <ISourceBlock <T>, int> onItemsRemoved = null;

            if (dataflowBlockOptions.BoundedCapacity > 0)
            {
                onItemsRemoved = (owningSource, count) => ((BufferBlock <T>)owningSource).OnItemsRemoved(count);
                _boundingState = new BoundingStateWithPostponedAndTask <T>(dataflowBlockOptions.BoundedCapacity);
            }

            // Initialize the source state
            _source = new SourceCore <T>(this, dataflowBlockOptions,
                                         owningSource => ((BufferBlock <T>)owningSource).Complete(),
                                         onItemsRemoved);

            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
            // In those cases we need to fault the target half to drop its buffered messages and to release its
            // reservations. This should not create an infinite loop, because all our implementations are designed
            // to handle multiple completion requests and to carry over only one.
#if NET_4_0_ABOVE
            _source.Completion.ContinueWith((completed, state) =>
            {
                var thisBlock = ((BufferBlock <T>)state) as IDataflowBlock;
                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
                thisBlock.Fault(completed.Exception);
            }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
#else
            Action <Task> continuationAction = completed =>
            {
                var thisBlock = this as IDataflowBlock;
                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
                thisBlock.Fault(completed.Exception);
            };
            _source.Completion.ContinueWith(continuationAction, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
#endif
            // Handle async cancellation requests by declining on the target
            Common.WireCancellationToComplete(
                dataflowBlockOptions.CancellationToken, _source.Completion, owningSource => ((BufferBlock <T>)owningSource).Complete(), this);
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
#endif
        }
Esempio n. 3
0
        /// <summary>Initializes the <see cref="WriteOnceBlock{T}"/> with the specified <see cref="DataflowBlockOptions"/>.</summary>
        /// <param name="cloningFunction">
        /// The function to use to clone the data when offered to other blocks.
        /// This may be null to indicate that no cloning need be performed.
        /// </param>
        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="WriteOnceBlock{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
        public WriteOnceBlock(Func <T, T> cloningFunction, DataflowBlockOptions dataflowBlockOptions)
        {
            // Validate arguments
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException("dataflowBlockOptions");
            }
            Contract.EndContractBlock();

            // Store the option
            _cloningFunction      = cloningFunction;
            _dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // The target registry also serves as our ValueLock,
            // and thus must always be initialized, even if the block is pre-canceled, as
            // subsequent usage of the block may run through code paths that try to take this lock.
            _targetRegistry = new TargetRegistry <T>(this);

            // If a cancelable CancellationToken has been passed in,
            // we need to initialize the completion task's TCS now.
            if (dataflowBlockOptions.CancellationToken.CanBeCanceled)
            {
                _lazyCompletionTaskSource = new TaskCompletionSource <VoidResult>();

                // If we've already had cancellation requested, do as little work as we have to
                // in order to be done.
                if (dataflowBlockOptions.CancellationToken.IsCancellationRequested)
                {
                    _completionReserved = _decliningPermanently = true;

                    // Cancel the completion task's TCS
                    _lazyCompletionTaskSource.SetCanceled();
                }
                else
                {
                    // Handle async cancellation requests by declining on the target
                    Common.WireCancellationToComplete(
                        dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock <T>)state).Complete(), this);
                }
            }
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
#endif
        }
Esempio n. 4
0
        /// <summary>Completes the block.</summary>
        /// <remarks>
        /// This is called only once.
        /// </remarks>
        private void CompleteBlock(IList <Exception> exceptions)
        {
            // Do not invoke the CompletionTaskSource property if there is a chance that _lazyCompletionTaskSource
            // has not been initialized yet and we may have to complete normally, because that would defeat the
            // sole purpose of the TCS being lazily initialized.

            Contract.Requires(_lazyCompletionTaskSource == null || !_lazyCompletionTaskSource.Task.IsCompleted, "The task completion source must not be completed. This must be the only thread that ever completes the block.");

            // Save the linked list of targets so that it could be traversed later to propagate completion
            TargetRegistry <T> .LinkedTargetInfo linkedTargets = _targetRegistry.ClearEntryPoints();

            // Complete the block's completion task
            if (exceptions != null && exceptions.Count > 0)
            {
                CompletionTaskSource.TrySetException(exceptions);
            }
            else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
            {
                CompletionTaskSource.TrySetCanceled();
            }
            else
            {
                // Safely try to initialize the completion task's TCS with a cached completed TCS.
                // If our attempt succeeds (CompareExchange returns null), we have nothing more to do.
                // If the completion task's TCS was already initialized (CompareExchange returns non-null),
                // we have to complete that TCS instance.
                if (Interlocked.CompareExchange(ref _lazyCompletionTaskSource, Common.CompletedVoidResultTaskCompletionSource, null) != null)
                {
                    _lazyCompletionTaskSource.TrySetResult(default(VoidResult));
                }
            }

            // Now that the completion task is completed, we may propagate completion to the linked targets
            _targetRegistry.PropagateCompletion(linkedTargets);
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCompleted(this);
            }
#endif
        }
Esempio n. 5
0
        private void CompleteBlockAsync(IList <Exception> exceptions)
        {
            Contract.Requires(_decliningPermanently, "We may get here only after we have started to decline permanently.");
            Contract.Requires(_completionReserved, "We may get here only after we have reserved completion.");
            Common.ContractAssertMonitorStatus(ValueLock, held: false);

            // If there is no exceptions list, we offer the message around, and then complete.
            // If there is an exception list, we complete without offering the message.
            if (exceptions == null)
            {
                // Offer the message to any linked targets and complete the block asynchronously to avoid blocking the caller
                var taskForOutputProcessing = new Task(state => ((WriteOnceBlock <T>)state).OfferToTargetsAndCompleteBlock(), this,
                                                       Common.GetCreationOptionsForTask());

#if FEATURE_TRACING
                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.TaskLaunchedForMessageHandling(
                        this, taskForOutputProcessing, DataflowEtwProvider.TaskLaunchedReason.OfferingOutputMessages, _header.IsValid ? 1 : 0);
                }
#endif

                // Start the task handling scheduling exceptions
                Exception exception = Common.StartTaskSafe(taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler);
                if (exception != null)
                {
                    CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: true);
                }
            }
            else
            {
                // Complete the block asynchronously to avoid blocking the caller
                Task.Factory.StartNew(state =>
                {
                    Tuple <WriteOnceBlock <T>, IList <Exception> > blockAndList = (Tuple <WriteOnceBlock <T>, IList <Exception> >)state;
                    blockAndList.Item1.CompleteBlock(blockAndList.Item2);
                },
                                      Tuple.Create(this, exceptions), CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
            }
        }
Esempio n. 6
0
        /// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified delegate and options.</summary>
        /// <param name="action">The action to invoke with each data element received.</param>
        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="ActionBlock{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
        private ActionBlock(Delegate action, ExecutionDataflowBlockOptions dataflowBlockOptions)
        {
            // Validate arguments
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException("dataflowBlockOptions");
            }
            Contract.Ensures((_spscTarget != null) ^ (_defaultTarget != null), "One and only one of the two targets must be non-null after construction");
            Contract.EndContractBlock();

            // Ensure we have options that can't be changed by the caller
            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // Based on the mode, initialize the target.  If the user specifies SingleProducerConstrained,
            // we'll try to employ an optimized mode under a limited set of circumstances.
            var syncAction = action as Action <TInput>;

            if (syncAction != null &&
                dataflowBlockOptions.SingleProducerConstrained &&
                dataflowBlockOptions.MaxDegreeOfParallelism == 1 &&
                !dataflowBlockOptions.CancellationToken.CanBeCanceled &&
                dataflowBlockOptions.BoundedCapacity == DataflowBlockOptions.Unbounded)
            {
                // Initialize the SPSC fast target to handle the bulk of the processing.
                // The SpscTargetCore is only supported when BoundedCapacity, CancellationToken,
                // and MaxDOP are all their default values.  It's also only supported for sync
                // delegates and not for async delegates.
                _spscTarget = new SpscTargetCore <TInput>(this, syncAction, dataflowBlockOptions);
            }
            else
            {
                // Initialize the TargetCore which handles the bulk of the processing.
                // The default target core can handle all options and delegate flavors.

                if (syncAction != null) // sync
                {
                    _defaultTarget = new TargetCore <TInput>(this,
                                                             messageWithId => ProcessMessage(syncAction, messageWithId),
                                                             null, dataflowBlockOptions, TargetCoreOptions.RepresentsBlockCompletion);
                }
                else // async
                {
                    var asyncAction = action as Func <TInput, Task>;
                    Debug.Assert(asyncAction != null, "action is of incorrect delegate type");
                    _defaultTarget = new TargetCore <TInput>(this,
                                                             messageWithId => ProcessMessageWithTask(asyncAction, messageWithId),
                                                             null, dataflowBlockOptions, TargetCoreOptions.RepresentsBlockCompletion | TargetCoreOptions.UsesAsyncCompletion);
                }

                // Handle async cancellation requests by declining on the target
                Common.WireCancellationToComplete(
                    dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore <TInput>)state).Complete(exception: null, dropPendingMessages: true), _defaultTarget);
            }
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
#endif
        }
Esempio n. 7
0
        /// <summary>Initializes this <see cref="BatchedJoinBlock{T1,T2}"/> with the specified configuration.</summary>
        /// <param name="batchSize">The number of items to group into a batch.</param>
        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BatchedJoinBlock{T1,T2}"/>.</param>
        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
        public BatchedJoinBlock(int batchSize, GroupingDataflowBlockOptions dataflowBlockOptions)
        {
            // Validate arguments
            if (batchSize < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(batchSize), SR.ArgumentOutOfRange_GenericPositive);
            }
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException(nameof(dataflowBlockOptions));
            }
            if (!dataflowBlockOptions.Greedy)
            {
                throw new ArgumentException(SR.Argument_NonGreedyNotSupported, nameof(dataflowBlockOptions));
            }
            if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded)
            {
                throw new ArgumentException(SR.Argument_BoundedCapacityNotSupported, nameof(dataflowBlockOptions));
            }

            // Store arguments
            _batchSize           = batchSize;
            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // Configure the source
            _source = new SourceCore <Tuple <IList <T1>, IList <T2> > >(
                this, dataflowBlockOptions, owningSource => ((BatchedJoinBlock <T1, T2>)owningSource).CompleteEachTarget());

            // The action to run when a batch should be created.  This is typically called
            // when we have a full batch, but it will also be called when we're done receiving
            // messages, and thus when there may be a few stragglers we need to make a batch out of.
            Action createBatchAction = () =>
            {
                if (_target1.Count > 0 || _target2.Count > 0)
                {
                    _source.AddMessage(Tuple.Create(_target1.GetAndEmptyMessages(), _target2.GetAndEmptyMessages()));
                }
            };

            // Configure the targets
            _sharedResources = new BatchedJoinBlockTargetSharedResources(
                batchSize, dataflowBlockOptions,
                createBatchAction,
                () =>
            {
                createBatchAction();
                _source.Complete();
            },
                _source.AddException,
                Complete);
            _target1 = new BatchedJoinBlockTarget <T1>(_sharedResources);
            _target2 = new BatchedJoinBlockTarget <T2>(_sharedResources);

            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
            // In those cases we need to fault the target half to drop its buffered messages and to release its
            // reservations. This should not create an infinite loop, because all our implementations are designed
            // to handle multiple completion requests and to carry over only one.
            _source.Completion.ContinueWith((completed, state) =>
            {
                var thisBlock = ((BatchedJoinBlock <T1, T2>)state !) as IDataflowBlock;
                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
                thisBlock.Fault(completed.Exception !);
            }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

            // Handle async cancellation requests by declining on the target
            Common.WireCancellationToComplete(
                dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock <T1, T2>)state !).CompleteEachTarget(), this);
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
#endif
        }
Esempio n. 8
0
        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
        /// <param name="transformSync">The synchronous function to invoke with each data element received.</param>
        /// <param name="transformAsync">The asynchronous function to invoke with each data element received.</param>
        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformManyBlock{TInput,TOutput}"/>.</param>
        /// <exception cref="System.ArgumentNullException">The <paramref name="transformSync"/> and <paramref name="transformAsync"/> are both null (Nothing in Visual Basic).</exception>
        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
        private TransformManyBlock(Func <TInput, IEnumerable <TOutput> > transformSync, Func <TInput, Task <IEnumerable <TOutput> > > transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions)
        {
            // Validate arguments.  It's ok for the filterFunction to be null, but not the other parameters.
            if (transformSync == null && transformAsync == null)
            {
                throw new ArgumentNullException("transform");
            }
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException(nameof(dataflowBlockOptions));
            }

            Contract.Requires(transformSync == null ^ transformAsync == null, "Exactly one of transformSync and transformAsync must be null.");
            Contract.EndContractBlock();

            // Ensure we have options that can't be changed by the caller
            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // Initialize onItemsRemoved delegate if necessary
            Action <ISourceBlock <TOutput>, int> onItemsRemoved = null;

            if (dataflowBlockOptions.BoundedCapacity > 0)
            {
                onItemsRemoved = (owningSource, count) => ((TransformManyBlock <TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
            }

            // Initialize source component
            _source = new SourceCore <TOutput>(this, dataflowBlockOptions,
                                               owningSource => ((TransformManyBlock <TInput, TOutput>)owningSource)._target.Complete(exception: null, dropPendingMessages: true),
                                               onItemsRemoved);

            // If parallelism is employed, we will need to support reordering messages that complete out-of-order.
            // However, a developer can override this with EnsureOrdered == false.
            if (dataflowBlockOptions.SupportsParallelExecution && dataflowBlockOptions.EnsureOrdered)
            {
                _reorderingBuffer = new ReorderingBuffer <IEnumerable <TOutput> >(
                    this, (source, messages) => ((TransformManyBlock <TInput, TOutput>)source)._source.AddMessages(messages));
            }

            // Create the underlying target and source
            if (transformSync != null) // sync
            {
                // If an enumerable function was provided, we can use synchronous completion, meaning
                // that the target will consider a message fully processed as soon as the
                // delegate returns.
                _target = new TargetCore <TInput>(this,
                                                  messageWithId => ProcessMessage(transformSync, messageWithId),
                                                  _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.None);
            }
            else // async
            {
                Debug.Assert(transformAsync != null, "Incorrect delegate type.");

                // If a task-based function was provided, we need to use asynchronous completion, meaning
                // that the target won't consider a message completed until the task
                // returned from that delegate has completed.
                _target = new TargetCore <TInput>(this,
                                                  messageWithId => ProcessMessageWithTask(transformAsync, messageWithId),
                                                  _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.UsesAsyncCompletion);
            }

            // Link up the target half with the source half.  In doing so,
            // ensure exceptions are propagated, and let the source know no more messages will arrive.
            // As the target has completed, and as the target synchronously pushes work
            // through the reordering buffer when async processing completes,
            // we know for certain that no more messages will need to be sent to the source.
            _target.Completion.ContinueWith((completed, state) =>
            {
                var sourceCore = (SourceCore <TOutput>)state;
                if (completed.IsFaulted)
                {
                    sourceCore.AddAndUnwrapAggregateException(completed.Exception);
                }
                sourceCore.Complete();
            }, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);

            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
            // In those cases we need to fault the target half to drop its buffered messages and to release its
            // reservations. This should not create an infinite loop, because all our implementations are designed
            // to handle multiple completion requests and to carry over only one.
            _source.Completion.ContinueWith((completed, state) =>
            {
                var thisBlock = ((TransformManyBlock <TInput, TOutput>)state) as IDataflowBlock;
                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
                thisBlock.Fault(completed.Exception);
            }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

            // Handle async cancellation requests by declining on the target
            Common.WireCancellationToComplete(
                dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore <TInput>)state).Complete(exception: null, dropPendingMessages: true), _target);
#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
#endif
        }
Esempio n. 9
0
        private void Initialize(
            Action <KeyValuePair <TInput, long> > processMessageAction,
            ExecutionDataflowBlockOptions dataflowBlockOptions,
            [NotNull] ref SourceCore <TOutput>?source,
            [NotNull] ref TargetCore <TInput>?target,
            ref ReorderingBuffer <IEnumerable <TOutput> >?reorderingBuffer,
            TargetCoreOptions targetCoreOptions)
        {
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException(nameof(dataflowBlockOptions));
            }

            // Ensure we have options that can't be changed by the caller
            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();

            // Initialize onItemsRemoved delegate if necessary
            Action <ISourceBlock <TOutput>, int>?onItemsRemoved = null;

            if (dataflowBlockOptions.BoundedCapacity > 0)
            {
                onItemsRemoved = (owningSource, count) => ((TransformManyBlock <TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
            }

            // Initialize source component
            source = new SourceCore <TOutput>(this, dataflowBlockOptions,
                                              owningSource => ((TransformManyBlock <TInput, TOutput>)owningSource)._target.Complete(exception: null, dropPendingMessages: true),
                                              onItemsRemoved);

            // If parallelism is employed, we will need to support reordering messages that complete out-of-order.
            // However, a developer can override this with EnsureOrdered == false.
            if (dataflowBlockOptions.SupportsParallelExecution && dataflowBlockOptions.EnsureOrdered)
            {
                reorderingBuffer = new ReorderingBuffer <IEnumerable <TOutput> >(
                    this, (source, messages) => ((TransformManyBlock <TInput, TOutput>)source)._source.AddMessages(messages));
            }

            // Create the underlying target and source
            target = new TargetCore <TInput>(this, processMessageAction, _reorderingBuffer, dataflowBlockOptions, targetCoreOptions);

            // Link up the target half with the source half.  In doing so,
            // ensure exceptions are propagated, and let the source know no more messages will arrive.
            // As the target has completed, and as the target synchronously pushes work
            // through the reordering buffer when async processing completes,
            // we know for certain that no more messages will need to be sent to the source.
            target.Completion.ContinueWith((completed, state) =>
            {
                var sourceCore = (SourceCore <TOutput>)state !;
                if (completed.IsFaulted)
                {
                    sourceCore.AddAndUnwrapAggregateException(completed.Exception !);
                }
                sourceCore.Complete();
            }, source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);

            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
            // In those cases we need to fault the target half to drop its buffered messages and to release its
            // reservations. This should not create an infinite loop, because all our implementations are designed
            // to handle multiple completion requests and to carry over only one.
            source.Completion.ContinueWith((completed, state) =>
            {
                var thisBlock = ((TransformManyBlock <TInput, TOutput>)state !) as IDataflowBlock;
                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
                thisBlock.Fault(completed.Exception !);
            }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

            // Handle async cancellation requests by declining on the target
            Common.WireCancellationToComplete(
                dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore <TInput>)state !).Complete(exception: null, dropPendingMessages: true), target);
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
            }
        }