示例#1
0
        /// <summary>Initializes the target core.</summary>
        /// <param name="owningTarget">The target using this helper.</param>
        /// <param name="callAction">An action to invoke for all accepted items.</param>
        /// <param name="reorderingBuffer">The reordering buffer used by the owner; may be null.</param>
        /// <param name="dataflowBlockOptions">The options to use to configure this block. The target core assumes these options are immutable.</param>
        /// <param name="targetCoreOptions">Options for how the target core should behave.</param>
        internal TargetCore(
            ITargetBlock <TInput> owningTarget,
            Action <KeyValuePair <TInput, long> > callAction,
            IReorderingBuffer reorderingBuffer,
            ExecutionDataflowBlockOptions dataflowBlockOptions,
            TargetCoreOptions targetCoreOptions)
        {
            // Validate internal arguments
            Debug.Assert(owningTarget != null, "Core must be associated with a target block.");
            Debug.Assert(dataflowBlockOptions != null, "Options must be provided to configure the core.");
            Debug.Assert(callAction != null, "Action to invoke for each item is required.");

            // Store arguments and do additional initialization
            _owningTarget         = owningTarget;
            _callAction           = callAction;
            _reorderingBuffer     = reorderingBuffer;
            _dataflowBlockOptions = dataflowBlockOptions;
            _targetCoreOptions    = targetCoreOptions;
            _messages             = (dataflowBlockOptions.MaxDegreeOfParallelism == 1) ?
                                    (IProducerConsumerQueue <KeyValuePair <TInput, long> >) new SingleProducerSingleConsumerQueue <KeyValuePair <TInput, long> >() :
                                    (IProducerConsumerQueue <KeyValuePair <TInput, long> >) new MultiProducerMultiConsumerQueue <KeyValuePair <TInput, long> >();
            if (_dataflowBlockOptions.BoundedCapacity != System.Threading.Tasks.Dataflow.DataflowBlockOptions.Unbounded)
            {
                Debug.Assert(_dataflowBlockOptions.BoundedCapacity > 0, "Positive bounding count expected; should have been verified by options ctor");
                _boundingState = new BoundingStateWithPostponed <TInput>(_dataflowBlockOptions.BoundedCapacity);
            }
        }
示例#2
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);
            }
        }