Implementation of GetLoopbackInterfaceIndex on Windows
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / CancellationTokenSource.cs
1 #pragma warning disable 0420
2 // ==++==
3 // 
4 //   Copyright (c) Microsoft Corporation.  All rights reserved.
5 // 
6 // ==--==
7 //
8 // <OWNER>[....]</OWNER>
9 ////////////////////////////////////////////////////////////////////////////////
10
11 using System;
12 using System.Security;
13 using System.Collections.Generic;
14 using System.Runtime.InteropServices;
15 using System.Security.Permissions;
16 using System.Diagnostics.Contracts;
17 using System.Runtime;
18
19 namespace System.Threading
20 {
21     /// <summary>
22     /// Signals to a <see cref="System.Threading.CancellationToken"/> that it should be canceled.
23     /// </summary>
24     /// <remarks>
25     /// <para>
26     /// <see cref="T:System.Threading.CancellationTokenSource"/> is used to instantiate a <see
27     /// cref="T:System.Threading.CancellationToken"/>
28     /// (via the source's <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property)
29     /// that can be handed to operations that wish to be notified of cancellation or that can be used to
30     /// register asynchronous operations for cancellation. That token may have cancellation requested by
31     /// calling to the source's <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
32     /// method.
33     /// </para>
34     /// <para>
35     /// All members of this class, except <see cref="Dispose">Dispose</see>, are thread-safe and may be used
36     /// concurrently from multiple threads.
37     /// </para>
38     /// </remarks>
39     [ComVisible(false)]
40     [HostProtection(Synchronization = true, ExternalThreading = true)]
41
42     public class CancellationTokenSource : IDisposable
43     {
44         //static sources that can be used as the backing source for 'fixed' CancellationTokens that never change state.
45         private static readonly CancellationTokenSource _staticSource_Set = new CancellationTokenSource(true);
46         private static readonly CancellationTokenSource _staticSource_NotCancelable = new CancellationTokenSource(false);
47
48         //Note: the callback lists array is only created on first registration.
49         //      the actual callback lists are only created on demand.
50         //      Storing a registered callback costs around >60bytes, hence some overhead for the lists array is OK
51         // At most 24 lists seems reasonable, and caps the cost of the listsArray to 96bytes(32-bit,24-way) or 192bytes(64-bit,24-way).
52         private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount; 
53
54         private volatile ManualResetEvent m_kernelEvent; //lazily initialized if required.
55
56         private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists;
57  
58         // legal values for m_state
59         private const int CANNOT_BE_CANCELED = 0;
60         private const int NOT_CANCELED = 1;
61         private const int NOTIFYING = 2;
62         private const int NOTIFYINGCOMPLETE = 3;
63         
64         //m_state uses the pattern "volatile int32 reads, with cmpxch writes" which is safe for updates and cannot suffer torn reads.
65         private volatile int m_state;
66
67
68         /// The ID of the thread currently executing the main body of CTS.Cancel()
69         /// this helps us to know if a call to ctr.Dispose() is running 'within' a cancellation callback.
70         /// This is updated as we move between the main thread calling cts.Cancel() and any syncContexts that are used to 
71         /// actually run the callbacks.
72         private volatile int m_threadIDExecutingCallbacks = -1;
73
74         private bool m_disposed;
75
76         private CancellationTokenRegistration [] m_linkingRegistrations; //lazily initialized if required.
77         
78         private static readonly Action<object> s_LinkedTokenCancelDelegate = new Action<object>(LinkedTokenCancelDelegate);
79         
80         // we track the running callback to assist ctr.Dispose() to wait for the target callback to complete.
81         private volatile CancellationCallbackInfo m_executingCallback;
82
83         // provided for CancelAfter and timer-related constructors
84         private volatile Timer m_timer;
85
86         private static void LinkedTokenCancelDelegate(object source)
87         {
88             CancellationTokenSource cts = source as CancellationTokenSource;
89             Contract.Assert(source != null);
90             cts.Cancel();
91         }
92         
93         // ---------------------- 
94         // ** public properties
95
96         /// <summary>
97         /// Gets whether cancellation has been requested for this <see
98         /// cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see>.
99         /// </summary>
100         /// <value>Whether cancellation has been requested for this <see
101         /// cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see>.</value>
102         /// <remarks>
103         /// <para>
104         /// This property indicates whether cancellation has been requested for this token source, such as
105         /// due to a call to its
106         /// <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see> method.
107         /// </para>
108         /// <para>
109         /// If this property returns true, it only guarantees that cancellation has been requested. It does not
110         /// guarantee that every handler registered with the corresponding token has finished executing, nor
111         /// that cancellation requests have finished propagating to all registered handlers. Additional
112         /// synchronization may be required, particularly in situations where related objects are being
113         /// canceled concurrently.
114         /// </para>
115         /// </remarks>
116         public bool IsCancellationRequested
117         {
118             get { return m_state >= NOTIFYING; }
119         }
120
121         /// <summary>
122         /// A simple helper to determine whether cancellation has finished.
123         /// </summary>
124         internal bool IsCancellationCompleted
125         {
126             get { return m_state == NOTIFYINGCOMPLETE; }
127         }
128
129         /// <summary>
130         /// A simple helper to determine whether disposal has occured.
131         /// </summary>
132         internal bool IsDisposed
133         {
134             get { return m_disposed; }
135         }
136
137         /// <summary>
138         /// The ID of the thread that is running callbacks.
139         /// </summary>
140         internal int ThreadIDExecutingCallbacks
141         {
142             set { m_threadIDExecutingCallbacks = value; }
143             get { return m_threadIDExecutingCallbacks; }
144         }
145
146         /// <summary>
147         /// Gets the <see cref="System.Threading.CancellationToken">CancellationToken</see>
148         /// associated with this <see cref="CancellationTokenSource"/>.
149         /// </summary>
150         /// <value>The <see cref="System.Threading.CancellationToken">CancellationToken</see>
151         /// associated with this <see cref="CancellationTokenSource"/>.</value>
152         /// <exception cref="T:System.ObjectDisposedException">The token source has been
153         /// disposed.</exception>
154         public CancellationToken Token
155         {
156             get
157             {
158                 ThrowIfDisposed();
159                 return new CancellationToken(this);
160             }
161         }
162
163         // ---------------------- 
164         // ** internal and private properties.
165
166         /// <summary>
167         ///
168         /// </summary>
169         internal bool CanBeCanceled
170         {
171             get { return m_state != CANNOT_BE_CANCELED; }
172         }
173
174         /// <summary>
175         ///
176         /// </summary>
177         internal WaitHandle WaitHandle
178         {
179             get
180             {
181                 ThrowIfDisposed();
182
183                 // fast path if already allocated.
184                 if (m_kernelEvent != null)
185                     return m_kernelEvent;
186                 
187                 // lazy-init the mre.
188                 ManualResetEvent mre = new ManualResetEvent(false);
189                 if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null)
190                 {    
191                     ((IDisposable)mre).Dispose();
192                 }
193
194                 // There is a ---- between checking IsCancellationRequested and setting the event.
195                 // However, at this point, the kernel object definitely exists and the cases are:
196                 //   1. if IsCancellationRequested = true, then we will call Set()
197                 //   2. if IsCancellationRequested = false, then NotifyCancellation will see that the event exists, and will call Set().
198                 if (IsCancellationRequested)
199                     m_kernelEvent.Set();
200
201                 return m_kernelEvent;
202             }
203         }
204
205
206         /// <summary>
207         /// The currently executing callback
208         /// </summary>
209         internal CancellationCallbackInfo ExecutingCallback
210         {
211             get { return m_executingCallback; }
212         }
213
214 #if DEBUG
215         /// <summary>
216         /// Used by the dev unit tests to check the number of outstanding registrations.
217         /// They use private reflection to gain access.  Because this would be dead retail
218         /// code, however, it is ifdef'd out to work only in debug builds.
219         /// </summary>
220         private int CallbackCount
221         {
222             get
223             {
224                 SparselyPopulatedArray<CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists;
225                 if (callbackLists == null)
226                     return 0;
227
228                 int count = 0;
229                 foreach(SparselyPopulatedArray<CancellationCallbackInfo> sparseArray in callbackLists)
230                 {
231                     if(sparseArray != null)
232                     {
233                         SparselyPopulatedArrayFragment<CancellationCallbackInfo> currCallbacks = sparseArray.Head;
234                         while (currCallbacks != null)
235                         {
236                             for (int i = 0; i < currCallbacks.Length; i++)
237                                 if (currCallbacks[i] != null)
238                                     count++;
239
240                             currCallbacks = currCallbacks.Next;
241                         }
242                     }
243                 }
244                 return count;
245             }
246         }
247 #endif
248
249         // ** Public Constructors
250
251         /// <summary>
252         /// Initializes the <see cref="T:System.Threading.CancellationTokenSource"/>.
253         /// </summary>
254         public CancellationTokenSource()
255         {
256             m_state = NOT_CANCELED;
257         }
258
259         // ** Private constructors for static sources.
260         // set=false ==> cannot be canceled.
261         // set=true  ==> is canceled. 
262         private CancellationTokenSource(bool set)
263         {
264             m_state = set ? NOTIFYINGCOMPLETE : CANNOT_BE_CANCELED;
265         }
266
267         /// <summary>
268         /// Constructs a <see cref="T:System.Threading.CancellationTokenSource"/> that will be canceled after a specified time span.
269         /// </summary>
270         /// <param name="delay">The time span to wait before canceling this <see cref="T:System.Threading.CancellationTokenSource"/></param>
271         /// <exception cref="T:System.ArgumentOutOfRangeException">
272         /// The exception that is thrown when <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
273         /// </exception>
274         /// <remarks>
275         /// <para>
276         /// The countdown for the delay starts during the call to the constructor.  When the delay expires, 
277         /// the constructed <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
278         /// not been canceled already.
279         /// </para>
280         /// <para>
281         /// Subsequent calls to CancelAfter will reset the delay for the constructed 
282         /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
283         /// canceled already.
284         /// </para>
285         /// </remarks>
286         public CancellationTokenSource(TimeSpan delay)
287         {
288             long totalMilliseconds = (long)delay.TotalMilliseconds;
289             if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
290             {
291                 throw new ArgumentOutOfRangeException("delay");
292             }
293
294             InitializeWithTimer((int)totalMilliseconds);
295         }
296
297         /// <summary>
298         /// Constructs a <see cref="T:System.Threading.CancellationTokenSource"/> that will be canceled after a specified time span.
299         /// </summary>
300         /// <param name="millisecondsDelay">The time span to wait before canceling this <see cref="T:System.Threading.CancellationTokenSource"/></param>
301         /// <exception cref="T:System.ArgumentOutOfRangeException">
302         /// The exception that is thrown when <paramref name="millisecondsDelay"/> is less than -1.
303         /// </exception>
304         /// <remarks>
305         /// <para>
306         /// The countdown for the millisecondsDelay starts during the call to the constructor.  When the millisecondsDelay expires, 
307         /// the constructed <see cref="T:System.Threading.CancellationTokenSource"/> is canceled (if it has
308         /// not been canceled already).
309         /// </para>
310         /// <para>
311         /// Subsequent calls to CancelAfter will reset the millisecondsDelay for the constructed 
312         /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
313         /// canceled already.
314         /// </para>
315         /// </remarks>
316         public CancellationTokenSource(Int32 millisecondsDelay)
317         {
318             if (millisecondsDelay < -1)
319             {
320                 throw new ArgumentOutOfRangeException("millisecondsDelay");
321             }
322
323             InitializeWithTimer(millisecondsDelay);
324         }
325
326         // Common initialization logic when constructing a CTS with a delay parameter
327         private void InitializeWithTimer(Int32 millisecondsDelay)
328         {
329             m_state = NOT_CANCELED;
330             m_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
331         }
332
333         // ** Public Methods
334
335         /// <summary>
336         /// Communicates a request for cancellation.
337         /// </summary>
338         /// <remarks>
339         /// <para>
340         /// The associated <see cref="T:System.Threading.CancellationToken" /> will be
341         /// notified of the cancellation and will transition to a state where 
342         /// <see cref="System.Threading.CancellationToken.IsCancellationRequested">IsCancellationRequested</see> returns true. 
343         /// Any callbacks or cancelable operations
344         /// registered with the <see cref="T:System.Threading.CancellationToken"/>  will be executed.
345         /// </para>
346         /// <para>
347         /// Cancelable operations and callbacks registered with the token should not throw exceptions.
348         /// However, this overload of Cancel will aggregate any exceptions thrown into a <see cref="System.AggregateException"/>,
349         /// such that one callback throwing an exception will not prevent other registered callbacks from being executed.
350         /// </para>
351         /// <para>
352         /// The <see cref="T:System.Threading.ExecutionContext"/> that was captured when each callback was registered
353         /// will be reestablished when the callback is invoked.
354         /// </para>
355         /// </remarks>
356         /// <exception cref="T:System.AggregateException">An aggregate exception containing all the exceptions thrown
357         /// by the registered callbacks on the associated <see cref="T:System.Threading.CancellationToken"/>.</exception>
358         /// <exception cref="T:System.ObjectDisposedException">This <see
359         /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.</exception> 
360         public void Cancel()
361         {
362             Cancel(false);
363         }
364
365         /// <summary>
366         /// Communicates a request for cancellation.
367         /// </summary>
368         /// <remarks>
369         /// <para>
370         /// The associated <see cref="T:System.Threading.CancellationToken" /> will be
371         /// notified of the cancellation and will transition to a state where 
372         /// <see cref="System.Threading.CancellationToken.IsCancellationRequested">IsCancellationRequested</see> returns true. 
373         /// Any callbacks or cancelable operations
374         /// registered with the <see cref="T:System.Threading.CancellationToken"/>  will be executed.
375         /// </para>
376         /// <para>
377         /// Cancelable operations and callbacks registered with the token should not throw exceptions. 
378         /// If <paramref name="throwOnFirstException"/> is true, an exception will immediately propagate out of the
379         /// call to Cancel, preventing the remaining callbacks and cancelable operations from being processed.
380         /// If <paramref name="throwOnFirstException"/> is false, this overload will aggregate any 
381         /// exceptions thrown into a <see cref="System.AggregateException"/>,
382         /// such that one callback throwing an exception will not prevent other registered callbacks from being executed.
383         /// </para>
384         /// <para>
385         /// The <see cref="T:System.Threading.ExecutionContext"/> that was captured when each callback was registered
386         /// will be reestablished when the callback is invoked.
387         /// </para>
388         /// </remarks>
389         /// <param name="throwOnFirstException">Specifies whether exceptions should immediately propagate.</param>
390         /// <exception cref="T:System.AggregateException">An aggregate exception containing all the exceptions thrown
391         /// by the registered callbacks on the associated <see cref="T:System.Threading.CancellationToken"/>.</exception>
392         /// <exception cref="T:System.ObjectDisposedException">This <see
393         /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.</exception> 
394         public void Cancel(bool throwOnFirstException)
395         {
396             ThrowIfDisposed();
397             NotifyCancellation(throwOnFirstException);            
398         }
399
400         /// <summary>
401         /// Schedules a Cancel operation on this <see cref="T:System.Threading.CancellationTokenSource"/>.
402         /// </summary>
403         /// <param name="delay">The time span to wait before canceling this <see
404         /// cref="T:System.Threading.CancellationTokenSource"/>.
405         /// </param>
406         /// <exception cref="T:System.ObjectDisposedException">The exception thrown when this <see
407         /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.
408         /// </exception>
409         /// <exception cref="T:System.ArgumentOutOfRangeException">
410         /// The exception thrown when <paramref name="delay"/> is less than -1 or 
411         /// greater than Int32.MaxValue.
412         /// </exception>
413         /// <remarks>
414         /// <para>
415         /// The countdown for the delay starts during this call.  When the delay expires, 
416         /// this <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
417         /// not been canceled already.
418         /// </para>
419         /// <para>
420         /// Subsequent calls to CancelAfter will reset the delay for this  
421         /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
422         /// canceled already.
423         /// </para>
424         /// </remarks>
425         public void CancelAfter(TimeSpan delay)
426         {
427             long totalMilliseconds = (long)delay.TotalMilliseconds;
428             if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
429             {
430                 throw new ArgumentOutOfRangeException("delay");
431             }
432
433             CancelAfter((int)totalMilliseconds);
434         }
435
436         /// <summary>
437         /// Schedules a Cancel operation on this <see cref="T:System.Threading.CancellationTokenSource"/>.
438         /// </summary>
439         /// <param name="millisecondsDelay">The time span to wait before canceling this <see
440         /// cref="T:System.Threading.CancellationTokenSource"/>.
441         /// </param>
442         /// <exception cref="T:System.ObjectDisposedException">The exception thrown when this <see
443         /// cref="T:System.Threading.CancellationTokenSource"/> has been disposed.
444         /// </exception>
445         /// <exception cref="T:System.ArgumentOutOfRangeException">
446         /// The exception thrown when <paramref name="millisecondsDelay"/> is less than -1.
447         /// </exception>
448         /// <remarks>
449         /// <para>
450         /// The countdown for the millisecondsDelay starts during this call.  When the millisecondsDelay expires, 
451         /// this <see cref="T:System.Threading.CancellationTokenSource"/> is canceled, if it has
452         /// not been canceled already.
453         /// </para>
454         /// <para>
455         /// Subsequent calls to CancelAfter will reset the millisecondsDelay for this  
456         /// <see cref="T:System.Threading.CancellationTokenSource"/>, if it has not been
457         /// canceled already.
458         /// </para>
459         /// </remarks>
460         public void CancelAfter(Int32 millisecondsDelay)
461         {
462             ThrowIfDisposed();
463
464             if (millisecondsDelay < -1)
465             {
466                 throw new ArgumentOutOfRangeException("millisecondsDelay");
467             }
468
469             if (IsCancellationRequested) return;
470
471             // There is a race condition here as a Cancel could occur between the check of
472             // IsCancellationRequested and the creation of the timer.  This is benign; in the 
473             // worst case, a timer will be created that has no effect when it expires.
474
475             // Also, if Dispose() is called right here (after ThrowIfDisposed(), before timer
476             // creation), it would result in a leaked Timer object (at least until the timer
477             // expired and Disposed itself).  But this would be considered bad behavior, as
478             // Dispose() is not thread-safe and should not be called concurrently with CancelAfter().
479
480             if (m_timer == null)
481             {
482                 // Lazily initialize the timer in a thread-safe fashion.
483                 // Initially set to "never go off" because we don't want to take a
484                 // chance on a timer "losing" the initialization ---- and then
485                 // cancelling the token before it (the timer) can be disposed.
486                 Timer newTimer = new Timer(s_timerCallback, this, -1, -1);
487                 if (Interlocked.CompareExchange(ref m_timer, newTimer, null) != null)
488                 {
489                     // We lost the ---- to initialize the timer.  Dispose the new timer.
490                     newTimer.Dispose();
491                 }
492             }
493
494             
495             // It is possible that m_timer has already been disposed, so we must do
496             // the following in a try/catch block.
497             try
498             {
499                 m_timer.Change(millisecondsDelay, -1);
500             }
501             catch (ObjectDisposedException)
502             {
503                 // Just eat the exception.  There is no other way to tell that
504                 // the timer has been disposed, and even if there were, there
505                 // would not be a good way to deal with the observe/dispose
506                 // race condition.
507             }
508
509         }
510
511         private static readonly TimerCallback s_timerCallback = new TimerCallback(TimerCallbackLogic);
512
513         // Common logic for a timer delegate
514         private static void TimerCallbackLogic(object obj)
515         {
516             CancellationTokenSource cts = (CancellationTokenSource)obj;
517
518             // Cancel the source; handle a race condition with cts.Dispose()
519             if (!cts.IsDisposed)
520             {
521                 // There is a small window for a race condition where a cts.Dispose can sneak
522                 // in right here.  I'll wrap the cts.Cancel() in a try/catch to proof us
523                 // against this ----.
524                 try
525                 {
526                     cts.Cancel(); // will take care of disposing of m_timer
527                 }
528                 catch (ObjectDisposedException)
529                 {
530                     // If the ODE was not due to the target cts being disposed, then propagate the ODE.
531                     if (!cts.IsDisposed) throw;
532                 }
533             }
534         }
535
536         /// <summary>
537         /// Releases the resources used by this <see cref="T:System.Threading.CancellationTokenSource" />.
538         /// </summary>
539         /// <remarks>
540         /// This method is not thread-safe for any other concurrent calls.
541         /// </remarks>
542         public void Dispose()
543         {
544             Dispose(true);
545             GC.SuppressFinalize(this);
546         }
547
548         /// <summary>
549         /// Releases the unmanaged resources used by the <see cref="T:System.Threading.CancellationTokenSource" /> class and optionally releases the managed resources.
550         /// </summary>
551         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
552         protected virtual void Dispose(bool disposing)
553         {
554             // There is nothing to do if disposing=false because the CancellationTokenSource holds no unmanaged resources.
555
556             if (disposing)
557             {
558                 //NOTE: We specifically tolerate that a callback can be deregistered
559                 //      after the CTS has been disposed and/or concurrently with cts.Dispose().
560                 //      This is safe without locks because the reg.Dispose() only
561                 //      mutates a sparseArrayFragment and then reads from properties of the CTS that are not
562                 //      invalidated by cts.Dispose().
563                 //     
564                 //      We also tolerate that a callback can be registered after the CTS has been
565                 //      disposed.  This is safe without locks because InternalRegister is tolerant
566                 //      of m_registeredCallbacksLists becoming null during its execution.  However,
567                 //      we run the acceptable risk of m_registeredCallbacksLists getting reinitialized
568                 //      to non-null if there is a ---- between Dispose and Register, in which case this
569                 //      instance may unnecessarily hold onto a registered callback.  But that's no worse
570                 //      than if Dispose wasn't safe to use concurrently, as Dispose would never be called,
571                 //      and thus no handlers would be dropped.
572
573                 if (m_disposed)
574                     return;
575
576                 if (m_timer != null) m_timer.Dispose();
577
578                 var linkingRegistrations = m_linkingRegistrations;
579                 if (linkingRegistrations != null)
580                 {
581                     m_linkingRegistrations = null; // free for GC once we're done enumerating
582                     for (int i = 0; i < linkingRegistrations.Length; i++)
583                     {
584                         linkingRegistrations[i].Dispose();
585                     }
586                 }
587
588                 // registered callbacks are now either complete or will never run, due to guarantees made by ctr.Dispose()
589                 // so we can now perform main disposal work without risk of linking callbacks trying to use this CTS.
590
591                 m_registeredCallbacksLists = null; // free for GC.
592
593 #if MONO
594                 //
595                 // .NET version has a race on m_kernelEvent which it's not easy to
596                 // trigger on .net probably due to much faster Close implementation
597                 // but on Mono this can happen quite easily.
598                 //
599                 // First race was between Dispose and NotifyCancellation where m_kernelEvent
600                 // can be nulled/Closed and Set at same time.
601                 //
602                 // Second race was between concurrent Dispose calls.
603                 //
604                 // Third race is between Dispose and WaitHandle propery but that should
605                 // be handled by user.
606                 //
607                 var ke = m_kernelEvent;
608                 if (ke != null)
609                 {
610                     m_kernelEvent = null;
611                     ke.Close();
612                 }
613 #else
614                 if (m_kernelEvent != null)
615                 {
616                     m_kernelEvent.Close(); // the critical cleanup to release an OS handle
617                     m_kernelEvent = null; // free for GC.
618                 }
619 #endif
620
621                 m_disposed = true;
622             }
623         }
624
625         // -- Internal methods.
626
627         /// <summary>
628         /// Throws an exception if the source has been disposed.
629         /// </summary>
630         internal void ThrowIfDisposed()
631         {
632             if (m_disposed)
633                 ThrowObjectDisposedException();
634         }
635
636         // separation enables inlining of ThrowIfDisposed
637         private static void ThrowObjectDisposedException()
638         {
639             throw new ObjectDisposedException(null, Environment.GetResourceString("CancellationTokenSource_Disposed"));
640         }
641
642         /// <summary>
643         /// InternalGetStaticSource()
644         /// </summary>
645         /// <param name="set">Whether the source should be set.</param>
646         /// <returns>A static source to be shared among multiple tokens.</returns>
647         internal static CancellationTokenSource InternalGetStaticSource(bool set)
648         {
649             return set ? _staticSource_Set : _staticSource_NotCancelable;
650         }
651
652         /// <summary>
653         /// Registers a callback object. If cancellation has already occurred, the
654         /// callback will have been run by the time this method returns.
655         /// </summary>
656         internal CancellationTokenRegistration InternalRegister(
657             Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext executionContext)
658         {
659             if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
660             {
661                 ThrowIfDisposed();
662             }
663
664             // the CancellationToken has already checked that the token is cancelable before calling this method.
665             Contract.Assert(CanBeCanceled, "Cannot register for uncancelable token src");
666
667             // if not canceled, register the event handlers
668             // if canceled already, run the callback synchronously
669             // Apart from the semantics of late-enlistment, this also ensures that during ExecuteCallbackHandlers() there
670             // will be no mutation of the _registeredCallbacks list
671
672             if (!IsCancellationRequested)
673             {
674                 // In order to enable code to not leak too many handlers, we allow Dispose to be called concurrently
675                 // with Register.  While this is not a recommended practice, consumers can and do use it this way.
676                 // We don't make any guarantees about whether the CTS will hold onto the supplied callback
677                 // if the CTS has already been disposed when the callback is registered, but we try not to
678                 // while at the same time not paying any non-negligible overhead.  The simple compromise
679                 // is to check whether we're disposed (not volatile), and if we see we are, to return an empty
680                 // registration, just as if CanBeCanceled was false for the check made in CancellationToken.Register.
681                 // If there's a ---- and m_disposed is false even though it's been disposed, or if the disposal request
682                 // comes in after this line, we simply run the minor risk of having m_registeredCallbacksLists reinitialized
683                 // (after it was cleared to null during Dispose).
684
685                 if (m_disposed && !AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
686                     return new CancellationTokenRegistration();
687
688                 int myIndex = Thread.CurrentThread.ManagedThreadId % s_nLists;
689
690                 CancellationCallbackInfo callbackInfo = new CancellationCallbackInfo(callback, stateForCallback, targetSyncContext, executionContext, this);
691
692                 //allocate the callback list array
693                 var registeredCallbacksLists = m_registeredCallbacksLists;
694                 if (registeredCallbacksLists == null)
695                 {
696                     SparselyPopulatedArray<CancellationCallbackInfo>[] list = new SparselyPopulatedArray<CancellationCallbackInfo>[s_nLists];
697                     registeredCallbacksLists = Interlocked.CompareExchange(ref m_registeredCallbacksLists, list, null);
698                     if (registeredCallbacksLists == null) registeredCallbacksLists = list;
699                 }
700
701                 //allocate the actual lists on-demand to save mem in low-use situations, and to avoid false-sharing.
702                 var callbacks = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref registeredCallbacksLists[myIndex]);
703                 if (callbacks == null)
704                 {
705                     SparselyPopulatedArray<CancellationCallbackInfo> callBackArray = new SparselyPopulatedArray<CancellationCallbackInfo>(4);
706                     Interlocked.CompareExchange(ref (registeredCallbacksLists[myIndex]), callBackArray, null);
707                     callbacks = registeredCallbacksLists[myIndex];
708                 }
709
710                 // Now add the registration to the list.
711                 SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> addInfo = callbacks.Add(callbackInfo);
712                 CancellationTokenRegistration registration = new CancellationTokenRegistration(callbackInfo, addInfo);
713
714                 if (!IsCancellationRequested)
715                     return registration;
716
717                 // If a cancellation has since come in, we will try to undo the registration and run the callback ourselves.
718                 // (this avoids leaving the callback orphaned)
719                 bool deregisterOccurred = registration.TryDeregister();
720
721                 if (!deregisterOccurred)
722                 {
723                     // The thread that is running Cancel() snagged our callback for execution.
724                     // So we don't need to run it, but we do return the registration so that 
725                     // ctr.Dispose() will wait for callback completion.
726                     return registration;
727                 }
728             }
729
730             // If cancellation already occurred, we run the callback on this thread and return an empty registration.
731             callback(stateForCallback);
732             return new CancellationTokenRegistration();
733         }
734
735         /// <summary>
736         /// 
737         /// </summary>
738         private void NotifyCancellation(bool throwOnFirstException)
739         {
740             // fast-path test to check if Notify has been called previously
741             if (IsCancellationRequested)
742                 return;
743
744             // If we're the first to signal cancellation, do the main extra work.
745             if (Interlocked.CompareExchange(ref m_state, NOTIFYING, NOT_CANCELED) == NOT_CANCELED)
746             {
747                 // Dispose of the timer, if any
748                 Timer timer = m_timer;
749                 if(timer != null) timer.Dispose();
750
751                 //record the threadID being used for running the callbacks.
752                 ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
753                 
754                 //If the kernel event is null at this point, it will be set during lazy construction.
755 #if MONO
756                 var ke = m_kernelEvent;
757                 if (ke != null) {
758                     try {
759                         ke.Set(); // update the MRE value.
760                     } catch (ObjectDisposedException) {
761                         //
762                         // Rethrow only when we are not in race with Dispose
763                         //
764                         if (m_kernelEvent != null)
765                             throw;
766                     }
767                 }
768 #else
769                 if (m_kernelEvent != null)
770                     m_kernelEvent.Set(); // update the MRE value.
771 #endif
772
773                 // - late enlisters to the Canceled event will have their callbacks called immediately in the Register() methods.
774                 // - Callbacks are not called inside a lock.
775                 // - After transition, no more delegates will be added to the 
776                 // - list of handlers, and hence it can be consumed and cleared at leisure by ExecuteCallbackHandlers.
777                 ExecuteCallbackHandlers(throwOnFirstException);
778                 Contract.Assert(IsCancellationCompleted, "Expected cancellation to have finished");
779             }
780         }
781
782         /// <summary>
783         /// Invoke the Canceled event.
784         /// </summary>
785         /// <remarks>
786         /// The handlers are invoked synchronously in LIFO order.
787         /// </remarks>
788         private void ExecuteCallbackHandlers(bool throwOnFirstException)
789         {
790             Contract.Assert(IsCancellationRequested, "ExecuteCallbackHandlers should only be called after setting IsCancellationRequested->true");
791             Contract.Assert(ThreadIDExecutingCallbacks != -1, "ThreadIDExecutingCallbacks should have been set.");
792
793             // Design decision: call the delegates in LIFO order so that callbacks fire 'deepest first'.
794             // This is intended to help with nesting scenarios so that child enlisters cancel before their parents.
795             List<Exception> exceptionList = null;
796             SparselyPopulatedArray<CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists;
797
798             // If there are no callbacks to run, we can safely exit.  Any ----s to lazy initialize it
799             // will see IsCancellationRequested and will then run the callback themselves.
800             if (callbackLists == null)
801             {
802                 Interlocked.Exchange(ref m_state, NOTIFYINGCOMPLETE);
803                 return;
804             }
805             
806             try
807             {
808                 for (int index = 0; index < callbackLists.Length; index++)
809                 {
810                     SparselyPopulatedArray<CancellationCallbackInfo> list = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref callbackLists[index]);
811                     if (list != null)
812                     {
813                         SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment = list.Tail;
814
815                         while (currArrayFragment != null)
816                         {
817                             for (int i = currArrayFragment.Length - 1; i >= 0; i--)
818                             {
819                                 // 1a. publish the indended callback, to ensure ctr.Dipose can tell if a wait is necessary.
820                                 // 1b. transition to the target syncContext and continue there..
821                                 //  On the target SyncContext.
822                                 //   2. actually remove the callback
823                                 //   3. execute the callback
824                                 // re:#2 we do the remove on the syncCtx so that we can be sure we have control of the syncCtx before
825                                 //        grabbing the callback.  This prevents a deadlock if ctr.Dispose() might run on the syncCtx too.
826                                 m_executingCallback = currArrayFragment[i];
827                                 if (m_executingCallback != null)
828                                 {
829                                     //Transition to the target [....] context (if necessary), and continue our work there.
830                                     CancellationCallbackCoreWorkArguments args = new CancellationCallbackCoreWorkArguments(currArrayFragment, i);
831
832                                     // marshal exceptions: either aggregate or perform an immediate rethrow
833                                     // We assume that syncCtx.Send() has forwarded on user exceptions when appropriate.
834                                     try
835                                     {
836                                         if (m_executingCallback.TargetSyncContext != null)
837                                         {
838
839                                             m_executingCallback.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, args);
840                                             // CancellationCallbackCoreWork_OnSyncContext may have altered ThreadIDExecutingCallbacks, so reset it. 
841                                             ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
842                                         }
843                                         else
844                                         {
845                                             CancellationCallbackCoreWork(args);
846                                         }
847                                     }
848                                     catch(Exception ex)
849                                     {
850                                         if (throwOnFirstException)
851                                             throw;
852     
853                                         // Otherwise, log it and proceed.
854                                         if(exceptionList == null)
855                                             exceptionList = new List<Exception>();
856                                         exceptionList.Add(ex);
857                                     }
858                                 }
859                             }
860
861                             currArrayFragment = currArrayFragment.Prev;
862                         }
863                     }
864                 }
865             }
866             finally
867             {
868                 m_state = NOTIFYINGCOMPLETE;
869                 m_executingCallback = null;
870                 Thread.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state.
871             }
872
873             if (exceptionList != null)
874             {
875                 Contract.Assert(exceptionList.Count > 0, "Expected exception count > 0");
876                 throw new AggregateException(exceptionList);
877             }
878         }
879
880         // The main callback work that executes on the target synchronization context
881         private void CancellationCallbackCoreWork_OnSyncContext(object obj)
882         {
883             CancellationCallbackCoreWork((CancellationCallbackCoreWorkArguments)obj);
884         }
885
886         private void CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args)
887         {
888             // remove the intended callback..and ensure that it worked.
889             // otherwise the callback has disappeared in the interim and we can immediately return.
890             CancellationCallbackInfo callback = args.m_currArrayFragment.SafeAtomicRemove(args.m_currArrayIndex, m_executingCallback);
891             if (callback == m_executingCallback)
892             {
893                 if (callback.TargetExecutionContext != null)
894                 {
895                     // we are running via a custom [....] context, so update the executing threadID
896                     callback.CancellationTokenSource.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
897                 }
898                 callback.ExecuteCallback();
899             }
900         }
901
902
903         /// <summary>
904         /// Creates a <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that will be in the canceled state
905         /// when any of the source tokens are in the canceled state.
906         /// </summary>
907         /// <param name="token1">The first <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to observe.</param>
908         /// <param name="token2">The second <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to observe.</param>
909         /// <returns>A <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that is linked 
910         /// to the source tokens.</returns>
911         public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2)
912         {
913             CancellationTokenSource linkedTokenSource = new CancellationTokenSource();
914
915             bool token2CanBeCanceled = token2.CanBeCanceled;
916
917             if( token1.CanBeCanceled )
918             {
919                 linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[token2CanBeCanceled ? 2 : 1]; // there will be at least 1 and at most 2 linkings
920                 linkedTokenSource.m_linkingRegistrations[0] = token1.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
921             }
922             
923             if( token2CanBeCanceled )
924             {
925                 int index = 1;
926                 if( linkedTokenSource.m_linkingRegistrations == null )
927                 {
928                     linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[1]; // this will be the only linking
929                     index = 0;
930                 }
931                 linkedTokenSource.m_linkingRegistrations[index] = token2.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
932             }
933             
934             return linkedTokenSource;
935         }
936
937         /// <summary>
938         /// Creates a <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that will be in the canceled state
939         /// when any of the source tokens are in the canceled state.
940         /// </summary>
941         /// <param name="tokens">The <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances to observe.</param>
942         /// <returns>A <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> that is linked 
943         /// to the source tokens.</returns>
944         /// <exception cref="T:System.ArgumentNullException"><paramref name="tokens"/> is null.</exception>
945         public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens)
946         {
947             if (tokens == null)
948                 throw new ArgumentNullException("tokens");
949
950             if (tokens.Length == 0)
951                 throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty"));
952
953             // a defensive copy is not required as the array has value-items that have only a single IntPtr field,
954             // hence each item cannot be null itself, and reads of the payloads cannot be torn.
955             Contract.EndContractBlock();
956
957             CancellationTokenSource linkedTokenSource = new CancellationTokenSource();
958             linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length];
959
960             for (int i = 0; i < tokens.Length; i++)
961             {
962                 if (tokens[i].CanBeCanceled)
963                 {
964                     linkedTokenSource.m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
965                 }
966                 // Empty slots in the array will be default(CancellationTokenRegistration), which are nops to Dispose.
967                 // Based on usage patterns, such occurrences should also be rare, such that it's not worth resizing
968                 // the array and incurring the related costs.
969             }
970
971             return linkedTokenSource;
972         }
973
974
975         // Wait for a single callback to complete (or, more specifically, to not be running).
976         // It is ok to call this method if the callback has already finished.
977         // Calling this method before the target callback has been selected for execution would be an error.
978         internal void WaitForCallbackToComplete(CancellationCallbackInfo callbackInfo)
979         {
980             SpinWait sw = new SpinWait();
981             while (ExecutingCallback == callbackInfo)
982             {
983                 sw.SpinOnce();  //spin as we assume callback execution is fast and that this situation is rare.
984             }
985         }
986     }
987
988     // ----------------------------------------------------------
989     // -- CancellationCallbackCoreWorkArguments --
990     // ----------------------------------------------------------
991     // Helper struct for passing data to the target [....] context
992     internal struct CancellationCallbackCoreWorkArguments
993     {
994         internal SparselyPopulatedArrayFragment<CancellationCallbackInfo> m_currArrayFragment;
995         internal int m_currArrayIndex;
996         
997         public CancellationCallbackCoreWorkArguments(SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment, int currArrayIndex)
998         {
999             m_currArrayFragment = currArrayFragment;
1000             m_currArrayIndex = currArrayIndex;
1001         }
1002     }
1003
1004     // ----------------------------------------------------------
1005     // -- CancellationCallbackInfo --
1006     // ----------------------------------------------------------
1007
1008     /// <summary>
1009     /// A helper class for collating the various bits of information required to execute 
1010     /// cancellation callbacks.
1011     /// </summary>
1012     internal class CancellationCallbackInfo
1013     {
1014         internal readonly Action<object> Callback;
1015         internal readonly object StateForCallback;
1016         internal readonly SynchronizationContext TargetSyncContext;
1017         internal readonly ExecutionContext TargetExecutionContext;
1018         internal readonly CancellationTokenSource CancellationTokenSource;
1019
1020         internal CancellationCallbackInfo(
1021             Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext targetExecutionContext,
1022             CancellationTokenSource cancellationTokenSource)
1023         {
1024             Callback = callback;
1025             StateForCallback = stateForCallback;
1026             TargetSyncContext = targetSyncContext;
1027             TargetExecutionContext = targetExecutionContext;
1028             CancellationTokenSource = cancellationTokenSource;
1029         }
1030
1031         // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical
1032         [SecurityCritical]
1033         private static ContextCallback s_executionContextCallback;
1034
1035         /// <summary>
1036         /// InternalExecuteCallbackSynchronously_GeneralPath
1037         /// This will be called on the target synchronization context, however, we still need to restore the required execution context
1038         /// </summary>
1039         [SecuritySafeCritical]
1040         internal void ExecuteCallback()
1041         {
1042             if (TargetExecutionContext != null)
1043             {
1044                 // Lazily initialize the callback delegate; benign ----
1045                 var callback = s_executionContextCallback;
1046                 if (callback == null) s_executionContextCallback = callback = new ContextCallback(ExecutionContextCallback);
1047                 
1048                 ExecutionContext.Run(
1049                     TargetExecutionContext,
1050                     callback,
1051                     this);
1052             }
1053             else
1054             {
1055                 //otherwise run directly
1056                 ExecutionContextCallback(this);
1057             }
1058         }
1059
1060         // the worker method to actually run the callback
1061         // The signature is such that it can be used as a 'ContextCallback'
1062         [SecurityCritical]
1063         private static void ExecutionContextCallback(object obj)
1064         {
1065             CancellationCallbackInfo callbackInfo = obj as CancellationCallbackInfo;
1066             Contract.Assert(callbackInfo != null);
1067             callbackInfo.Callback(callbackInfo.StateForCallback);
1068         }
1069     }
1070
1071
1072     // ----------------------------------------------------------
1073     // -- SparselyPopulatedArray --
1074     // ----------------------------------------------------------
1075
1076     /// <summary>
1077     /// A sparsely populated array.  Elements can be sparse and some null, but this allows for
1078     /// lock-free additions and growth, and also for constant time removal (by nulling out).
1079     /// </summary>
1080     /// <typeparam name="T">The kind of elements contained within.</typeparam>
1081     internal class SparselyPopulatedArray<T> where T : class
1082     {
1083         private readonly SparselyPopulatedArrayFragment<T> m_head;
1084         private volatile SparselyPopulatedArrayFragment<T> m_tail;
1085
1086         /// <summary>
1087         /// Allocates a new array with the given initial size.
1088         /// </summary>
1089         /// <param name="initialSize">How many array slots to pre-allocate.</param>
1090         internal SparselyPopulatedArray(int initialSize)
1091         {
1092             m_head = m_tail = new SparselyPopulatedArrayFragment<T>(initialSize);
1093         }
1094
1095 #if DEBUG
1096         // Used in DEBUG mode by CancellationTokenSource.CallbackCount
1097         /// <summary>
1098         /// The head of the doubly linked list.
1099         /// </summary>
1100         internal SparselyPopulatedArrayFragment<T> Head
1101         {
1102             get { return m_head; }
1103         }
1104 #endif
1105
1106         /// <summary>
1107         /// The tail of the doubly linked list.
1108         /// </summary>
1109         internal SparselyPopulatedArrayFragment<T> Tail
1110         {
1111             get { return m_tail; }
1112         }
1113
1114         /// <summary>
1115         /// Adds an element in the first available slot, beginning the search from the tail-to-head.
1116         /// If no slots are available, the array is grown.  The method doesn't return until successful.
1117         /// </summary>
1118         /// <param name="element">The element to add.</param>
1119         /// <returns>Information about where the add happened, to enable O(1) deregistration.</returns>
1120         internal SparselyPopulatedArrayAddInfo<T> Add(T element)
1121         {
1122             while (true)
1123             {
1124                 // Get the tail, and ensure it's up to date.
1125                 SparselyPopulatedArrayFragment<T> tail = m_tail;
1126                 while (tail.m_next != null)
1127                     m_tail = (tail = tail.m_next);
1128
1129                 // Search for a free index, starting from the tail.
1130                 SparselyPopulatedArrayFragment<T> curr = tail;
1131                 while (curr != null)
1132                 {
1133                     const int RE_SEARCH_THRESHOLD = -10; // Every 10 skips, force a search.
1134                     if (curr.m_freeCount < 1)
1135                         --curr.m_freeCount;
1136
1137                     if (curr.m_freeCount > 0 || curr.m_freeCount < RE_SEARCH_THRESHOLD)
1138                     {
1139                         int c = curr.Length;
1140
1141                         // We'll compute a start offset based on how many free slots we think there
1142                         // are.  This optimizes for ordinary the LIFO deregistration pattern, and is
1143                         // far from perfect due to the non-threadsafe ++ and -- of the free counter.
1144                         int start = ((c - curr.m_freeCount) % c);
1145                         if (start < 0)
1146                         {
1147                             start = 0;
1148                             curr.m_freeCount--; // Too many free elements; fix up.
1149                         }
1150                         Contract.Assert(start >= 0 && start < c, "start is outside of bounds");
1151
1152                         // Now walk the array until we find a free slot (or reach the end).
1153                         for (int i = 0; i < c; i++)
1154                         {
1155                             // If the slot is null, try to CAS our element into it.
1156                             int tryIndex = (start + i) % c;
1157                             Contract.Assert(tryIndex >= 0 && tryIndex < curr.m_elements.Length, "tryIndex is outside of bounds");
1158                             
1159                             if (curr.m_elements[tryIndex] == null && Interlocked.CompareExchange(ref curr.m_elements[tryIndex], element, null) == null)
1160                             {
1161                                 // We adjust the free count by --. Note: if this drops to 0, we will skip
1162                                 // the fragment on the next search iteration.  Searching threads will -- the
1163                                 // count and force a search every so often, just in case fragmentation occurs.
1164                                 int newFreeCount = curr.m_freeCount - 1;
1165                                 curr.m_freeCount = newFreeCount > 0 ? newFreeCount : 0;
1166                                 return new SparselyPopulatedArrayAddInfo<T>(curr, tryIndex);
1167                             }
1168                         }
1169                     }
1170
1171                     curr = curr.m_prev;
1172                 }
1173
1174                 // If we got here, we need to add a new chunk to the tail and try again.
1175                 SparselyPopulatedArrayFragment<T> newTail = new SparselyPopulatedArrayFragment<T>(
1176                     tail.m_elements.Length == 4096 ? 4096 : tail.m_elements.Length * 2, tail);
1177                 if (Interlocked.CompareExchange(ref tail.m_next, newTail, null) == null)
1178                 {
1179                     m_tail = newTail;
1180                 }
1181             }
1182         }
1183     }
1184
1185     /// <summary>
1186     /// A struct to hold a link to the exact spot in an array an element was inserted, enabling
1187     /// constant time removal later on.
1188     /// </summary>
1189     internal struct SparselyPopulatedArrayAddInfo<T> where T : class
1190     {
1191         private SparselyPopulatedArrayFragment<T> m_source;
1192         private int m_index;
1193
1194         internal SparselyPopulatedArrayAddInfo(SparselyPopulatedArrayFragment<T> source, int index)
1195         {
1196             Contract.Assert(source != null);
1197             Contract.Assert(index >= 0 && index < source.Length);
1198             m_source = source;
1199             m_index = index;
1200         }
1201
1202         internal SparselyPopulatedArrayFragment<T> Source
1203         {
1204             get { return m_source; }
1205         }
1206
1207         internal int Index
1208         {
1209             get { return m_index; }
1210         }
1211     }
1212
1213     /// <summary>
1214     /// A fragment of a sparsely populated array, doubly linked.
1215     /// </summary>
1216     /// <typeparam name="T">The kind of elements contained within.</typeparam>
1217     internal class SparselyPopulatedArrayFragment<T> where T : class
1218     {
1219         internal readonly T[] m_elements; // The contents, sparsely populated (with nulls).
1220         internal volatile int m_freeCount; // A hint of the number of free elements.
1221         internal volatile SparselyPopulatedArrayFragment<T> m_next; // The next fragment in the chain.
1222         internal volatile SparselyPopulatedArrayFragment<T> m_prev; // The previous fragment in the chain.
1223
1224         internal SparselyPopulatedArrayFragment(int size) : this(size, null)
1225         {
1226         }
1227
1228         internal SparselyPopulatedArrayFragment(int size, SparselyPopulatedArrayFragment<T> prev)
1229         {
1230             m_elements = new T[size];
1231             m_freeCount = size;
1232             m_prev = prev;
1233         }
1234
1235         internal T this[int index]
1236         {
1237             get { return Volatile.Read<T>(ref m_elements[index]); }
1238         }
1239
1240         internal int Length
1241         {
1242             get { return m_elements.Length; }
1243         }
1244
1245 #if DEBUG
1246         // Used in DEBUG mode by CancellationTokenSource.CallbackCount
1247         internal SparselyPopulatedArrayFragment<T> Next
1248         {
1249             get { return m_next; }
1250         }
1251 #endif
1252         internal SparselyPopulatedArrayFragment<T> Prev
1253         {
1254             get { return m_prev; }
1255         }
1256
1257         // only removes the item at the specified index if it is still the expected one.
1258         // Returns the prevailing value.
1259         // The remove occured successfully if the return value == expected element
1260         // otherwise the remove did not occur.
1261         internal T SafeAtomicRemove(int index, T expectedElement)
1262         {
1263             T prevailingValue = Interlocked.CompareExchange(ref m_elements[index], null, expectedElement);
1264             if (prevailingValue != null) 
1265                 ++m_freeCount;
1266             return prevailingValue;
1267         }
1268     }
1269 }