Skip to content

Instantly share code, notes, and snippets.

@StephenCleary
Created September 14, 2023 12:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StephenCleary/d1b122265f404f27e04e2d933a324618 to your computer and use it in GitHub Desktop.
Save StephenCleary/d1b122265f404f27e04e2d933a324618 to your computer and use it in GitHub Desktop.
Resettable CTSs using InterlockedState
using System.Threading;
/// <summary>
/// A <see cref="CancellationTokenSource"/> that resets itself to uncanceled after <see cref="Cancel"/> is called.
/// </summary>
public sealed class AutoResetCancellationTokenSource
{
/// <summary>
/// Cancels any tokens previously returned from <see cref="Token"/>, and resets this instance to an uncancelled state.
/// </summary>
public void Cancel()
{
var (oldState, _) = InterlockedState.Transform(ref _cts, state => null);
oldState?.Cancel();
}
/// <summary>
/// Returns the current <see cref="CancellationToken"/>.
/// </summary>
public CancellationToken Token
{
get
{
var (_, newState) = InterlockedState.Transform(ref _cts, state => state switch
{
null => new CancellationTokenSource(),
not null => state,
});
return newState!.Token;
}
}
/// <summary>
/// Either <c>null</c> or a <see cref="CancellationTokenSource"/> instance that is not canceled.
/// </summary>
private CancellationTokenSource? _cts;
}
using System.Threading;
/// <summary>
/// A <see cref="CancellationTokenSource"/> that stays canceled until <see cref="Reset"/> is called.
/// </summary>
public sealed class ManualResetCancellationTokenSource
{
/// <inheritdoc cref="CancellationTokenSource.IsCancellationRequested"/>
public bool IsCancellationRequested
{
get
{
var state = InterlockedState.Read(ref _cts);
if (state == null)
return false;
return state.IsCancellationRequested;
}
}
/// <summary>
/// Returns the current <see cref="CancellationToken"/>.
/// </summary>
public CancellationToken Token
{
get
{
var (_, newState) = InterlockedState.Transform(ref _cts, state => state switch
{
null => new CancellationTokenSource(),
not null => state,
});
return newState!.Token;
}
}
/// <summary>
/// Cancels any tokens previously returned from <see cref="Token"/>.
/// </summary>
public void Cancel()
{
var (_, newState) = InterlockedState.Transform(ref _cts, state => state switch
{
null => new CancellationTokenSource(),
not null => state,
});
newState!.Cancel();
}
/// <summary>
/// Resets this instance to an uncancelled state.
/// </summary>
public void Reset()
{
InterlockedState.Transform(ref _cts, state => state switch
{
{ IsCancellationRequested: true } => null,
{ IsCancellationRequested: false } => state,
null => null,
});
}
/// <summary>
/// If <c>null</c>, this instance is not canceled. If not <c>null</c>, the <see cref="CancellationTokenSource"/> may or may not be canceled.
/// </summary>
private CancellationTokenSource? _cts;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment