Exemplo n.º 1
0
        private void ScheduleConsumerIfNecessary(bool isReplica)
        {
            // If there's currently no active task...
            if (_activeConsumer == null)
            {
                // Create a new consumption task and try to set it as current as long as there's still no other task
                var newConsumer = new Task(
                    state => ((SpscTargetCore <TInput>)state).ProcessMessagesLoopCore(),
                    this, CancellationToken.None, Common.GetCreationOptionsForTask(isReplica));
                if (Interlocked.CompareExchange(ref _activeConsumer, newConsumer, null) == null)
                {
                    // We won the race.  This task is now the consumer.

#if FEATURE_TRACING
                    DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
                    if (etwLog.IsEnabled())
                    {
                        etwLog.TaskLaunchedForMessageHandling(
                            _owningTarget, newConsumer, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages, _messages.Count);
                    }
#endif

                    // Start the task.  In the erroneous case where the scheduler throws an exception,
                    // just allow it to propagate. Our other option would be to fault the block with
                    // that exception, but in order for the block to complete we need to schedule a consumer
                    // task to do so, and it's very likely that if the scheduler is throwing an exception
                    // now, it would do so again.
                    newConsumer.Start(_dataflowBlockOptions.TaskScheduler);
                }
            }
        }
Exemplo n.º 2
0
        /// <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
        }
Exemplo n.º 3
0
        private void ProcessAsyncIfNecessary_Slow(bool repeat)
        {
            Debug.Assert(HasRoomForMoreServiceTasks, "There must be room to process asynchronously.");
            Common.ContractAssertMonitorStatus(IncomingLock, held: true);

            // Determine preconditions to launching a processing task
            bool messagesAvailableOrPostponed =
                !_messages.IsEmpty ||
                (!_decliningPermanently && _boundingState != null && _boundingState.CountIsLessThanBound && _boundingState.PostponedMessages.Count > 0);

            // If all conditions are met, launch away
            if (messagesAvailableOrPostponed && !CanceledOrFaulted)
            {
                // Any book keeping related to the processing task like incrementing the
                // DOP counter or eventually recording the tasks reference must be done
                // before the task starts. That is because the task itself will do the
                // reverse operation upon its completion.
                _numberOfOutstandingOperations++;
                if (UsesAsyncCompletion)
                {
                    _numberOfOutstandingServiceTasks++;
                }

                var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore <TInput>)thisTargetCore).ProcessMessagesLoopCore(), this,
                                                      Common.GetCreationOptionsForTask(repeat));

#if FEATURE_TRACING
                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.TaskLaunchedForMessageHandling(
                        _owningTarget, taskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
                        _messages.Count + (_boundingState != null ? _boundingState.PostponedMessages.Count : 0));
                }
#endif

                // Start the task handling scheduling exceptions
                Exception exception = Common.StartTaskSafe(taskForInputProcessing, _dataflowBlockOptions.TaskScheduler);
                if (exception != null)
                {
                    // Get out from under currently held locks. Complete re-acquires the locks it needs.
                    Task.Factory.StartNew(exc => Complete(exception: (Exception)exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true,
                                                          unwrapInnerExceptions: false, revertProcessingState: true),
                                          exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>Actually removes the target from the registry.</summary>
        /// <param name="target">The target to remove.</param>
        /// <param name="onlyIfReachedMaxMessages">
        /// Only remove the target if it's configured to be unlinked after one propagation.
        /// </param>
        private void Remove_Slow(ITargetBlock <T> target, bool onlyIfReachedMaxMessages)
        {
            Contract.Requires(target != null, "Target to remove is required.");

            // Make sure we've intended to go the slow route
            Contract.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
            Contract.Assert(!onlyIfReachedMaxMessages || _linksWithRemainingMessages > 0, "We shouldn't have ended on the slow path.");

            // If the target is registered...
            LinkedTargetInfo node;

            if (_targetInformation.TryGetValue(target, out node))
            {
                Contract.Assert(node != null, "The LinkedTargetInfo node referenced in the Dictionary must be non-null.");

                // Remove the target, if either there's no constraint on the removal
                // or if this was the last remaining message.
                if (!onlyIfReachedMaxMessages || node.RemainingMessages == 1)
                {
                    RemoveFromList(node);
                    _targetInformation.Remove(target);

                    // Decrement the optimization counter if needed
                    if (node.RemainingMessages == 0)
                    {
                        _linksWithRemainingMessages--;
                    }
                    Contract.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
#if FEATURE_TRACING
                    DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
                    if (etwLog.IsEnabled())
                    {
                        etwLog.DataflowBlockUnlinking(_owningSource, target);
                    }
#endif
                }
                // If the target is to stay and we are counting the remaining messages for this link, decrement the counter
                else if (node.RemainingMessages > 0)
                {
                    Contract.Assert(node.RemainingMessages > 1, "The target should have been removed, because there are no remaining messages.");
                    node.RemainingMessages--;
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Completes the block.  This must only be called once, and only once all of the completion conditions are met.
        /// </summary>
        private void CompleteBlockOncePossible()
        {
            Debug.Assert(_completionReserved, "Should only invoke once completion has been reserved.");

            // Dump any messages that might remain in the queue, which could happen if we completed due to exceptions.
            TInput dumpedMessage;

            while (_messages.TryDequeue(out dumpedMessage))
            {
                ;
            }

            // Complete the completion task
            bool result;

            if (_exceptions != null)
            {
                Exception[] exceptions;
                lock (_exceptions) exceptions = _exceptions.ToArray();
                result = CompletionSource.TrySetException(exceptions);
            }
            else
            {
                result = CompletionSource.TrySetResult(default(VoidResult));
            }
            Debug.Assert(result, "Expected completion task to not yet be completed");
            // We explicitly do not set the _activeTask to null here, as that would
            // allow for races where a producer calling OfferMessage could end up
            // seeing _activeTask as null and queueing a new consumer task even
            // though the block has completed.

#if FEATURE_TRACING
            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.DataflowBlockCompleted(_owningTarget);
            }
#endif
        }