CancellationToken.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using System.Diagnostics.CodeAnalysis;
  6. namespace System.Threading
  7. {
  8. /// <summary>
  9. /// Propagates notification that operations should be canceled.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>
  13. /// A <see cref="CancellationToken"/> may be created directly in an unchangeable canceled or non-canceled state
  14. /// using the CancellationToken's constructors. However, to have a CancellationToken that can change
  15. /// from a non-canceled to a canceled state,
  16. /// <see cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see> must be used.
  17. /// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its
  18. /// <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property.
  19. /// </para>
  20. /// <para>
  21. /// Once canceled, a token may not transition to a non-canceled state, and a token whose
  22. /// <see cref="CanBeCanceled"/> is false will never change to one that can be canceled.
  23. /// </para>
  24. /// <para>
  25. /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
  26. /// </para>
  27. /// </remarks>
  28. [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
  29. public readonly struct CancellationToken
  30. {
  31. // The backing TokenSource.
  32. // if null, it implicitly represents the same thing as new CancellationToken(false).
  33. // When required, it will be instantiated to reflect this.
  34. private readonly CancellationTokenSource? _source;
  35. //!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid
  36. private static readonly Action<object?> s_actionToActionObjShunt = obj =>
  37. {
  38. Debug.Assert(obj is Action, $"Expected {typeof(Action)}, got {obj}");
  39. ((Action)obj)();
  40. };
  41. /// <summary>
  42. /// Returns an empty CancellationToken value.
  43. /// </summary>
  44. /// <remarks>
  45. /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
  46. /// </remarks>
  47. public static CancellationToken None => default;
  48. /// <summary>
  49. /// Gets whether cancellation has been requested for this token.
  50. /// </summary>
  51. /// <value>Whether cancellation has been requested for this token.</value>
  52. /// <remarks>
  53. /// <para>
  54. /// This property indicates whether cancellation has been requested for this token,
  55. /// either through the token initially being constructed in a canceled state, or through
  56. /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
  57. /// on the token's associated <see cref="CancellationTokenSource"/>.
  58. /// </para>
  59. /// <para>
  60. /// If this property is true, it only guarantees that cancellation has been requested.
  61. /// It does not guarantee that every registered handler
  62. /// has finished executing, nor that cancellation requests have finished propagating
  63. /// to all registered handlers. Additional synchronization may be required,
  64. /// particularly in situations where related objects are being canceled concurrently.
  65. /// </para>
  66. /// </remarks>
  67. public bool IsCancellationRequested => _source != null && _source.IsCancellationRequested;
  68. /// <summary>
  69. /// Gets whether this token is capable of being in the canceled state.
  70. /// </summary>
  71. /// <remarks>
  72. /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
  73. /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
  74. /// return true.
  75. /// </remarks>
  76. public bool CanBeCanceled => _source != null;
  77. /// <summary>
  78. /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
  79. /// <remarks>
  80. /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
  81. /// to be instantiated. It is preferable to only use this property when necessary, and to then
  82. /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
  83. /// the source will dispose of this allocated handle). The handle should not be closed or disposed directly.
  84. /// </remarks>
  85. /// <exception cref="T:System.ObjectDisposedException">The associated <see
  86. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  87. public WaitHandle WaitHandle => (_source ?? CancellationTokenSource.s_neverCanceledSource).WaitHandle;
  88. // public CancellationToken()
  89. // this constructor is implicit for structs
  90. // -> this should behaves exactly as for new CancellationToken(false)
  91. /// <summary>
  92. /// Internal constructor only a CancellationTokenSource should create a CancellationToken
  93. /// </summary>
  94. internal CancellationToken(CancellationTokenSource? source) => _source = source;
  95. /// <summary>
  96. /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
  97. /// </summary>
  98. /// <param name="canceled">
  99. /// The canceled state for the token.
  100. /// </param>
  101. /// <remarks>
  102. /// Tokens created with this constructor will remain in the canceled state specified
  103. /// by the <paramref name="canceled"/> parameter. If <paramref name="canceled"/> is false,
  104. /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
  105. /// If <paramref name="canceled"/> is true,
  106. /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true.
  107. /// </remarks>
  108. public CancellationToken(bool canceled) : this(canceled ? CancellationTokenSource.s_canceledSource : null)
  109. {
  110. }
  111. /// <summary>
  112. /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  113. /// </summary>
  114. /// <remarks>
  115. /// <para>
  116. /// If this token is already in the canceled state, the
  117. /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
  118. /// propagated out of this method call.
  119. /// </para>
  120. /// <para>
  121. /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
  122. /// along with the delegate and will be used when executing it.
  123. /// </para>
  124. /// </remarks>
  125. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  126. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  127. /// be used to unregister the callback.</returns>
  128. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  129. public CancellationTokenRegistration Register(Action callback) =>
  130. Register(
  131. s_actionToActionObjShunt,
  132. callback ?? throw new ArgumentNullException(nameof(callback)),
  133. useSynchronizationContext: false,
  134. useExecutionContext: true);
  135. /// <summary>
  136. /// Registers a delegate that will be called when this
  137. /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  138. /// </summary>
  139. /// <remarks>
  140. /// <para>
  141. /// If this token is already in the canceled state, the
  142. /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
  143. /// propagated out of this method call.
  144. /// </para>
  145. /// <para>
  146. /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
  147. /// along with the delegate and will be used when executing it.
  148. /// </para>
  149. /// </remarks>
  150. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  151. /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
  152. /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
  153. /// when invoking the <paramref name="callback"/>.</param>
  154. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  155. /// be used to unregister the callback.</returns>
  156. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  157. public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext) =>
  158. Register(
  159. s_actionToActionObjShunt,
  160. callback ?? throw new ArgumentNullException(nameof(callback)),
  161. useSynchronizationContext,
  162. useExecutionContext: true);
  163. /// <summary>
  164. /// Registers a delegate that will be called when this
  165. /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  166. /// </summary>
  167. /// <remarks>
  168. /// <para>
  169. /// If this token is already in the canceled state, the
  170. /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
  171. /// propagated out of this method call.
  172. /// </para>
  173. /// <para>
  174. /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
  175. /// along with the delegate and will be used when executing it.
  176. /// </para>
  177. /// </remarks>
  178. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  179. /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
  180. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  181. /// be used to unregister the callback.</returns>
  182. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  183. public CancellationTokenRegistration Register(Action<object?> callback, object? state) =>
  184. Register(callback, state, useSynchronizationContext: false, useExecutionContext: true);
  185. /// <summary>
  186. /// Registers a delegate that will be called when this
  187. /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  188. /// </summary>
  189. /// <remarks>
  190. /// <para>
  191. /// If this token is already in the canceled state, the
  192. /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
  193. /// propagated out of this method call.
  194. /// </para>
  195. /// <para>
  196. /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists,
  197. /// will be captured along with the delegate and will be used when executing it.
  198. /// </para>
  199. /// </remarks>
  200. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  201. /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
  202. /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
  203. /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
  204. /// when invoking the <paramref name="callback"/>.</param>
  205. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  206. /// be used to unregister the callback.</returns>
  207. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  208. /// <exception cref="T:System.ObjectDisposedException">The associated <see
  209. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  210. public CancellationTokenRegistration Register(Action<object?> callback, object? state, bool useSynchronizationContext) =>
  211. Register(callback, state, useSynchronizationContext, useExecutionContext: true);
  212. /// <summary>
  213. /// Registers a delegate that will be called when this
  214. /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  215. /// </summary>
  216. /// <remarks>
  217. /// <para>
  218. /// If this token is already in the canceled state, the delegate will be run immediately and synchronously.
  219. /// Any exception the delegate generates will be propagated out of this method call.
  220. /// </para>
  221. /// <para>
  222. /// <see cref="System.Threading.ExecutionContext">ExecutionContext</see> is not captured nor flowed
  223. /// to the callback's invocation.
  224. /// </para>
  225. /// </remarks>
  226. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  227. /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
  228. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  229. /// be used to unregister the callback.</returns>
  230. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  231. public CancellationTokenRegistration UnsafeRegister(Action<object?> callback, object? state) =>
  232. Register(callback, state, useSynchronizationContext: false, useExecutionContext: false);
  233. /// <summary>
  234. /// Registers a delegate that will be called when this
  235. /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
  236. /// </summary>
  237. /// <remarks>
  238. /// <para>
  239. /// If this token is already in the canceled state, the
  240. /// delegate will be run immediately and synchronously. Any exception the delegate generates will be
  241. /// propagated out of this method call.
  242. /// </para>
  243. /// </remarks>
  244. /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
  245. /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked. This may be null.</param>
  246. /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
  247. /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
  248. /// when invoking the <paramref name="callback"/>.</param>
  249. /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can
  250. /// be used to unregister the callback.</returns>
  251. /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
  252. /// <exception cref="T:System.ObjectDisposedException">The associated <see
  253. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  254. private CancellationTokenRegistration Register(Action<object?> callback, object? state, bool useSynchronizationContext, bool useExecutionContext)
  255. {
  256. if (callback == null)
  257. throw new ArgumentNullException(nameof(callback));
  258. CancellationTokenSource? source = _source;
  259. return source != null ?
  260. source.InternalRegister(callback, state, useSynchronizationContext ? SynchronizationContext.Current : null, useExecutionContext ? ExecutionContext.Capture() : null) :
  261. default; // Nothing to do for tokens than can never reach the canceled state. Give back a dummy registration.
  262. }
  263. /// <summary>
  264. /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
  265. /// specified token.
  266. /// </summary>
  267. /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
  268. /// instance.</param>
  269. /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
  270. /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
  271. /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
  272. public bool Equals(CancellationToken other) => _source == other._source;
  273. /// <summary>
  274. /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the
  275. /// specified <see cref="T:System.Object"/>.
  276. /// </summary>
  277. /// <param name="other">The other object to which to compare this instance.</param>
  278. /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
  279. /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
  280. /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed
  281. /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
  282. /// <exception cref="T:System.ObjectDisposedException">An associated <see
  283. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  284. public override bool Equals(object? other) => other is CancellationToken && Equals((CancellationToken)other);
  285. /// <summary>
  286. /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
  287. /// </summary>
  288. /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
  289. public override int GetHashCode() => (_source ?? CancellationTokenSource.s_neverCanceledSource).GetHashCode();
  290. /// <summary>
  291. /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
  292. /// </summary>
  293. /// <param name="left">The first instance.</param>
  294. /// <param name="right">The second instance.</param>
  295. /// <returns>True if the instances are equal; otherwise, false.</returns>
  296. /// <exception cref="T:System.ObjectDisposedException">An associated <see
  297. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  298. public static bool operator ==(CancellationToken left, CancellationToken right) => left.Equals(right);
  299. /// <summary>
  300. /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
  301. /// </summary>
  302. /// <param name="left">The first instance.</param>
  303. /// <param name="right">The second instance.</param>
  304. /// <returns>True if the instances are not equal; otherwise, false.</returns>
  305. /// <exception cref="T:System.ObjectDisposedException">An associated <see
  306. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  307. public static bool operator !=(CancellationToken left, CancellationToken right) => !left.Equals(right);
  308. /// <summary>
  309. /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
  310. /// this token has had cancellation requested.
  311. /// </summary>
  312. /// <remarks>
  313. /// This method provides functionality equivalent to:
  314. /// <code>
  315. /// if (token.IsCancellationRequested)
  316. /// throw new OperationCanceledException(token);
  317. /// </code>
  318. /// </remarks>
  319. /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
  320. /// <exception cref="T:System.ObjectDisposedException">The associated <see
  321. /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
  322. public void ThrowIfCancellationRequested()
  323. {
  324. if (IsCancellationRequested)
  325. ThrowOperationCanceledException();
  326. }
  327. // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
  328. [DoesNotReturn]
  329. private void ThrowOperationCanceledException() =>
  330. throw new OperationCanceledException(SR.OperationCanceled, this);
  331. }
  332. }