mscorlib(4.0.0.0) API with additions
CancellationTokenSource.cs
4 
5 namespace System.Threading
6 {
8  [ComVisible(false)]
9  [__DynamicallyInvokable]
10  [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
12  {
13  private static readonly CancellationTokenSource _staticSource_Set = new CancellationTokenSource(set: true);
14 
15  private static readonly CancellationTokenSource _staticSource_NotCancelable = new CancellationTokenSource(set: false);
16 
17  private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount;
18 
19  private volatile ManualResetEvent m_kernelEvent;
20 
21  private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists;
22 
23  private const int CANNOT_BE_CANCELED = 0;
24 
25  private const int NOT_CANCELED = 1;
26 
27  private const int NOTIFYING = 2;
28 
29  private const int NOTIFYINGCOMPLETE = 3;
30 
31  private volatile int m_state;
32 
33  private volatile int m_threadIDExecutingCallbacks = -1;
34 
35  private bool m_disposed;
36 
37  private CancellationTokenRegistration[] m_linkingRegistrations;
38 
39  private static readonly Action<object> s_LinkedTokenCancelDelegate = LinkedTokenCancelDelegate;
40 
41  private volatile CancellationCallbackInfo m_executingCallback;
42 
43  private volatile Timer m_timer;
44 
45  private static readonly TimerCallback s_timerCallback = TimerCallbackLogic;
46 
50  [__DynamicallyInvokable]
51  public bool IsCancellationRequested
52  {
53  [__DynamicallyInvokable]
54  get
55  {
56  return m_state >= 2;
57  }
58  }
59 
60  internal bool IsCancellationCompleted => m_state == 3;
61 
62  internal bool IsDisposed => m_disposed;
63 
64  internal int ThreadIDExecutingCallbacks
65  {
66  get
67  {
68  return m_threadIDExecutingCallbacks;
69  }
70  set
71  {
72  m_threadIDExecutingCallbacks = value;
73  }
74  }
75 
79  [__DynamicallyInvokable]
80  public CancellationToken Token
81  {
82  [__DynamicallyInvokable]
83  get
84  {
85  ThrowIfDisposed();
86  return new CancellationToken(this);
87  }
88  }
89 
90  internal bool CanBeCanceled => m_state != 0;
91 
92  internal WaitHandle WaitHandle
93  {
94  get
95  {
96  ThrowIfDisposed();
97  if (m_kernelEvent != null)
98  {
99  return m_kernelEvent;
100  }
101  ManualResetEvent manualResetEvent = new ManualResetEvent(initialState: false);
102  if (Interlocked.CompareExchange(ref m_kernelEvent, manualResetEvent, null) != null)
103  {
104  ((IDisposable)manualResetEvent).Dispose();
105  }
106  if (IsCancellationRequested)
107  {
108  m_kernelEvent.Set();
109  }
110  return m_kernelEvent;
111  }
112  }
113 
114  internal CancellationCallbackInfo ExecutingCallback => m_executingCallback;
115 
116  private static void LinkedTokenCancelDelegate(object source)
117  {
118  CancellationTokenSource cancellationTokenSource = source as CancellationTokenSource;
119  cancellationTokenSource.Cancel();
120  }
121 
123  [__DynamicallyInvokable]
125  {
126  m_state = 1;
127  }
128 
129  private CancellationTokenSource(bool set)
130  {
131  m_state = (set ? 3 : 0);
132  }
133 
140  [__DynamicallyInvokable]
142  {
143  long num = (long)delay.TotalMilliseconds;
144  if (num < -1 || num > int.MaxValue)
145  {
146  throw new ArgumentOutOfRangeException("delay");
147  }
148  InitializeWithTimer((int)num);
149  }
150 
155  [__DynamicallyInvokable]
156  public CancellationTokenSource(int millisecondsDelay)
157  {
158  if (millisecondsDelay < -1)
159  {
160  throw new ArgumentOutOfRangeException("millisecondsDelay");
161  }
162  InitializeWithTimer(millisecondsDelay);
163  }
164 
165  private void InitializeWithTimer(int millisecondsDelay)
166  {
167  m_state = 1;
168  m_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
169  }
170 
174  [__DynamicallyInvokable]
175  public void Cancel()
176  {
177  Cancel(throwOnFirstException: false);
178  }
179 
185  [__DynamicallyInvokable]
186  public void Cancel(bool throwOnFirstException)
187  {
188  ThrowIfDisposed();
189  NotifyCancellation(throwOnFirstException);
190  }
191 
196  [__DynamicallyInvokable]
197  public void CancelAfter(TimeSpan delay)
198  {
199  long num = (long)delay.TotalMilliseconds;
200  if (num < -1 || num > int.MaxValue)
201  {
202  throw new ArgumentOutOfRangeException("delay");
203  }
204  CancelAfter((int)num);
205  }
206 
211  [__DynamicallyInvokable]
212  public void CancelAfter(int millisecondsDelay)
213  {
214  ThrowIfDisposed();
215  if (millisecondsDelay < -1)
216  {
217  throw new ArgumentOutOfRangeException("millisecondsDelay");
218  }
219  if (IsCancellationRequested)
220  {
221  return;
222  }
223  if (m_timer == null)
224  {
225  Timer timer = new Timer(s_timerCallback, this, -1, -1);
226  if (Interlocked.CompareExchange(ref m_timer, timer, null) != null)
227  {
228  timer.Dispose();
229  }
230  }
231  try
232  {
233  m_timer.Change(millisecondsDelay, -1);
234  }
236  {
237  }
238  }
239 
240  private static void TimerCallbackLogic(object obj)
241  {
242  CancellationTokenSource cancellationTokenSource = (CancellationTokenSource)obj;
243  if (!cancellationTokenSource.IsDisposed)
244  {
245  try
246  {
247  cancellationTokenSource.Cancel();
248  }
250  {
251  if (!cancellationTokenSource.IsDisposed)
252  {
253  throw;
254  }
255  }
256  }
257  }
258 
260  [__DynamicallyInvokable]
261  public void Dispose()
262  {
263  Dispose(disposing: true);
264  GC.SuppressFinalize(this);
265  }
266 
270  [__DynamicallyInvokable]
271  protected virtual void Dispose(bool disposing)
272  {
273  if (!disposing || m_disposed)
274  {
275  return;
276  }
277  if (m_timer != null)
278  {
279  m_timer.Dispose();
280  }
281  CancellationTokenRegistration[] linkingRegistrations = m_linkingRegistrations;
282  if (linkingRegistrations != null)
283  {
284  m_linkingRegistrations = null;
285  for (int i = 0; i < linkingRegistrations.Length; i++)
286  {
287  linkingRegistrations[i].Dispose();
288  }
289  }
290  m_registeredCallbacksLists = null;
291  if (m_kernelEvent != null)
292  {
293  m_kernelEvent.Close();
294  m_kernelEvent = null;
295  }
296  m_disposed = true;
297  }
298 
299  internal void ThrowIfDisposed()
300  {
301  if (m_disposed)
302  {
303  ThrowObjectDisposedException();
304  }
305  }
306 
307  private static void ThrowObjectDisposedException()
308  {
309  throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationTokenSource_Disposed"));
310  }
311 
312  internal static CancellationTokenSource InternalGetStaticSource(bool set)
313  {
314  if (!set)
315  {
316  return _staticSource_NotCancelable;
317  }
318  return _staticSource_Set;
319  }
320 
321  internal CancellationTokenRegistration InternalRegister(Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext executionContext)
322  {
323  if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
324  {
325  ThrowIfDisposed();
326  }
327  if (!IsCancellationRequested)
328  {
329  if (m_disposed && !AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
330  {
331  return default(CancellationTokenRegistration);
332  }
333  int num = Thread.CurrentThread.ManagedThreadId % s_nLists;
334  CancellationCallbackInfo cancellationCallbackInfo = new CancellationCallbackInfo(callback, stateForCallback, targetSyncContext, executionContext, this);
335  SparselyPopulatedArray<CancellationCallbackInfo>[] array = m_registeredCallbacksLists;
336  if (array == null)
337  {
338  SparselyPopulatedArray<CancellationCallbackInfo>[] array2 = new SparselyPopulatedArray<CancellationCallbackInfo>[s_nLists];
339  array = Interlocked.CompareExchange(ref m_registeredCallbacksLists, array2, null);
340  if (array == null)
341  {
342  array = array2;
343  }
344  }
345  SparselyPopulatedArray<CancellationCallbackInfo> sparselyPopulatedArray = Volatile.Read(ref array[num]);
346  if (sparselyPopulatedArray == null)
347  {
348  SparselyPopulatedArray<CancellationCallbackInfo> value = new SparselyPopulatedArray<CancellationCallbackInfo>(4);
349  Interlocked.CompareExchange(ref array[num], value, null);
350  sparselyPopulatedArray = array[num];
351  }
352  SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> registrationInfo = sparselyPopulatedArray.Add(cancellationCallbackInfo);
353  CancellationTokenRegistration result = new CancellationTokenRegistration(cancellationCallbackInfo, registrationInfo);
354  if (!IsCancellationRequested)
355  {
356  return result;
357  }
358  if (!result.TryDeregister())
359  {
360  return result;
361  }
362  }
363  callback(stateForCallback);
364  return default(CancellationTokenRegistration);
365  }
366 
367  private void NotifyCancellation(bool throwOnFirstException)
368  {
369  if (!IsCancellationRequested && Interlocked.CompareExchange(ref m_state, 2, 1) == 1)
370  {
371  m_timer?.Dispose();
372  ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
373  if (m_kernelEvent != null)
374  {
375  m_kernelEvent.Set();
376  }
377  ExecuteCallbackHandlers(throwOnFirstException);
378  }
379  }
380 
381  private void ExecuteCallbackHandlers(bool throwOnFirstException)
382  {
383  List<Exception> list = null;
384  SparselyPopulatedArray<CancellationCallbackInfo>[] registeredCallbacksLists = m_registeredCallbacksLists;
385  if (registeredCallbacksLists == null)
386  {
387  Interlocked.Exchange(ref m_state, 3);
388  return;
389  }
390  try
391  {
392  for (int i = 0; i < registeredCallbacksLists.Length; i++)
393  {
394  SparselyPopulatedArray<CancellationCallbackInfo> sparselyPopulatedArray = Volatile.Read(ref registeredCallbacksLists[i]);
395  if (sparselyPopulatedArray != null)
396  {
397  for (SparselyPopulatedArrayFragment<CancellationCallbackInfo> sparselyPopulatedArrayFragment = sparselyPopulatedArray.Tail; sparselyPopulatedArrayFragment != null; sparselyPopulatedArrayFragment = sparselyPopulatedArrayFragment.Prev)
398  {
399  for (int num = sparselyPopulatedArrayFragment.Length - 1; num >= 0; num--)
400  {
401  m_executingCallback = sparselyPopulatedArrayFragment[num];
402  if (m_executingCallback != null)
403  {
404  CancellationCallbackCoreWorkArguments cancellationCallbackCoreWorkArguments = new CancellationCallbackCoreWorkArguments(sparselyPopulatedArrayFragment, num);
405  try
406  {
407  if (m_executingCallback.TargetSyncContext != null)
408  {
409  m_executingCallback.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, cancellationCallbackCoreWorkArguments);
410  ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
411  }
412  else
413  {
414  CancellationCallbackCoreWork(cancellationCallbackCoreWorkArguments);
415  }
416  }
417  catch (Exception item)
418  {
419  if (throwOnFirstException)
420  {
421  throw;
422  }
423  if (list == null)
424  {
425  list = new List<Exception>();
426  }
427  list.Add(item);
428  }
429  }
430  }
431  }
432  }
433  }
434  }
435  finally
436  {
437  m_state = 3;
438  m_executingCallback = null;
439  Thread.MemoryBarrier();
440  }
441  if (list == null)
442  {
443  return;
444  }
445  throw new AggregateException(list);
446  }
447 
448  private void CancellationCallbackCoreWork_OnSyncContext(object obj)
449  {
450  CancellationCallbackCoreWork((CancellationCallbackCoreWorkArguments)obj);
451  }
452 
453  private void CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args)
454  {
455  CancellationCallbackInfo cancellationCallbackInfo = args.m_currArrayFragment.SafeAtomicRemove(args.m_currArrayIndex, m_executingCallback);
456  if (cancellationCallbackInfo == m_executingCallback)
457  {
458  if (cancellationCallbackInfo.TargetExecutionContext != null)
459  {
460  cancellationCallbackInfo.CancellationTokenSource.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
461  }
462  cancellationCallbackInfo.ExecuteCallback();
463  }
464  }
465 
471  [__DynamicallyInvokable]
473  {
474  CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
475  bool canBeCanceled = token2.CanBeCanceled;
476  if (token1.CanBeCanceled)
477  {
478  cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[(!canBeCanceled) ? 1 : 2];
479  cancellationTokenSource.m_linkingRegistrations[0] = token1.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, cancellationTokenSource);
480  }
481  if (canBeCanceled)
482  {
483  int num = 1;
484  if (cancellationTokenSource.m_linkingRegistrations == null)
485  {
486  cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[1];
487  num = 0;
488  }
489  cancellationTokenSource.m_linkingRegistrations[num] = token2.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, cancellationTokenSource);
490  }
491  return cancellationTokenSource;
492  }
493 
502  [__DynamicallyInvokable]
504  {
505  if (tokens == null)
506  {
507  throw new ArgumentNullException("tokens");
508  }
509  if (tokens.Length == 0)
510  {
511  throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty"));
512  }
513  CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
514  cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length];
515  for (int i = 0; i < tokens.Length; i++)
516  {
517  if (tokens[i].CanBeCanceled)
518  {
519  cancellationTokenSource.m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, cancellationTokenSource);
520  }
521  }
522  return cancellationTokenSource;
523  }
524 
525  internal void WaitForCallbackToComplete(CancellationCallbackInfo callbackInfo)
526  {
527  SpinWait spinWait = default(SpinWait);
528  while (ExecutingCallback == callbackInfo)
529  {
530  spinWait.SpinOnce();
531  }
532  }
533  }
534 }
CancellationTokenSource(int millisecondsDelay)
Initializes a new instance of the T:System.Threading.CancellationTokenSource class that will be cance...
The exception that is thrown when a null reference (Nothing in Visual Basic) is passed to a method th...
CancellationTokenSource(TimeSpan delay)
Initializes a new instance of the T:System.Threading.CancellationTokenSource class that will be cance...
Propagates notification that operations should be canceled.
Encapsulates operating system–specific objects that wait for exclusive access to shared resources.
Definition: WaitHandle.cs:15
static void SuppressFinalize(object obj)
Requests that the common language runtime not call the finalizer for the specified object.
Definition: GC.cs:308
Provides support for spin-based waiting.
Definition: SpinWait.cs:8
static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2)
Creates a T:System.Threading.CancellationTokenSource that will be in the canceled state when any of t...
Provides a mechanism for releasing unmanaged resources.To browse the .NET Framework source code for t...
Definition: IDisposable.cs:8
void Cancel()
Communicates a request for cancellation.
Definition: __Canon.cs:3
The exception that is thrown when the value of an argument is outside the allowable range of values a...
Represents a callback delegate that has been registered with a T:System.Threading....
double TotalMilliseconds
Gets the value of the current T:System.TimeSpan structure expressed in whole and fractional milliseco...
Definition: TimeSpan.cs:180
bool CanBeCanceled
Gets whether this token is capable of being in the canceled state.
The exception that is thrown when an operation is performed on a disposed object.
SecurityAction
Specifies the security actions that can be performed using declarative security.
Provides information about, and means to manipulate, the current environment and platform....
Definition: Environment.cs:21
static int CompareExchange(ref int location1, int value, int comparand)
Compares two 32-bit signed integers for equality and, if they are equal, replaces the first value.
void Dispose()
Releases all resources used by the current instance of the T:System.Threading.CancellationTokenSource...
delegate void TimerCallback(object state)
Represents the method that handles calls from a T:System.Threading.Timer.
Provides a mechanism for executing a method on a thread pool thread at specified intervals....
Definition: Timer.cs:12
Notifies one or more waiting threads that an event has occurred. This class cannot be inherited.
void Cancel(bool throwOnFirstException)
Communicates a request for cancellation, and specifies whether remaining callbacks and cancelable ope...
bool Dispose(WaitHandle notifyObject)
Releases all resources used by the current instance of T:System.Threading.Timer and signals when the ...
Definition: Timer.cs:238
Controls the system garbage collector, a service that automatically reclaims unused memory.
Definition: GC.cs:11
CancellationTokenSource()
Initializes a new instance of the T:System.Threading.CancellationTokenSource class.
The exception that is thrown when one of the arguments provided to a method is not valid.
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search...
Definition: List.cs:14
void SpinOnce()
Performs a single spin.
Definition: SpinWait.cs:48
Represents a time interval.To browse the .NET Framework source code for this type,...
Definition: TimeSpan.cs:12
static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens)
Creates a T:System.Threading.CancellationTokenSource that will be in the canceled state when any of t...
Signals to a T:System.Threading.CancellationToken that it should be canceled.
bool IsCancellationRequested
Gets whether cancellation has been requested for this T:System.Threading.CancellationTokenSource.
void CancelAfter(int millisecondsDelay)
Schedules a cancel operation on this T:System.Threading.CancellationTokenSource after the specified n...
Provides atomic operations for variables that are shared by multiple threads.
Definition: Interlocked.cs:10
void Dispose()
Releases all resources used by the current instance of the T:System.Threading.CancellationTokenRegist...
void CancelAfter(TimeSpan delay)
Schedules a cancel operation on this T:System.Threading.CancellationTokenSource after the specified t...
virtual void Dispose(bool disposing)
Releases the unmanaged resources used by the T:System.Threading.CancellationTokenSource class and opt...