3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>Microsoft</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
64 get { return default(CancellationToken); }
68 /// Gets whether cancellation has been requested for this token.
70 /// <value>Whether cancellation has been requested for this token.</value>
73 /// This property indicates whether cancellation has been requested for this token,
74 /// either through the token initially being construted in a canceled state, or through
75 /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
76 /// on the token's associated <see cref="CancellationTokenSource"/>.
79 /// If this property is true, it only guarantees that cancellation has been requested.
80 /// It does not guarantee that every registered handler
81 /// has finished executing, nor that cancellation requests have finished propagating
82 /// to all registered handlers. Additional synchronization may be required,
83 /// particularly in situations where related objects are being canceled concurrently.
86 public bool IsCancellationRequested
90 return m_source
!= null && m_source
.IsCancellationRequested
;
95 /// Gets whether this token is capable of being in the canceled state.
98 /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
99 /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
102 public bool CanBeCanceled
106 return m_source
!= null && m_source
.CanBeCanceled
;
111 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
113 /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
114 /// to be instantiated. It is preferable to only use this property when necessary, and to then
115 /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
116 /// the source will dispose of this allocated handle). The handle should not be closed or disposed directly.
118 /// <exception cref="T:System.ObjectDisposedException">The associated <see
119 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
120 public WaitHandle WaitHandle
124 if (m_source
== null)
126 InitializeDefaultSource();
129 return m_source
.WaitHandle
;
133 // public CancellationToken()
134 // this constructor is implicit for structs
135 // -> this should behaves exactly as for new CancellationToken(false)
138 /// Internal constructor only a CancellationTokenSource should create a CancellationToken
140 internal CancellationToken(CancellationTokenSource source
)
146 /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
148 /// <param name="canceled">
149 /// The canceled state for the token.
152 /// Tokens created with this constructor will remain in the canceled state specified
153 /// by the <paramref name="canceled"/> parameter. If <paramref name="canceled"/> is false,
154 /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
155 /// If <paramref name="canceled"/> is true,
156 /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true.
158 public CancellationToken(bool canceled
) :
162 m_source
= CancellationTokenSource
.InternalGetStaticSource(canceled
);
168 private readonly static Action
<Object
> s_ActionToActionObjShunt
= new Action
<Object
>(ActionToActionObjShunt
);
169 private static void ActionToActionObjShunt(object obj
)
171 Action action
= obj
as Action
;
172 Contract
.Assert(action
!= null, "Expected an Action here");
177 /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
181 /// If this token is already in the canceled state, the
182 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
183 /// propagated out of this method call.
186 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
187 /// along with the delegate and will be used when executing it.
190 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
191 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
192 /// be used to deregister the callback.</returns>
193 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
194 public CancellationTokenRegistration
Register(Action callback
)
196 if (callback
== null)
197 throw new ArgumentNullException("callback");
200 s_ActionToActionObjShunt
,
202 false, // useSync=false
203 true // useExecutionContext=true
208 /// Registers a delegate that will be called when this
209 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
213 /// If this token is already in the canceled state, the
214 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
215 /// propagated out of this method call.
218 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
219 /// along with the delegate and will be used when executing it.
222 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
223 /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
224 /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
225 /// when invoking the <paramref name="callback"/>.</param>
226 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
227 /// be used to deregister the callback.</returns>
228 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
229 public CancellationTokenRegistration
Register(Action callback
, bool useSynchronizationContext
)
231 if (callback
== null)
232 throw new ArgumentNullException("callback");
235 s_ActionToActionObjShunt
,
237 useSynchronizationContext
,
238 true // useExecutionContext=true
243 /// Registers a delegate that will be called when this
244 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
248 /// If this token is already in the canceled state, the
249 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
250 /// propagated out of this method call.
253 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
254 /// along with the delegate and will be used when executing it.
257 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
258 /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
259 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
260 /// be used to deregister the callback.</returns>
261 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
262 public CancellationTokenRegistration
Register(Action
<Object
> callback
, Object state
)
264 if (callback
== null)
265 throw new ArgumentNullException("callback");
270 false, // useSync=false
271 true // useExecutionContext=true
276 /// Registers a delegate that will be called when this
277 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
281 /// If this token is already in the canceled state, the
282 /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
283 /// propagated out of this method call.
286 /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists,
287 /// will be captured along with the delegate and will be used when executing it.
290 /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
291 /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
292 /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
293 /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
294 /// when invoking the <paramref name="callback"/>.</param>
295 /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
296 /// be used to deregister the callback.</returns>
297 /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
298 /// <exception cref="T:System.ObjectDisposedException">The associated <see
299 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
300 public CancellationTokenRegistration
Register(Action
<Object
> callback
, Object state
, bool useSynchronizationContext
)
305 useSynchronizationContext
,
306 true // useExecutionContext=true
310 // helper for internal registration needs that don't require an EC capture (e.g. creating linked token sources, or registering unstarted TPL tasks)
311 // has a handy signature, and skips capturing execution context.
312 internal CancellationTokenRegistration
InternalRegisterWithoutEC(Action
<object> callback
, Object state
)
317 false, // useSyncContext=false
318 false // useExecutionContext=false
323 [SecuritySafeCritical
]
324 [MethodImpl(MethodImplOptions
.NoInlining
)]
325 private CancellationTokenRegistration
Register(Action
<Object
> callback
, Object state
, bool useSynchronizationContext
, bool useExecutionContext
)
327 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
329 if (callback
== null)
330 throw new ArgumentNullException("callback");
332 if (CanBeCanceled
== false)
334 return new CancellationTokenRegistration(); // nothing to do for tokens than can never reach the canceled state. Give them a dummy registration.
337 // Capture sync/execution contexts if required.
338 // Note: Only capture sync/execution contexts if IsCancellationRequested = false
339 // as we know that if it is true that the callback will just be called synchronously.
341 SynchronizationContext capturedSyncContext
= null;
342 ExecutionContext capturedExecutionContext
= null;
343 if (!IsCancellationRequested
)
345 if (useSynchronizationContext
)
346 capturedSyncContext
= SynchronizationContext
.Current
;
347 if (useExecutionContext
)
348 capturedExecutionContext
= ExecutionContext
.Capture(
349 ref stackMark
, ExecutionContext
.CaptureOptions
.OptimizeDefaultCase
); // ideally we'd also use IgnoreSyncCtx, but that could break compat
352 // Register the callback with the source.
353 return m_source
.InternalRegister(callback
, state
, capturedSyncContext
, capturedExecutionContext
);
357 /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
360 /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
361 /// instance.</param>
362 /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
363 /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
364 /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
365 public bool Equals(CancellationToken other
)
367 //if both sources are null, then both tokens represent the Empty token.
368 if (m_source
== null && other
.m_source
== null)
373 // one is null but other has inflated the default source
374 // these are only equal if the inflated one is the staticSource(false)
375 if (m_source
== null)
377 return other
.m_source
== CancellationTokenSource
.InternalGetStaticSource(false);
380 if (other
.m_source
== null)
382 return m_source
== CancellationTokenSource
.InternalGetStaticSource(false);
385 // general case, we check if the sources are identical
387 return m_source
== other
.m_source
;
391 /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
392 /// specified <see cref="T:System.Object"/>.
394 /// <param name="other">The other object to which to compare this instance.</param>
395 /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
396 /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
397 /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
398 /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
399 /// <exception cref="T:System.ObjectDisposedException">An associated <see
400 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
401 public override bool Equals(Object other
)
403 if (other
is CancellationToken
)
405 return Equals((CancellationToken
) other
);
412 /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
414 /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
415 public override Int32
GetHashCode()
417 if (m_source
== null)
419 // link to the common source so that we have a source to interrogate.
420 return CancellationTokenSource
.InternalGetStaticSource(false).GetHashCode();
423 return m_source
.GetHashCode();
427 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
429 /// <param name="left">The first instance.</param>
430 /// <param name="right">The second instance.</param>
431 /// <returns>True if the instances are equal; otherwise, false.</returns>
432 /// <exception cref="T:System.ObjectDisposedException">An associated <see
433 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
434 public static bool operator ==(CancellationToken left
, CancellationToken right
)
436 return left
.Equals(right
);
440 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
442 /// <param name="left">The first instance.</param>
443 /// <param name="right">The second instance.</param>
444 /// <returns>True if the instances are not equal; otherwise, false.</returns>
445 /// <exception cref="T:System.ObjectDisposedException">An associated <see
446 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
447 public static bool operator !=(CancellationToken left
, CancellationToken right
)
449 return !left
.Equals(right
);
453 /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
454 /// this token has had cancellation requested.
457 /// This method provides functionality equivalent to:
459 /// if (token.IsCancellationRequested)
460 /// throw new OperationCanceledException(token);
463 /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
464 /// <exception cref="T:System.ObjectDisposedException">The associated <see
465 /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
466 public void ThrowIfCancellationRequested()
468 if (IsCancellationRequested
)
469 ThrowOperationCanceledException();
472 // Throw an ODE if this CancellationToken's source is disposed.
473 internal void ThrowIfSourceDisposed()
475 if ((m_source
!= null) && m_source
.IsDisposed
)
476 ThrowObjectDisposedException();
479 // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
480 private void ThrowOperationCanceledException()
482 throw new OperationCanceledException(Environment
.GetResourceString("OperationCanceled"), this);
485 private static void ThrowObjectDisposedException()
487 throw new ObjectDisposedException(null, Environment
.GetResourceString("CancellationToken_SourceDisposed"));
490 // -----------------------------------
493 private void InitializeDefaultSource()
495 // Lazy is slower, and although multiple threads may ---- and set m_source repeatedly, the ---- is benign.
496 // Alternative: LazyInititalizer.EnsureInitialized(ref m_source, ()=>CancellationTokenSource.InternalGetStaticSource(false));
498 m_source
= CancellationTokenSource
.InternalGetStaticSource(false);