0

An OperationCanceledException could be thrown in a couple of different reasons:

  • The cancelation token has been set, or
  • There has been a timeout (TaskCancelationException)

To get around this, a common workaround (described here, et al: Distinguish timeout from user cancellation) is as follows:

catch (OperationCanceledException ex)
{
    if (token.IsCancellationRequested)
    {
        return -1;
    }

    return -2;
}

But it would make more sense for me to look at the actual cancelation state that was given to the exception. Something like this:

if (ex is OperationCanceledException ocEx)
{
    if (ocEx.CancellationToken.IsCancellationRequested)
    {
        return -1;
    }

    return -2;
}

Am I missing something, is there some reason this is not promoted as a solution for this common problem?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Hoppy
  • 720
  • 2
  • 12
  • 24
  • 1
    I've always used `ex.CancellationToken.IsCancellationRequested`. That's the standard way, AFAIK. In many (if not most) cases, you don't even have access to the original `token` variable inside the `catch` block. – 41686d6564 stands w. Palestine Oct 29 '19 at 05:36
  • The cancellation might be caused by a nested token - so it really depends on the correct semantics. *Checking the `IsCancellationRequested` of token passed to the OCE is a bit silly, however, as the OCE says "this is the token that caused me, thus it's cancelled".* One might want to check the OCE token against the KNOWN token to ensure it was thrown by the known: `if (one.CancellationToken == myCancellationToken)` - not checking to see "if cancelled" here (as the OCE token *is* cancelled if it was triggered by a CT), rather if it's the same trigger token. Anyway, semantics.. – user2864740 Oct 29 '19 at 05:56
  • (Hint: if there is an internal CT causing the OCE that is not chained from the known token then the cancellation state is not directly tied to the known `token`; hence first semantics. Even if it is linked, the linked token may be cancelled independently without trigger cancellation of the linked sources eg.) – user2864740 Oct 29 '19 at 06:00
  • @user2864740 I didn't pay attention to the part about the timeout in the question. Your logic actually makes sense when dealing with nested tokens or if one wants to distinguish between a timeout and a cancellation request. That being said, I'm 100% sure that I've encountered situations where an OCE is thrown but no cancellation was involved whatsoever and that's when `ex.CancellationToken.IsCancellationRequested` becomes relevant. – 41686d6564 stands w. Palestine Oct 29 '19 at 06:24
  • 2
    @Hoppy If you're trying to distinguish between a timeout and a cancellation request, please note that my first comment doesn't apply to this situation because `ocEx.CancellationToken.IsCancellationRequested` will return true in both cases (did you try it?) while `token.IsCancellationRequested` will only return true in the case of a cancellation. – 41686d6564 stands w. Palestine Oct 29 '19 at 06:26

1 Answers1

0
if (ex is OperationCanceledException ocEx)
{
   if (ocEx.CancellationToken.IsCancellationRequested)
   {
       return -1;
   }

   return -2;
}

The ocEx.CancellationToken.IsCancellationRequested will be true 99% of the time, so if you do this, your method will return -1 99% of the time. The reason for checking the token.IsCancellationRequested is because you want to differentiate between:

  1. The OperationCanceledException was caused by the cancellation of the known token.
  2. The OperationCanceledException has some other cause.

You can't really be 100% sure that your token caused the exception, because there is a race condition: the exception might occur before the cancellation of the token, and then the token was canceled shortly before checking the token.IsCancellationRequested. But this race condition is subtle and not very important, and you can't do anything about it anyway.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104