Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is code that disposes CancellationTokenSource while tasks are canceling correct?

I see this code in front of me and I am suspicious:

CancellationTokenSource _cts;

public void Dispose();
{
    _cts.Cancel();
    _cts.Dispose();
    _task.Wait(); //wait for the task to be canceled!?
}

Is it safe to call _cts.Dispose() straight after cancel? Wouldn't that dispose of underlying resources of the CancellationTokenSource that are needed for the task being cancelled to successfully Wait on the CancellationToken, if it wanted to do so?

like image 564
Tim Lovell-Smith Avatar asked Apr 21 '15 18:04

Tim Lovell-Smith


People also ask

Does disposing a CancellationTokenSource cancel it?

The Dispose method leaves the CancellationTokenSource in an unusable state. After calling Dispose , you must release all references to the CancellationTokenSource so the garbage collector can reclaim the memory that the CancellationTokenSource was occupying.

How do you handle a cancellation token?

The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. The operation can then just exit, or throw an OperationCanceledException, as appropriate.

What is task Cancelled exception?

TaskCanceledException(String, Exception, CancellationToken) Initializes a new instance of the TaskCanceledException class with a specified error message, a reference to the inner exception that is the cause of this exception, and the CancellationToken that triggered the cancellation.

Which object do you inspect to determine if a long running task will be Cancelled?

The background long-running tasks are cancelled by using the CancellationToken object in Blazor.


1 Answers

Is it safe to call _cts.Dispose() straight after cancel?

In order to know that, we need to understand what happens when we cancel a CancellationTokenSource.

When you cancel a CancellationTokenSource, it goes on to invoke any callbacks registered via the CancellationToken, which holds a reference to it's parent source via the CancellationToken.Register() method.

When you dispose a CTS, any linking callback that was registered is attempted to be unregistered from the token. If it's currently executing, it will wait until it's delegate is complete.

Which means, that although you've disposed your CTS, it's object is still referenced by the token. Hence, it still isn't eligible for collection.

Now let's look at CancellationToken.IsCancellationRequested:

public bool IsCancellationRequested 
{
    get
    {
        return m_source != null && m_source.IsCancellationRequested;
    }
}

This means that while disposed, checking the cancellation will yield true. This means, that it is safe for you to wait on the tasks completion after calling dispose.

As a side note, if you (for some reason) try to pass a token via it's disposed CancellationTokenSource, you will hit an ObjectDisposedException.

Edit:

Two things I want to add. First, let me say that I don't recommend using this approach. It should work for some code execution paths, but not for all. CancellationTokenSource should normally be disposed only if you use it's WaitHandle property. Otherwise, it is fine to leave it up to the GC to do the cleaning. But, as this is a matter of flavor, you may choose whichever you like. I would certainly advise to dispose only after you're certain the task has observed the cancellation request.

As per usage of WaitHandle, it will be disposed and nulled out once you dispose, so it will not be reachable.

like image 89
Yuval Itzchakov Avatar answered Oct 24 '22 04:10

Yuval Itzchakov