Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to correlate an OperationCanceledException to a CancellationToken

I'm using an async api which supports cancellation and I'm passing to that api a CancellationToken instance. As usual, if the cancellation is requested on the passed token, the api that I'm invoking will throws an OperationCanceledException (this is the standard cooperative cancellation pattern of the .NET framework).

I want to be able to catch the OperationCanceledException if and only if it is the exception raised due to the cancellation of the provided cancellation token.

The following code illustrates what I'm trying to achieve:

try 
{
    await _service.DoSomethingAsync(cancellationToken: token);
}
catch (OperationCanceledException ex) when ( /* here I want a condition signifying that the OperationCanceledException is caused by the cancellation of the token object */)
{
    // avoid logging the exception: this is raised by design (cooperative cancellation)
    throw;
}
catch (Exception ex) 
{
    _logger.LogError(ex, "An error occurred: {0}", ex.Message);
    throw;
}

For the exception filter of the code above I have basically two ideas:

  • checking the IsCancellationRequested property on the token object: when (token.IsCancellationRequested)
  • checking the CancellationToken property on the ex object: when (ex.CancellationToken == token)

What is the right way to do the check I want to perform ? Are the two ways showed above equivalent ? Is there a best practice ?

IMPORTANT NOTE: I know that the code showed above can be written in a more efficient way, because catching exceptions is an expensive operation. The best thing to do is probably removing the first catch block at all, and only catching an Exception if and only if it is not related with the cancellation of the token object. I'm aware of that, but that's not the point of my question. I have written the code in the question that way only for clarity, because the whole point of my question is how to properly correlate an OperationCanceledException with the CancellationTokenwhich has caused the exception itself.

like image 961
Enrico Massone Avatar asked Apr 21 '21 21:04

Enrico Massone


People also ask

What is difference between CancellationTokenSource and CancellationToken?

CancellationTokenSource - This is the object responsible for creating a cancellation token and sending a cancellation request to all copies of that token. CancellationToken - This is the structure used by listeners to monitor the token's current state.

Should I call ThrowIfCancellationRequested?

Yes, you are supposed to call ThrowIfCancellationRequested() manually, in the appropriate places in your code (where appropriate is determined by you as a programmer). Consider the following example of a simple job processing function that reads jobs from a queue and does stuff with them.

Does CancellationTokenSource need to be disposed?

Always call Dispose before you release your last reference to the CancellationTokenSource. Otherwise, the resources it is using will not be freed until the garbage collector calls the CancellationTokenSource object's Finalize method.


1 Answers

I want to be able to catch the OperationCanceledException if and only if it is the exception raised due to the cancellation of the provided cancellation token.

You can't do this exactly, but you can "catch the canceled exception only if my provided token has been cancelled", which is usually good enough.

when (token.IsCancellationRequested) is what you want.

Don't check the ex.CancellationToken, because the method you're calling may be observing a linked cancellation token, which would be different than the one you provided.

like image 162
Stephen Cleary Avatar answered Nov 03 '22 04:11

Stephen Cleary