Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / corlib / System.Threading / CancellationTokenSource.cs
index b82e49f59bfb751b810a4406af15003b593f7152..c5557f0446e69b7808e73a295b08c7c244d4e469 100644 (file)
@@ -61,7 +61,7 @@ namespace System.Threading
 #if NET_4_5
                        timer_callback = token => {
                                var cts = (CancellationTokenSource) token;
-                               cts.Cancel ();
+                               cts.CancelSafe ();
                        };
 #endif
                }
@@ -118,23 +118,46 @@ namespace System.Threading
                public void Cancel (bool throwOnFirstException)
                {
                        CheckDisposed ();
+                       Cancellation (throwOnFirstException);
+               }
+
+               //
+               // Don't throw ObjectDisposedException if the callback
+               // is called concurrently with a Dispose
+               //
+               public void CancelSafe ()
+               {
+                       if (!disposed)
+                               Cancellation (true);
+               }
 
+               void Cancellation (bool throwOnFirstException)
+               {
                        if (canceled)
                                return;
 
                        Thread.MemoryBarrier ();
                        canceled = true;
-                       
-                       handle.Set ();
+
+                       Thread.MemoryBarrier ();
+
+                       // Dispose might be running at same time
+                       if (!disposed)
+                               handle.Set ();
+
                        if (linkedTokens != null)
                                UnregisterLinkedTokens ();
-                       
+
+                       var cbs = callbacks;
+                       if (cbs == null)
+                               return;
+
                        List<Exception> exceptions = null;
-                       
+
                        try {
                                Action cb;
-                               for (int id = int.MinValue + 1; id <= currId; id++) {
-                                       if (!callbacks.TryRemove (new CancellationTokenRegistration (id, this), out cb))
+                               for (int id = currId; id != int.MinValue; id--) {
+                                       if (!cbs.TryRemove (new CancellationTokenRegistration (id, this), out cb))
                                                continue;
                                        if (cb == null)
                                                continue;
@@ -153,24 +176,13 @@ namespace System.Threading
                                        }
                                }
                        } finally {
-                               callbacks.Clear ();
+                               cbs.Clear ();
                        }
 
                        if (exceptions != null)
                                throw new AggregateException (exceptions);
                }
 
-               /* This is the callback registered on linked tokens
-                * so that they don't throw an ODE if the callback
-                * is called concurrently with a Dispose
-                */
-               void SafeLinkedCancel ()
-               {
-                       try {
-                               Cancel ();
-                       } catch (ObjectDisposedException) {}
-               }
-
 #if NET_4_5
                public void CancelAfter (TimeSpan delay)
                {
@@ -212,7 +224,7 @@ namespace System.Threading
                                throw new ArgumentException ("Empty tokens array");
 
                        CancellationTokenSource src = new CancellationTokenSource ();
-                       Action action = src.SafeLinkedCancel;
+                       Action action = src.CancelSafe;
                        var registrations = new List<CancellationTokenRegistration> (tokens.Length);
 
                        foreach (CancellationToken token in tokens) {
@@ -250,18 +262,20 @@ namespace System.Threading
                void Dispose (bool disposing)
                {
                        if (disposing && !disposed) {
-                               Thread.MemoryBarrier ();
                                disposed = true;
+                               Thread.MemoryBarrier ();
 
                                if (!canceled) {
-                                       Thread.MemoryBarrier ();
                                        UnregisterLinkedTokens ();
                                        callbacks = null;
+                               } else {
+                                       handle.WaitOne ();
                                }
 #if NET_4_5
                                if (timer != null)
                                        timer.Dispose ();
 #endif
+
                                handle.Dispose ();
                        }
                }