CancellationToken.cs 21 KB

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