3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>[....]</OWNER>
8 ////////////////////////////////////////////////////////////////////////////////
10 #pragma warning disable 0420 // turn off 'a reference to a volatile field will not be treated as volatile' during CAS.
13 using System.Diagnostics;
14 using System.Runtime.InteropServices;
15 using System.Security.Permissions;
16 using System.Diagnostics.Contracts;
18 using System.Runtime.CompilerServices;
19 using System.Security;
21 namespace System.Threading
24 /// Propagates notification that operations should be canceled.
28 /// A <see cref="CancellationToken"/> may be created directly in an unchangeable canceled or non-canceled state
29 /// using the CancellationToken's constructors. However, to have a CancellationToken that can change
30 /// from a non-canceled to a canceled state,
31 /// <see cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see> must be used.
32 /// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its
33 /// <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property.
36 /// Once canceled, a token may not transition to a non-canceled state, and a token whose
37 /// <see cref="CanBeCanceled"/> is false will never change to one that can be canceled.
40 /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
44 [HostProtection(Synchronization = true, ExternalThreading = true)]
45 [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
46 public struct CancellationToken
48 // The backing TokenSource.
49 // if null, it implicitly represents the same thing as new CancellationToken(false).
50 // When required, it will be instantiated to reflect this.
51 private CancellationTokenSource m_source;
52 //!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid
57 /// Returns an empty CancellationToken value.
60 /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
62 public static CancellationToken None
65 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
67 get { return default(CancellationToken); }
71 /// Gets whether cancellation has been requested for this token.
73 /// <value>Whether cancellation has been requested for this token.</value>
76 /// This property indicates whether cancellation has been requested for this token,
77 /// either through the token initially being construted in a canceled state, or through
78 /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
79 /// on the token's associated <see cref="CancellationTokenSource"/>.
82 /// If this property is true, it only guarantees that cancellation has been requested.
83 /// It does not guarantee that every registered handler
84 /// has finished executing, nor that cancellation requests have finished propagating
85 /// to all registered handlers. Additional synchronization may be required,
86 /// particularly in situations where related objects are being canceled concurrently.
89 public bool IsCancellationRequested
92 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
96 return m_source != null && m_source.IsCancellationRequested;
101 /// Gets whether this token is capable of being in the canceled state.
104 /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
105 /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
108 public bool CanBeCanceled
111 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
115 return m_source != null && m_source.CanBeCanceled;
120 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
122 /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
123 /// to be instantiated. It is preferable to only use this property when necessary, and to then
124 /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
125 /// the source will dispose of this allocated handle). The handle should not be closed or disposed directly.
127 /// <exception cref="T:System.ObjectDisposedException">The associated <see
128 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
129 public WaitHandle WaitHandle
133 if (m_source == null)
135 InitializeDefaultSource();
138 return m_source.WaitHandle;
142 // public CancellationToken()
143 // this constructor is implicit for structs
144 // -> this should behaves exactly as for new CancellationToken(false)
147 /// Internal constructor only a CancellationTokenSource should create a CancellationToken
150 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
152 internal CancellationToken(CancellationTokenSource source)
158 /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
160 /// <param name="canceled">
161 /// The canceled state for the token.
164 /// Tokens created with this constructor will remain in the canceled state specified
165 /// by the <paramref name="canceled"/> parameter. If <paramref name="canceled"/> is false,
166 /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
167 /// If <paramref name="canceled"/> is true,
168 /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true.
170 public CancellationToken(bool canceled) :
174 m_source = CancellationTokenSource.InternalGetStaticSource(canceled);
180 private readonly static Action<Object> s_ActionToActionObjShunt = new Action<Object>(ActionToActionObjShunt);
181 private static void ActionToActionObjShunt(object obj)
183 Action action = obj as Action;
184 Contract.Assert(action != null, "Expected an Action here");
189 /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
193 /// If this token is already in the canceled state, the
194 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
195 /// propagated out of this method call.
198 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
199 /// along with the delegate and will be used when executing it.
202 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
203 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
204 /// be used to deregister the callback.</returns>
205 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
206 /// <exception cref="T:System.ObjectDisposedException">The associated <see
207 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
208 public CancellationTokenRegistration Register(Action callback)
210 if (callback == null)
211 throw new ArgumentNullException("callback");
214 s_ActionToActionObjShunt,
216 false, // useSync=false
217 true // useExecutionContext=true
222 /// Registers a delegate that will be called when this
223 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
227 /// If this token is already in the canceled state, the
228 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
229 /// propagated out of this method call.
232 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
233 /// along with the delegate and will be used when executing it.
236 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
237 /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
238 /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
239 /// when invoking the <paramref name="callback"/>.</param>
240 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
241 /// be used to deregister the callback.</returns>
242 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
243 /// <exception cref="T:System.ObjectDisposedException">The associated <see
244 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
245 public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext)
247 if (callback == null)
248 throw new ArgumentNullException("callback");
251 s_ActionToActionObjShunt,
253 useSynchronizationContext,
254 true // useExecutionContext=true
259 /// Registers a delegate that will be called when this
260 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
264 /// If this token is already in the canceled state, the
265 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
266 /// propagated out of this method call.
269 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
270 /// along with the delegate and will be used when executing it.
273 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
274 /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
275 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
276 /// be used to deregister the callback.</returns>
277 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
278 /// <exception cref="T:System.ObjectDisposedException">The associated <see
279 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
280 public CancellationTokenRegistration Register(Action<Object> callback, Object state)
282 if (callback == null)
283 throw new ArgumentNullException("callback");
288 false, // useSync=false
289 true // useExecutionContext=true
294 /// Registers a delegate that will be called when this
295 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
299 /// If this token is already in the canceled state, the
300 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
301 /// propagated out of this method call.
304 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists,
305 /// will be captured along with the delegate and will be used when executing it.
308 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
309 /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
310 /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
311 /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
312 /// when invoking the <paramref name="callback"/>.</param>
313 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
314 /// be used to deregister the callback.</returns>
315 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
316 /// <exception cref="T:System.ObjectDisposedException">The associated <see
317 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
318 public CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext)
323 useSynchronizationContext,
324 true // useExecutionContext=true
328 // helper for internal registration needs that don't require an EC capture (e.g. creating linked token sources, or registering unstarted TPL tasks)
329 // has a handy signature, and skips capturing execution context.
330 internal CancellationTokenRegistration InternalRegisterWithoutEC(Action<object> callback, Object state)
335 false, // useSyncContext=false
336 false // useExecutionContext=false
341 [SecuritySafeCritical]
342 [MethodImpl(MethodImplOptions.NoInlining)]
343 private CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext, bool useExecutionContext)
345 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
347 if (callback == null)
348 throw new ArgumentNullException("callback");
350 if (CanBeCanceled == false)
352 return new CancellationTokenRegistration(); // nothing to do for tokens than can never reach the canceled state. Give them a dummy registration.
355 // Capture [....]/execution contexts if required.
356 // Note: Only capture [....]/execution contexts if IsCancellationRequested = false
357 // as we know that if it is true that the callback will just be called synchronously.
359 SynchronizationContext capturedSyncContext = null;
360 ExecutionContext capturedExecutionContext = null;
361 if (!IsCancellationRequested)
363 if (useSynchronizationContext)
364 capturedSyncContext = SynchronizationContext.Current;
365 if (useExecutionContext)
366 capturedExecutionContext = ExecutionContext.Capture(
367 ref stackMark, ExecutionContext.CaptureOptions.OptimizeDefaultCase); // ideally we'd also use IgnoreSyncCtx, but that could break compat
370 // Register the callback with the source.
371 return m_source.InternalRegister(callback, state, capturedSyncContext, capturedExecutionContext);
375 /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
378 /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
379 /// instance.</param>
380 /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
381 /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
382 /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
383 public bool Equals(CancellationToken other)
385 //if both sources are null, then both tokens represent the Empty token.
386 if (m_source == null && other.m_source == null)
391 // one is null but other has inflated the default source
392 // these are only equal if the inflated one is the staticSource(false)
393 if (m_source == null)
395 return other.m_source == CancellationTokenSource.InternalGetStaticSource(false);
398 if (other.m_source == null)
400 return m_source == CancellationTokenSource.InternalGetStaticSource(false);
403 // general case, we check if the sources are identical
405 return m_source == other.m_source;
409 /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
410 /// specified <see cref="T:System.Object"/>.
412 /// <param name="other">The other object to which to compare this instance.</param>
413 /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
414 /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
415 /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
416 /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
417 /// <exception cref="T:System.ObjectDisposedException">An associated <see
418 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
419 public override bool Equals(Object other)
421 if (other is CancellationToken)
423 return Equals((CancellationToken) other);
430 /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
432 /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
433 public override Int32 GetHashCode()
435 if (m_source == null)
437 // link to the common source so that we have a source to interrogate.
438 return CancellationTokenSource.InternalGetStaticSource(false).GetHashCode();
441 return m_source.GetHashCode();
445 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
447 /// <param name="left">The first instance.</param>
448 /// <param name="right">The second instance.</param>
449 /// <returns>True if the instances are equal; otherwise, false.</returns>
450 /// <exception cref="T:System.ObjectDisposedException">An associated <see
451 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
452 public static bool operator ==(CancellationToken left, CancellationToken right)
454 return left.Equals(right);
458 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
460 /// <param name="left">The first instance.</param>
461 /// <param name="right">The second instance.</param>
462 /// <returns>True if the instances are not equal; otherwise, false.</returns>
463 /// <exception cref="T:System.ObjectDisposedException">An associated <see
464 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
465 public static bool operator !=(CancellationToken left, CancellationToken right)
467 return !left.Equals(right);
471 /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
472 /// this token has had cancellation requested.
475 /// This method provides functionality equivalent to:
477 /// if (token.IsCancellationRequested)
478 /// throw new OperationCanceledException(token);
481 /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
482 /// <exception cref="T:System.ObjectDisposedException">The associated <see
483 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
485 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
487 public void ThrowIfCancellationRequested()
489 if (IsCancellationRequested)
490 ThrowOperationCanceledException();
493 // Throw an ODE if this CancellationToken's source is disposed.
494 internal void ThrowIfSourceDisposed()
496 if ((m_source != null) && m_source.IsDisposed)
497 ThrowObjectDisposedException();
500 // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
501 private void ThrowOperationCanceledException()
503 throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this);
506 private static void ThrowObjectDisposedException()
508 throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationToken_SourceDisposed"));
511 // -----------------------------------
514 private void InitializeDefaultSource()
516 // Lazy is slower, and although multiple threads may ---- and set m_source repeatedly, the ---- is benign.
517 // Alternative: LazyInititalizer.EnsureInitialized(ref m_source, ()=>CancellationTokenSource.InternalGetStaticSource(false));
519 m_source = CancellationTokenSource.InternalGetStaticSource(false);