'CreateLinkedTokenSource throws ObjectDisposedException. How to correctly and safely dispose a CancellationTokenSource?

Multiple similar questions have been asked here before.

MSDN states as an important note that one should always dispose the CancellationTokenSource when done with it.

OK, but it becomes a little complicated with multithreaded applications and async-await model.

I'm developing a library. The problem I ran into is thatI'm using in several places CreateLinkedTokenSource out of a CancellationToken received from the user. Shortly, I'm doing it so that I'm able to cancel myself an operation if it takes longer than some time.

Example

public async Task<Result> DoAsync(CancellationToken cancellationToken)
{ 
    using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) 
    {
        // here pass linkedTokenSource.Token further down the line 
        var resultTask = sender.DoAsync(linkedTokenSource.Token); 
        var timeoutTask = Task.Delay(timeout); 
        var completed = await Task.WhenAny(resultTask, timeoutTask); 
        if (completed  == timeoutTask) 
        {
            linkedTokenSource.Cancel(); 
            throw TimeoutException(); 
        }

        return await resultTask;
        // from the point of view of this piece of code
        // we're done with the cancellationTokenSource right? 
        // so I need to dispose the source (done here via `using`)
    }
}

However, down the line in different code sections, due to race conditions, it happens that some threads are trying to CreateLinkedTokenSource out of linkedTokenSource.Token resulting in an ObjectDisposedException since the linkedTokenSource has already been disposed after the TimeoutException was thrown.
This will end up in a UnobservedTaskException which will confuse the user if he listens on unobserved exceptions.

Putting a try-catch on every CreateLinkedTokenSource and silencing the ObjectDisposedException line seems again strange for me.

My questions are:

  1. Why the CreateLinkedTokenSource throws this exception? Is there an explanation for this? Since the CencellationToken is a struct, why I shouldn't be able to create a new source out of it? (even in the cancellationToken is cancelled).

  2. Any suggestions on how should handle disposing in this scenario?



Solution 1:[1]

As I can see, you've removed the rest of code with task creation. Whenether, it's very likely that your code is something like this:

public async Task<Result> DoAsync(CancellationToken cancellationToken)
{ 
    using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) 
    {
        await Task.Run(() =>
        {
            // here pass linkedTokenSource.Token further down the line 
            // check if further processing reached some timeout and if it did:
            if (timeout) 
            {
                linkedTokenSource.Cancel(); 
                throw TimeoutException(); 
            }
            // from the point of view of this piece of code
            // we're done with the cancellationTokenSource right? 
            // so I need to dispose the source (done here via `using`)
        }
    }
}

So you catch a closure variable, which being used into a using construction. So it works like this:

  • You enter the using block
  • You start your task
  • You immediately return from the method
  • You execute the finally block for your method, which calls the .Dispose() method for your linkedTokenSource variable
  • You got a timeout
  • you access a disposed closure

You have to rewrite your code for manually disposing the linkedTokenSource after you are done with whole task, not when you're done with starting it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 VMAtm