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); } } }
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); } } }