0

Why do we have 2 different results in next 2 scenarios? In these samples token.IsCancellationRequested = true. Why does functionality have changed if i comment if(token.IsCancellationRequested), when in fact we can type if(true) instead of it.

Somewhere i read that in order to have TaskCanceledException ( Status = Canceled ) we need to follow next 3 conditions:

  1. Throw OperationCanceledException and pass token to it's constructor
  2. Pass the same token to the method, which creates a task (Task.Run)
  3. IsCancellationRequested should be true.

And it's true, if we use if(token.IsCancellationRequested) throw new OperationCanceledException(token) (equals to token.ThrowIfCancellationRequested) (second scenario) , we need to follow 2 other contidionts, but in first scenario if we comment if(token.IsCancellationRequested) (we can even remove 1 from CTS constructor ) we will still get Canceled status.

1. Here Status is Canceled

CancellationTokenSource cts = new CancellationTokenSource(1);
        var token = cts.Token;
        var task = Task.Run(() =>
        {
                Thread.Sleep(5);
                // if(token.IsCancellationRequested)
                throw new OperationCanceledException(token);
        });

        Thread.Sleep(50);
        Console.WriteLine(task.Status);

2. Here - Faulted.

CancellationTokenSource cts = new CancellationTokenSource(1);
        var token = cts.Token;
        var task = Task.Run(() =>
        {
            Thread.Sleep(5); 
            if(token.IsCancellationRequested) // true
                throw new OperationCanceledException(token);
        });

        Thread.Sleep(50);
        Console.WriteLine(task.Status);
  • 4
    BTW - Rather than test/throw yourself you should just be calling `ThrowIfCancellationRequested` – Damien_The_Unbeliever Mar 09 '21 at 13:11
  • if(token.IsCancellationRequested) throw new OperationCanceledException(token) is equal to ThrowIfCancellationRequested, but somewhere i read that in order to have TaskCanceledException ( Status = Canceled ) we need to: 1. Throw OperationCanceledException and pass token to it's constructor 2. Pass the same token to the method, which creates a task (Task.Run) 3. IsCancellationRequested should be true – Игорь Гарбуз Mar 09 '21 at 13:23
  • I think `TaskCanceledException` is just the result of catching a `OperationCanceledException`. But the canonical way of doing this is to use `ThrowIfCancellationRequested()` which depends on `IsCancellationRequested` – Charlieface Mar 09 '21 at 13:41
  • Your two examples call different overloads of `Task.Run()`, which in turn have different behaviors with respect to cancellation. See duplicate. – Peter Duniho Mar 09 '21 at 17:54

1 Answers1

1

It seems that if the compiler can know at compile time that the task will always throw OperationCanceledException is will end in Canceled state, otherwize it will end in Faulted.

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(1);
    cts.Cancel();
    var token = cts.Token;
    var task = Task.Run(() =>
    {
        //if(GetTrue())  //Faulted
        if(true)         //Canceled
            throw new OperationCanceledException();
    });
    
    Thread.Sleep(50);
    Console.WriteLine(task.Status);
}

bool GetTrue()
{
    return true;
}

But if you send in the same token to the Task.Run as you throw inside the task it will end in Canceled.

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(10000);
    var token = cts.Token;
    var task = Task.Run(() =>
    {
        cts.Cancel();
        token.ThrowIfCancellationRequested();
    }, token);
    
    Thread.Sleep(50);
    Console.WriteLine(task.Status);
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • 1
    The underlying reason that the compiler "seems" to "know" is explained here: https://stackoverflow.com/questions/24359761/faulted-vs-canceled-task-status-after-cancellationtoken-throwifcancellationreque – glenebob Mar 09 '21 at 16:53