// 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); } }