[System.Core] Fix possible state corruption in ReaderWriterLockSlim. Fix #6635.
authorJérémie Laval <jeremie.laval@gmail.com>
Tue, 4 Sep 2012 17:46:23 +0000 (18:46 +0100)
committerJérémie Laval <jeremie.laval@gmail.com>
Tue, 4 Sep 2012 17:53:18 +0000 (18:53 +0100)
The problem was triggered in a situation where threads were competing for the write-lock and were using timeout:
   - thread t1 takes the lock (state = RwWrite)
   - thread t2 tries to take the lock but fails because of t1, it registers it interest (state = RwWrite | RwWait) and goes to sleep
   - thread t3 tries to take the lock but failes because of t1 and don't register any interest because t2 did it already (state = RwWrite | RwWait)
   - t1 releases the lock, waking up t2 and t3 which race for it with t3 winning the race (state = RwWrite)
   - t2 tries to go back to sleep but has overrun its timeout so it exits.
   - with t2 exiting, it tries to remove previously registered interest (state = RwWrite - RwWait -> corruption).

mcs/class/System.Core/System.Threading/ReaderWriterLockSlim.cs

index 74320c1e42911011fdee896d90bcf9adf1417faa..512f97b27cb15619cd97894d61102b3cb895806d 100644 (file)
@@ -261,6 +261,7 @@ namespace System.Threading {
 
                                int stateCheck = isUpgradable ? RwWaitUpgrade + RwWait : RwWait;
                                long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds;
+                               int registration = isUpgradable ? RwWaitUpgrade : RwWait;
 
                                do {
                                        int state = rwlock;
@@ -268,7 +269,8 @@ namespace System.Threading {
                                        if (state <= stateCheck) {
                                                try {}
                                                finally {
-                                                       if (Interlocked.CompareExchange (ref rwlock, RwWrite, state) == state) {
+                                                       var toWrite = state + RwWrite - (registered ? registration : 0);
+                                                       if (Interlocked.CompareExchange (ref rwlock, toWrite, state) == state) {
                                                                writerDoneEvent.Reset ();
                                                                ctstate.LockState ^= LockState.Write;
                                                                ++ctstate.WriterRecursiveCount;
@@ -452,7 +454,7 @@ namespace System.Threading {
                                return rwlock >= RwRead && CurrentThreadState.LockState.Has (LockState.Read);
                        }
                }
-               
+
                public bool IsWriteLockHeld {
                        get {
                                return (rwlock & RwWrite) > 0 && CurrentThreadState.LockState.Has (LockState.Write);