コード例 #1
0
        /// <summary>Processes the message with a user-provided transform function that returns an observable.</summary>
        /// <param name="function">The transform function to use to process the message.</param>
        /// <param name="messageWithId">The message to be processed.</param>
        private void ProcessMessageWithTask(Func <TInput, Task <IEnumerable <TOutput> > > function, KeyValuePair <TInput, long> messageWithId)
        {
            Debug.Assert(function != null, "Function to invoke is required.");

            // Run the transform function to get the resulting task
            Task <IEnumerable <TOutput> >?task = null;
            Exception?caughtException          = null;

            try
            {
                task = function(messageWithId.Key);
            }
            catch (Exception exc) { caughtException = exc; }

            // If no task is available, either because null was returned or an exception was thrown, we're done.
            if (task == null)
            {
                // If we didn't get a task because an exception occurred, store it
                // (or if the exception was cancellation, just ignore it).
                if (caughtException != null && !Common.IsCooperativeCancellation(caughtException))
                {
                    Common.StoreDataflowMessageValueIntoExceptionData(caughtException, messageWithId.Key);
                    _target.Complete(caughtException, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
                }

                // Notify that we're done with this input and that we got no output for the input.
                if (_reorderingBuffer != null)
                {
                    // If there's a reordering buffer, "store" an empty output.  This will
                    // internally both update the output buffer and decrement the bounding count
                    // accordingly.
                    StoreOutputItems(messageWithId, null);
                    _target.SignalOneAsyncMessageCompleted();
                }
                else
                {
                    // As a fast path if we're not reordering, decrement the bounding
                    // count as part of our signaling that we're done, since this will
                    // internally take the lock only once, whereas the above path will
                    // take the lock twice.
                    _target.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
                }
                return;
            }

            // We got back a task.  Now wait for it to complete and store its results.
            // Unlike with TransformBlock and ActionBlock, We run the continuation on the user-provided
            // scheduler as we'll be running user code through enumerating the returned enumerable.
            task.ContinueWith((completed, state) =>
            {
                var tuple = (Tuple <TransformManyBlock <TInput, TOutput>, KeyValuePair <TInput, long> >)state !;
                tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2);
            }, Tuple.Create(this, messageWithId),
                              CancellationToken.None,
                              Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously),
                              _source.DataflowBlockOptions.TaskScheduler);
        }
コード例 #2
0
        /// <summary>Processes the message with a user-provided action that returns a task.</summary>
        /// <param name="action">The action to use to process the message.</param>
        /// <param name="messageWithId">The message to be processed.</param>
        private void ProcessMessageWithTask(Func <TInput, Task> action, KeyValuePair <TInput, long> messageWithId)
        {
            Debug.Assert(action != null, "action needed for processing");
            Debug.Assert(_defaultTarget != null);

            // Run the action to get the task that represents the operation's completion
            Task?     task            = null;
            Exception?caughtException = null;

            try
            {
                task = action(messageWithId.Key);
            }
            catch (Exception exc) { caughtException = exc; }

            // If no task is available, we're done.
            if (task == null)
            {
                // If we didn't get a task because an exception occurred,
                // store it (if the exception was cancellation, just ignore it).
                if (caughtException != null && !Common.IsCooperativeCancellation(caughtException))
                {
                    Common.StoreDataflowMessageValueIntoExceptionData(caughtException, messageWithId.Key);
                    _defaultTarget.Complete(caughtException, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
                }

                // Signal that we're done this async operation.
                _defaultTarget.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
                return;
            }
            else if (task.IsCompleted)
            {
                AsyncCompleteProcessMessageWithTask(task);
            }
            else
            {
                // Otherwise, join with the asynchronous operation when it completes.
                task.ContinueWith((completed, state) =>
                {
                    ((ActionBlock <TInput>)state !).AsyncCompleteProcessMessageWithTask(completed);
                }, this, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default);
            }
        }