Skip to content

Instantly share code, notes, and snippets.

@StephenCleary
Last active September 6, 2022 02:29
Show Gist options
  • Save StephenCleary/6c258078afe4242e0b3aee28f5c634b0 to your computer and use it in GitHub Desktop.
Save StephenCleary/6c258078afe4242e0b3aee28f5c634b0 to your computer and use it in GitHub Desktop.
Cache for reusing CTS instances.
using System.Collections.Concurrent;
using System.Diagnostics;
using Nito.Disposables;
/// <summary>
/// A cache of uncanceled <see cref="CancellationTokenSource"/> instances.
/// </summary>
public sealed class CancellationTokenSourceCache
{
private readonly ConcurrentBag<CancellationTokenSource> _sources = new();
public static CancellationTokenSourceCache Instance { get; } = new();
/// <summary>
/// Retrieves a CTS from the cache, or creates a new one if the cache is empty.
/// </summary>
public CachedCancellationTokenSource Allocate() => new(this, _sources.TryTake(out var result) ? result : new CancellationTokenSource());
/// <summary>
/// A CTS wrapper that, when disposed, returns the CTS to the cache if it is not canceled.
/// </summary>
public sealed class CachedCancellationTokenSource : SingleNonblockingDisposable<CancellationTokenSourceCache?>
{
private readonly CancellationTokenSource _source;
public CachedCancellationTokenSource(CancellationTokenSourceCache cache, CancellationTokenSource source)
: base(cache)
{
_source = source;
}
/// <summary>
/// Cancels the CTS. When disposed, this instance will dispose the CTS instead of returning it to the cache.
/// </summary>
public void Cancel()
{
if (TryUpdateContext(_ => null))
_source.Cancel();
}
protected override void Dispose(CancellationTokenSourceCache? context)
{
if (context == null)
{
_source.Dispose();
return;
}
Debug.Assert(!_source.IsCancellationRequested);
context._sources.Add(_source);
}
}
}
@StephenCleary
Copy link
Author

StephenCleary commented Sep 6, 2022

Note: caching CTSs (and reusing CTSs in general) assumes that any CT registrations are disposed when their operation completes without being canceled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment