Normally you use the CancellationToken
option in order to control the cancellation of a dataflow block, using an external CancellationTokenSource
. Canceling the block (assuming that its a TransformBlock
) has the following immediate effects:
- The block stops accepting incoming messages. Invoking its
Post
returns false
, meaning that the offered message is rejected.
- The messages that are currently stored in the block's internal input buffer are immediately discarded. These messages are lost. They will not be processed or propagated.
If the block is not currently processing any messages, the following effects will also follow immediately. Otherwise they will follow when the processing of all currently processed messages is completed:
- All the processed messages that are currently stored in this block's output buffer are discarded. The last processed messages (the messages that were in the middle of processing when the cancellation occurred) will not be propagated to linked blocks downstream.
- Any pending asynchronous
SendAsync
operations targeting the block, that were in-flight when the cancellation occurred, will complete with a result of false
(meaning "non accepted").
- The
Task
that represents the Completion
of the block transitions to the Canceled
state. In other words this task's IsCanceled
property becomes true
.
You can achieve all but the last effect directly, without using the CancellationToken
option, by invoking the block's Fault
method. This method is accessible through the IDataflowBlock
interface that all blocks implement. You can use it like this:
((IDataflowBlock)block).Fault(new OperationCanceledException());
The difference is that the Completion
task will now become Faulted
instead of Canceled
. This difference may or may not be important, depending on the situation. If you just await
the Completion
, which is how this property is normally used, in both cases a OperationCanceledException
will be thrown. So if you don't need to do anything fancy with the Completion
property, and you also want to avoid configuring the CancellationToken
for some reason, you could consider this trick as an option.
Update: Behavior when the cancellation occurs after the Complete
method has been invoked, in other words when the block is already in its completion phase, but has not completed yet:
- If the block is a processing block, like a
TransformBlock
, all of the above will happen just the same. The block will transition soon to the Canceled
state.
- If the block is a non-processing block, like a
BufferBlock<T>
, the (3) from the list above will not happen. The output buffer of a BufferBlock<T>
is not emptied, when the cancellation happen after the invocation of the Complete
method. See this GitHib issue for a demonstration of this behavior. Please take into consideration that the Complete
method may be invoked not only manually, but also automatically, if the block has been linked as the target of a source block, with the PropagateCompletion
configuration enabled. You may want to check out this question, to understand fully the implications of this behavior. Long story short, canceling all the blocks of a dataflow pipeline that contains a BufferBlock<T>
, does not guarantee that the pipeline will terminate.
Side note: When both the Complete
and Fault
methods are invoked, whatever was invoked first prevails regarding the final status of the block. If the Complete
was invoked first, the block will complete with status RanToCompletion
. If the Fault
was invoked first, the block will complete with status Faulted
. Faulting a Complete
d block has still an effect though: it empties its internal input buffer.
how I can use
? If you want to cancel the operation you can call.Cancel
on the_cts
object (assuming its aCancellationTokenSource
. Of course you can always check if there is a cancellation requested withIsCancellationRequested
– BernardinaBufferBlock
constructor (throughDataflowBlockOptions
). Then, if I pass this instance ofBufferBlock
into some library's produce method, that method has no way to access the token fromBufferBlock
. So what is usage of this? – FireboxDataflowBlockOptions.CancellationToken
or what is the purpose of passing it into the constructor ofBufferBlock
– Firebox