Search code examples
c#xamarin.iosdisposerace-conditioncancellationtokensource

CancellationTokenSource.Cancel throws an ObjectDisposedException


I have a class that owns a CancellationTokenSource.

public class GrabboxCell : UICollectionViewCell
{
    CancellationTokenSource _tokenSource = new CancellationTokenSource ();

    // ...
}

I'm using current token to start some long-running operations.

My object also needs to support “recycling”. Think reincarnation. All long-running operations started during previous life must be cancelled.

In this case I call Cancel and Dispose on the source, and issue a new token source:

void CancelToken (bool createNew)
{
    _tokenSource.Cancel ();
    _tokenSource.Dispose ();
    _tokenSource = null;

    if (createNew) {
        _tokenSource = new CancellationTokenSource ();
    }
}

I call this method in two places: when I want the token to expire and when this class is disposed.

public override void PrepareForReuse ()
{
    CancelToken (true);
    base.PrepareForReuse ();
}

protected override void Dispose (bool disposing)
{
    CancelToken (false);
    base.Dispose (disposing);
}

Sometimes I'm getting an ObjectDisposedException when calling _tokenSource.Cancel () from my Dispose method. Documentation says:

All public and protected members of CancellationTokenRegistration are thread-safe and may be used concurrently from multiple threads, with the exception of Dispose, which must only be used when all other operations on the CancellationTokenRegistration have completed.

I'm not sure what to do at this moment. Wrap CancelToken in a lock?
Where exactly does the race condition happen and how to mitigate it?

I know for sure that PrepareForReuse is always called on the same thread, but Dispose may be called on a different one.

If this is of any use, I'm running Mono and not .NET Framework but I'm pretty sure they should have the same semantics regarding cancellation tokens.


Solution

  • This isn't really interesting but I wrapped Cancel and Dispose into a try-catch that swallows ObjectDisposedException and haven't had problems since.