Skip to content

CancellationTokenSource is hard to use correctly #29970

Description

@NinoFloris

CancellationTokenSource is hard to use correctly, especially in concurrent scenarios.

Most CancellationTokenSource methods are booby-trapped with ThrowIfDisposed, especially tricky with .Token and .Cancel(). As for both cases there's something to say for silently returning a None token or not Cancelling, but also something to say for being explicit about it and throwing if it happens.

The problem with that last option though is that there's no way to actually check if the CancellationTokenSource is already disposed, now checking could obviously race but there are no Try## variants of these methods either. This leads to over cautious behavior, wrapping each call in a try block.

Concurrent success and cancellation/failure cases are also fun as Cancel throws if the CTS was already disposed. But for timer based CTS's you really do want to Dispose once some piece of code sucessfully completed. This is prone to races between timeouts that try to cancel and success that tries to dispose. This is 'fixable' by locking on the CTS but it all feels brittle.

The current Cancel api is in my opinion the most egrigious, TryCancel would be a valuable api to add.

Lastly there's the matter of having to Dispose a CTS — but really only in some cases. When the CTS is timer based, to keep the timer queue clean. Failing to do so isn't easy to diagnose and I believe it's a pervasive problem in any corporate codebase not familiar with all the details. Having some diagnostic help like UnobservedTaskException but for unobserved timer based CTS's would be amazing.

Right now CancellationTokenSource feels far from the pit of success while it really doesn't have to.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions