[corlib] Linked token callback to the proxy CancellationTokenSource shouldn't throw...
authorJérémie Laval <jeremie.laval@gmail.com>
Mon, 23 Jul 2012 11:48:40 +0000 (12:48 +0100)
committerJérémie Laval <jeremie.laval@gmail.com>
Mon, 23 Jul 2012 11:48:40 +0000 (12:48 +0100)
mcs/class/corlib/System.Threading/CancellationTokenSource.cs
mcs/class/corlib/Test/System.Threading/CancellationTokenSourceTest.cs

index b6668313257f441cc1d86ad94bf7345c49ca1c5f..ac1461110eafe44c50e4aa75880032204ed33d25 100644 (file)
@@ -158,6 +158,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)
                {
@@ -199,7 +210,7 @@ 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) {
index e8a6e3a15d5a4b28f015bc54ae0dc772f2e42e4b..e33802c431a8982be874c7f70f3a09a2c5d866e7 100644 (file)
@@ -33,6 +33,7 @@ using System;
 using System.Threading;
 using NUnit.Framework;
 using System.Threading.Tasks;
+using MonoTests.System.Threading.Tasks;
 
 namespace MonoTests.System.Threading
 {
@@ -413,6 +414,32 @@ namespace MonoTests.System.Threading
 
                        Assert.IsTrue (canceled, "#3");
                }
+
+               [Test]
+               public void ConcurrentCancelLinkedTokenSourceWhileDisposing ()
+               {
+                       ParallelTestHelper.Repeat (delegate {
+                               var src = new CancellationTokenSource ();
+                               var linked = CancellationTokenSource.CreateLinkedTokenSource (src.Token);
+                               var cntd = new CountdownEvent (2);
+
+                               var t1 = new Thread (() => {
+                                       if (!cntd.Signal ())
+                                               cntd.Wait ();
+                                       src.Cancel ();
+                               });
+                               var t2 = new Thread (() => {
+                                       if (!cntd.Signal ())
+                                               cntd.Wait ();
+                                       linked.Dispose ();
+                               });
+
+                               t1.Start ();
+                               t2.Start ();
+                               t1.Join (500);
+                               t2.Join (500);
+                               }, 500);
+               }
        }
 }