Exemple #1
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
        }
        /// <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
        }
        private List <Exception> OfferToTargets()
        {
            Common.ContractAssertMonitorStatus(ValueLock, held: false);

            // If there is a message, offer it to everyone.  Return values
            // don't matter, because we only get one message and then complete,
            // and everyone who wants a copy can get a copy.
            List <Exception> exceptions = null;

            if (HasValue)
            {
                TargetRegistry <T> .LinkedTargetInfo cur = _targetRegistry.FirstTargetNode;
                while (cur != null)
                {
                    TargetRegistry <T> .LinkedTargetInfo next = cur.Next;
                    ITargetBlock <T> target = cur.Target;
                    try
                    {
                        // Offer the message.  If there's a cloning function, we force the target to
                        // come back to us to consume the message, allowing us the opportunity to run
                        // the cloning function once we know they want the data.  If there is no cloning
                        // function, there's no reason for them to call back here.
                        bool useCloning = _cloningFunction != null;
                        target.OfferMessage(_header, _value, this, consumeToAccept: useCloning);
                    }
                    catch (Exception exc)
                    {
                        // Track any erroneous exceptions that may occur
                        // and return them to the caller so that they may
                        // be logged in the completion task.
                        Common.StoreDataflowMessageValueIntoExceptionData(exc, _value);
                        Common.AddException(ref exceptions, exc);
                    }
                    cur = next;
                }
            }
            return(exceptions);
        }