// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Threading
{
///
/// Propagates notification that operations should be canceled.
///
///
///
/// A may be created directly in an unchangeable canceled or non-canceled state
/// using the CancellationToken's constructors. However, to have a CancellationToken that can change
/// from a non-canceled to a canceled state,
/// CancellationTokenSource must be used.
/// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its
/// Token property.
///
///
/// Once canceled, a token may not transition to a non-canceled state, and a token whose
/// is false will never change to one that can be canceled.
///
///
/// All members of this struct are thread-safe and may be used concurrently from multiple threads.
///
///
[DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
public readonly struct CancellationToken
{
// The backing TokenSource.
// if null, it implicitly represents the same thing as new CancellationToken(false).
// When required, it will be instantiated to reflect this.
private readonly CancellationTokenSource _source;
//!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid
private readonly static Action s_actionToActionObjShunt = obj => ((Action)obj)();
///
/// Returns an empty CancellationToken value.
///
///
/// The value returned by this property will be non-cancelable by default.
///
public static CancellationToken None => default;
///
/// Gets whether cancellation has been requested for this token.
///
/// Whether cancellation has been requested for this token.
///
///
/// This property indicates whether cancellation has been requested for this token,
/// either through the token initially being constructed in a canceled state, or through
/// calling Cancel
/// on the token's associated .
///
///
/// If this property is true, it only guarantees that cancellation has been requested.
/// It does not guarantee that every registered handler
/// has finished executing, nor that cancellation requests have finished propagating
/// to all registered handlers. Additional synchronization may be required,
/// particularly in situations where related objects are being canceled concurrently.
///
///
public bool IsCancellationRequested => _source != null && _source.IsCancellationRequested;
///
/// Gets whether this token is capable of being in the canceled state.
///
///
/// If CanBeCanceled returns false, it is guaranteed that the token will never transition
/// into a canceled state, meaning that will never
/// return true.
///
public bool CanBeCanceled => _source != null;
///
/// Gets a that is signaled when the token is canceled.
///
/// Accessing this property causes a WaitHandle
/// to be instantiated. It is preferable to only use this property when necessary, and to then
/// dispose the associated instance at the earliest opportunity (disposing
/// the source will dispose of this allocated handle). The handle should not be closed or disposed directly.
///
/// The associated CancellationTokenSource has been disposed.
public WaitHandle WaitHandle => (_source ?? CancellationTokenSource.s_neverCanceledSource).WaitHandle;
// public CancellationToken()
// this constructor is implicit for structs
// -> this should behaves exactly as for new CancellationToken(false)
///
/// Internal constructor only a CancellationTokenSource should create a CancellationToken
///
internal CancellationToken(CancellationTokenSource source) => _source = source;
///
/// Initializes the CancellationToken .
///
///
/// The canceled state for the token.
///
///
/// Tokens created with this constructor will remain in the canceled state specified
/// by the parameter. If is false,
/// both and will be false.
/// If is true,
/// both and will be true.
///
public CancellationToken(bool canceled) : this(canceled ? CancellationTokenSource.s_canceledSource : null)
{
}
///
/// Registers a delegate that will be called when this CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the
/// delegate will be run immediately and synchronously. Any exception the delegate generates will be
/// propagated out of this method call.
///
///
/// The current ExecutionContext , if one exists, will be captured
/// along with the delegate and will be used when executing it.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// The instance that can
/// be used to unregister the callback.
/// is null.
public CancellationTokenRegistration Register(Action callback) =>
Register(
s_actionToActionObjShunt,
callback ?? throw new ArgumentNullException(nameof(callback)),
useSynchronizationContext: false,
useExecutionContext: true);
///
/// Registers a delegate that will be called when this
/// CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the
/// delegate will be run immediately and synchronously. Any exception the delegate generates will be
/// propagated out of this method call.
///
///
/// The current ExecutionContext , if one exists, will be captured
/// along with the delegate and will be used when executing it.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// A Boolean value that indicates whether to capture
/// the current SynchronizationContext and use it
/// when invoking the .
/// The instance that can
/// be used to unregister the callback.
/// is null.
public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext) =>
Register(
s_actionToActionObjShunt,
callback ?? throw new ArgumentNullException(nameof(callback)),
useSynchronizationContext,
useExecutionContext: true);
///
/// Registers a delegate that will be called when this
/// CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the
/// delegate will be run immediately and synchronously. Any exception the delegate generates will be
/// propagated out of this method call.
///
///
/// The current ExecutionContext , if one exists, will be captured
/// along with the delegate and will be used when executing it.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// The state to pass to the when the delegate is invoked. This may be null.
/// The instance that can
/// be used to unregister the callback.
/// is null.
public CancellationTokenRegistration Register(Action callback, object state) =>
Register(callback, state, useSynchronizationContext: false, useExecutionContext: true);
///
/// Registers a delegate that will be called when this
/// CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the
/// delegate will be run immediately and synchronously. Any exception the delegate generates will be
/// propagated out of this method call.
///
///
/// The current ExecutionContext , if one exists,
/// will be captured along with the delegate and will be used when executing it.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// The state to pass to the when the delegate is invoked. This may be null.
/// A Boolean value that indicates whether to capture
/// the current SynchronizationContext and use it
/// when invoking the .
/// The instance that can
/// be used to unregister the callback.
/// is null.
/// The associated CancellationTokenSource has been disposed.
public CancellationTokenRegistration Register(Action callback, object state, bool useSynchronizationContext) =>
Register(callback, state, useSynchronizationContext, useExecutionContext: true);
///
/// Registers a delegate that will be called when this
/// CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the delegate will be run immediately and synchronously.
/// Any exception the delegate generates will be propagated out of this method call.
///
///
/// ExecutionContext is not captured nor flowed
/// to the callback's invocation.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// The state to pass to the when the delegate is invoked. This may be null.
/// The instance that can
/// be used to unregister the callback.
/// is null.
public CancellationTokenRegistration UnsafeRegister(Action callback, object state) =>
Register(callback, state, useSynchronizationContext: false, useExecutionContext: false);
///
/// Registers a delegate that will be called when this
/// CancellationToken is canceled.
///
///
///
/// If this token is already in the canceled state, the
/// delegate will be run immediately and synchronously. Any exception the delegate generates will be
/// propagated out of this method call.
///
///
/// The delegate to be executed when the CancellationToken is canceled.
/// The state to pass to the when the delegate is invoked. This may be null.
/// A Boolean value that indicates whether to capture
/// the current SynchronizationContext and use it
/// when invoking the .
/// The instance that can
/// be used to unregister the callback.
/// is null.
/// The associated CancellationTokenSource has been disposed.
private CancellationTokenRegistration Register(Action callback, object state, bool useSynchronizationContext, bool useExecutionContext)
{
if (callback == null)
throw new ArgumentNullException(nameof(callback));
CancellationTokenSource source = _source;
return source != null ?
source.InternalRegister(callback, state, useSynchronizationContext ? SynchronizationContext.Current : null, useExecutionContext ? ExecutionContext.Capture() : null) :
default; // Nothing to do for tokens than can never reach the canceled state. Give back a dummy registration.
}
///
/// Determines whether the current CancellationToken instance is equal to the
/// specified token.
///
/// The other CancellationToken to which to compare this
/// instance.
/// True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
/// with the same CancellationTokenSource or if they were both constructed
/// from public CancellationToken constructors and their values are equal.
public bool Equals(CancellationToken other) => _source == other._source;
///
/// Determines whether the current CancellationToken instance is equal to the
/// specified .
///
/// The other object to which to compare this instance.
/// True if is a CancellationToken
/// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
/// with the same CancellationTokenSource or if they were both constructed
/// from public CancellationToken constructors and their values are equal.
/// An associated CancellationTokenSource has been disposed.
public override bool Equals(object other) => other is CancellationToken && Equals((CancellationToken)other);
///
/// Serves as a hash function for a CancellationToken .
///
/// A hash code for the current CancellationToken instance.
public override int GetHashCode() => (_source ?? CancellationTokenSource.s_neverCanceledSource).GetHashCode();
///
/// Determines whether two CancellationToken instances are equal.
///
/// The first instance.
/// The second instance.
/// True if the instances are equal; otherwise, false.
/// An associated CancellationTokenSource has been disposed.
public static bool operator ==(CancellationToken left, CancellationToken right) => left.Equals(right);
///
/// Determines whether two CancellationToken instances are not equal.
///
/// The first instance.
/// The second instance.
/// True if the instances are not equal; otherwise, false.
/// An associated CancellationTokenSource has been disposed.
public static bool operator !=(CancellationToken left, CancellationToken right) => !left.Equals(right);
///
/// Throws a OperationCanceledException if
/// this token has had cancellation requested.
///
///
/// This method provides functionality equivalent to:
///
/// if (token.IsCancellationRequested)
/// throw new OperationCanceledException(token);
///
///
/// The token has had cancellation requested.
/// The associated CancellationTokenSource has been disposed.
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested)
ThrowOperationCanceledException();
}
// Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
private void ThrowOperationCanceledException() =>
throw new OperationCanceledException(SR.OperationCanceled, this);
}
}