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?
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.
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.
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.
The background long-running tasks are cancelled by using the CancellationToken object in Blazor.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With