Fix dispose race condition in CancellationTokenSource
authorLudovic Henry <ludovic@xamarin.com>
Tue, 29 Sep 2015 11:46:31 +0000 (12:46 +0100)
committerMarek Safar <marek.safar@gmail.com>
Mon, 2 May 2016 22:10:26 +0000 (00:10 +0200)
commitc251136278fcbcc63aae52c73fbd5328b2f878f5
tree08243e1cd4e4e7c1af4b34a13fcc0de7adcae6b5
parent9e9868aa2ad3b5d5af4fb6192effa35e2fed0b98
Fix dispose race condition in CancellationTokenSource

The race condition would manifest in the following code:

```
for (int i = 0, total = 100000; i < total; ++i) {
if (i % 50 == 0)
Console.WriteLine ("{0}/{1}", i, total);

var c1 = new CancellationTokenSource ();
var wh = c1.Token.WaitHandle;
c1.CancelAfter (1);
Thread.Sleep (1);
c1.Dispose ();
}
```

And we would observe the following exception:
```
Unhandled Exception: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Threading.ManualResetEvent'.
 at System.Threading.WaitHandle.CheckDisposed () [0x00016] in /Users/builder/data/lanes/1196/33e00ac6/source/mono/mcs/class/corlib/System.Threading/WaitHandle.cs:426
 at System.Threading.EventWaitHandle.Set () [0x0000c] in /Users/builder/data/lanes/1196/33e00ac6/source/mono/mcs/class/corlib/System.Threading/EventWaitHandle.cs:205
 at (wrapper remoting-invoke-with-check) System.Threading.EventWaitHandle:Set ()
 at System.Threading.CancellationTokenSource.NotifyCancellation (Boolean throwOnFirstException) [0x00051] in /Users/builder/data/lanes/1196/33e00ac6/source/mono/external/referencesource/mscorlib/system/threading/CancellationTokenSource.cs:723
 at System.Threading.CancellationTokenSource.Cancel (Boolean throwOnFirstException) [0x00006] in /Users/builder/data/lanes/1196/33e00ac6/source/mono/external/referencesource/mscorlib/system/threading/CancellationTokenSource.cs:409
 at System.Threading.CancellationTokenSource.Cancel () <0x7d75ada8 + 0x00017> in <filename unknown>:0
 at System.Threading.CancellationTokenSource.TimerCallbackLogic (System.Object obj) [0x00012] in /Users/builder/data/lanes/1196/33e00ac6/source/mono/external/referencesource/mscorlib/system/threading/CancellationTokenSource.cs:538
```

This would be a race condition between the TimerCallbackLogic call to Cancel and the call to Dispose on another thread. You could trigger it more reliably by putting a Thread.Sleep at CancellationTokenSource.cs:599.
mcs/class/referencesource/mscorlib/system/threading/CancellationTokenSource.cs