Merge branch 'master' into msbuilddll2
[mono.git] / mcs / class / corlib / System.Threading / CancellationTokenSource.cs
index 69e6e819a7ca37af492de2b9a897bebce4e0f675..59637dd0a35e212c5685648d6c1c78d8501bc5ed 100644 (file)
@@ -26,7 +26,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-#if NET_4_0 || MOBILE
+#if NET_4_0
 using System.Collections.Generic;
 using System.Collections.Concurrent;
 
@@ -42,6 +42,7 @@ namespace System.Threading
                
                int currId = int.MinValue;
                ConcurrentDictionary<CancellationTokenRegistration, Action> callbacks;
+               CancellationTokenRegistration[] linkedTokens;
 
                ManualResetEvent handle;
                
@@ -125,12 +126,14 @@ namespace System.Threading
                        canceled = true;
                        
                        handle.Set ();
+                       if (linkedTokens != null)
+                               UnregisterLinkedTokens ();
                        
                        List<Exception> exceptions = null;
                        
                        try {
                                Action cb;
-                               for (int id = int.MinValue + 1; id <= currId; id++) {
+                               for (int id = currId; id != int.MinValue; id--) {
                                        if (!callbacks.TryRemove (new CancellationTokenRegistration (id, this), out cb))
                                                continue;
                                        if (cb == null)
@@ -157,6 +160,17 @@ namespace System.Threading
                                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)
                {
@@ -198,12 +212,14 @@ namespace System.Threading
                                throw new ArgumentException ("Empty tokens array");
 
                        CancellationTokenSource src = new CancellationTokenSource ();
-                       Action action = src.Cancel;
+                       Action action = src.SafeLinkedCancel;
+                       var registrations = new List<CancellationTokenRegistration> (tokens.Length);
 
                        foreach (CancellationToken token in tokens) {
                                if (token.CanBeCanceled)
-                                       token.Register (action);
+                                       registrations.Add (token.Register (action));
                        }
+                       src.linkedTokens = registrations.ToArray ();
                        
                        return src;
                }
@@ -239,6 +255,7 @@ namespace System.Threading
 
                                if (!canceled) {
                                        Thread.MemoryBarrier ();
+                                       UnregisterLinkedTokens ();
                                        callbacks = null;
                                }
 #if NET_4_5
@@ -248,6 +265,15 @@ namespace System.Threading
                                handle.Dispose ();
                        }
                }
+
+               void UnregisterLinkedTokens ()
+               {
+                       var registrations = Interlocked.Exchange (ref linkedTokens, null);
+                       if (registrations == null)
+                               return;
+                       foreach (var linked in registrations)
+                               linked.Dispose ();
+               }
                
                internal CancellationTokenRegistration Register (Action callback, bool useSynchronizationContext)
                {