Search code examples
c#asynchronousmediatorcancellation-token

C# CancellationTokens: Should Chained Calls Pass the Same One Down?


I'm using Mediator currently, and its Handle() method accepts a CancellationToken as a parameter. I have a scenario where I've chained a couple handling requests together.

public async Task<Foo> Handle(CreateFoo createFoo, CancellationToken cancellationToken)
{
    // Re-use CancellationToken, or use a new one?
    Bar newBar = await _mediator.Send(createFoo.CreateBar, cancellationToken);

    // Rest of Handle code snipped.
}

Before I was not passing the token into the Send() method call here, but then I was thinking the token should be passed down since it'd be part of the same request. Is this correct, or should I not re-use the cancellation token the way I did above?

I assume that the answer to this question applies not only to Mediator, but anything in C# that uses CancellationToken.


Solution

  • Cancellation token is a light weight struct. Its copied by value. And it is meant to be passed down to separate levels of the functions.

    So It is okay to pass cancellation token as much as you want.

    But make sure your cancellation is always tied with a single operation. If the operation is cancelled, it is cancelled. You cant revert the cancellation and re use the token.

    As long as you keep that in mind for the design, it seems your way of passing the cancellation token is correct.

    And the other option you have is to create a separate cancellation token. To do so you will need to create a new CancellationTokenSource. This is a disposable object. So you will have to carefully dispose it after its usage is ended. And it is not light weight compared to CancellationToken. Therefore if you are not logically starting a new operation that can be cancelled, I do not recommend to use a new CancellationTokenSource.

    For more information have a look at documentation

    Important parts extracted from documentation,

    1)

    The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Individual operations can in turn pass copies of the token to other operations. At some later time, the object that created the token can use it to request that the operations stop what they are doing.

    2)

    In the new cancellation framework, cancellation refers to operations, not objects. The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Therefore, cancellation tokens cannot be reused after they have been canceled.

    3)

    The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds