Switch from boolean to count mode to safely detect disposability in ManualResetEventSlim.
authorJeremie Laval <jeremie.laval@gmail.com>
Thu, 5 Apr 2012 11:16:49 +0000 (12:16 +0100)
committerJeremie Laval <jeremie.laval@gmail.com>
Tue, 10 Apr 2012 11:39:02 +0000 (12:39 +0100)
Having two concurrent operations of Set/Reset who succeeded in fetching the handle before it was disabled all the while trying to dispose may have caused a early release of the handle.

mcs/class/corlib/System.Threading/ManualResetEventSlim.cs

index e6bf04ab9208caf37eeab25c234734d8f5e44101..b1007515b311c3401c78b4bd9be089cc331a9e6f 100644 (file)
@@ -37,7 +37,7 @@ namespace System.Threading
 
                ManualResetEvent handle;
                internal AtomicBooleanValue disposed;
-               bool used;
+               int used;
                bool set;
 
                public ManualResetEventSlim ()
@@ -78,13 +78,13 @@ namespace System.Threading
                        set = false;
                        Thread.MemoryBarrier ();
                        if (handle != null) {
-                               used = true;
+                               Interlocked.Increment (ref used);
                                Thread.MemoryBarrier ();
                                var tmpHandle = handle;
                                if (tmpHandle != null)
                                        tmpHandle.Reset ();
                                Thread.MemoryBarrier ();
-                               used = false;
+                               Interlocked.Decrement (ref used);
                        }
                }
 
@@ -93,13 +93,13 @@ namespace System.Threading
                        set = true;
                        Thread.MemoryBarrier ();
                        if (handle != null) {
-                               used = true;
+                               Interlocked.Increment (ref used);
                                Thread.MemoryBarrier ();
                                var tmpHandle = handle;
                                if (tmpHandle != null)
                                        tmpHandle.Set ();
                                Thread.MemoryBarrier ();
-                               used = false;
+                               Interlocked.Decrement (ref used);
                        }
                }
 
@@ -211,10 +211,10 @@ namespace System.Threading
 
                        if (handle != null) {
                                var tmpHandle = Interlocked.Exchange (ref handle, null);
-                               if (used) {
+                               if (used > 0) {
                                        // A tiny wait (just a few cycles normally) before releasing
                                        SpinWait wait = new SpinWait ();
-                                       while (used)
+                                       while (used > 0)
                                                wait.SpinOnce ();
                                }
                                tmpHandle.Dispose ();