Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / threading / CancellationToken.cs
blob5367d15d32383984f1242fe2f1519fdc46bbe6f9
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 //
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.
12 using System;
13 using System.Diagnostics;
14 using System.Runtime.InteropServices;
15 using System.Security.Permissions;
16 using System.Diagnostics.Contracts;
17 using System.Runtime;
18 using System.Runtime.CompilerServices;
19 using System.Security;
21 namespace System.Threading
23 /// <summary>
24 /// Propagates notification that operations should be canceled.
25 /// </summary>
26 /// <remarks>
27 /// <para>
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.
34 /// </para>
35 /// <para>
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.
38 /// </para>
39 /// <para>
40 /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
41 /// </para>
42 /// </remarks>
43 [ComVisible(false)]
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
54 /* Properties */
56 /// <summary>
57 /// Returns an empty CancellationToken value.
58 /// </summary>
59 /// <remarks>
60 /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
61 /// </remarks>
62 public static CancellationToken None
64 get { return default(CancellationToken); }
67 /// <summary>
68 /// Gets whether cancellation has been requested for this token.
69 /// </summary>
70 /// <value>Whether cancellation has been requested for this token.</value>
71 /// <remarks>
72 /// <para>
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"/>.
77 /// </para>
78 /// <para>
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.
84 /// </para>
85 /// </remarks>
86 public bool IsCancellationRequested
88 get
90 return m_source != null && m_source.IsCancellationRequested;
94 /// <summary>
95 /// Gets whether this token is capable of being in the canceled state.
96 /// </summary>
97 /// <remarks>
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
100 /// return true.
101 /// </remarks>
102 public bool CanBeCanceled
106 return m_source != null && m_source.CanBeCanceled;
110 /// <summary>
111 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
112 /// <remarks>
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.
117 /// </remarks>
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)
137 /// <summary>
138 /// Internal constructor only a CancellationTokenSource should create a CancellationToken
139 /// </summary>
140 internal CancellationToken(CancellationTokenSource source)
142 m_source = source;
145 /// <summary>
146 /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
147 /// </summary>
148 /// <param name="canceled">
149 /// The canceled state for the token.
150 /// </param>
151 /// <remarks>
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.
157 /// </remarks>
158 public CancellationToken(bool canceled) :
159 this()
161 if(canceled)
162 m_source = CancellationTokenSource.InternalGetStaticSource(canceled);
165 /* Methods */
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");
173 action();
176 /// <summary>
177 /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
178 /// </summary>
179 /// <remarks>
180 /// <para>
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.
184 /// </para>
185 /// <para>
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.
188 /// </para>
189 /// </remarks>
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");
199 return Register(
200 s_ActionToActionObjShunt,
201 callback,
202 false, // useSync=false
203 true // useExecutionContext=true
207 /// <summary>
208 /// Registers a delegate that will be called when this
209 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
210 /// </summary>
211 /// <remarks>
212 /// <para>
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.
216 /// </para>
217 /// <para>
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.
220 /// </para>
221 /// </remarks>
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");
234 return Register(
235 s_ActionToActionObjShunt,
236 callback,
237 useSynchronizationContext,
238 true // useExecutionContext=true
242 /// <summary>
243 /// Registers a delegate that will be called when this
244 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
245 /// </summary>
246 /// <remarks>
247 /// <para>
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.
251 /// </para>
252 /// <para>
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.
255 /// </para>
256 /// </remarks>
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");
267 return Register(
268 callback,
269 state,
270 false, // useSync=false
271 true // useExecutionContext=true
275 /// <summary>
276 /// Registers a delegate that will be called when this
277 /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
278 /// </summary>
279 /// <remarks>
280 /// <para>
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.
284 /// </para>
285 /// <para>
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.
288 /// </para>
289 /// </remarks>
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)
302 return Register(
303 callback,
304 state,
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)
314 return Register(
315 callback,
316 state,
317 false, // useSyncContext=false
318 false // useExecutionContext=false
322 // the real work..
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);
356 /// <summary>
357 /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
358 /// specified token.
359 /// </summary>
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)
370 return true;
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;
390 /// <summary>
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"/>.
393 /// </summary>
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);
408 return false;
411 /// <summary>
412 /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
413 /// </summary>
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();
426 /// <summary>
427 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
428 /// </summary>
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);
439 /// <summary>
440 /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
441 /// </summary>
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);
452 /// <summary>
453 /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
454 /// this token has had cancellation requested.
455 /// </summary>
456 /// <remarks>
457 /// This method provides functionality equivalent to:
458 /// <code>
459 /// if (token.IsCancellationRequested)
460 /// throw new OperationCanceledException(token);
461 /// </code>
462 /// </remarks>
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 // -----------------------------------
491 // Private helpers
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);