2
0
Эх сурвалжийг харах

Fix ReaderWriterLockSlim Exit methods in SupportsRecursion mode and add corresponding unit tests

Jérémie Laval 15 жил өмнө
parent
commit
4016c03e34

+ 21 - 18
mcs/class/System.Core/System.Threading/ReaderWriterLockSlim.cs

@@ -210,10 +210,11 @@ namespace System.Threading {
 				if (!ctstate.LockState.Has (LockState.Read))
 					throw new SynchronizationLockException ("The current thread has not entered the lock in read mode");
 
-				ctstate.LockState ^= LockState.Read;
-				--ctstate.ReaderRecursiveCount;
-				if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
-					readerDoneEvent.Set ();
+				if (--ctstate.ReaderRecursiveCount == 0) {
+					ctstate.LockState ^= LockState.Read;
+					if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
+						readerDoneEvent.Set ();
+				}
 			}
 		}
 
@@ -325,14 +326,15 @@ namespace System.Threading {
 				if (!ctstate.LockState.Has (LockState.Write))
 					throw new SynchronizationLockException ("The current thread has not entered the lock in write mode");
 			
-				bool isUpgradable = ctstate.LockState.Has (LockState.Upgradable);
-				ctstate.LockState ^= LockState.Write;
-				--ctstate.WriterRecursiveCount;
-
-				int value = Interlocked.Add (ref rwlock, isUpgradable ? RwRead - RwWrite : -RwWrite);
-				writerDoneEvent.Set ();
-				if (isUpgradable && value >> RwReadBit == 1)
-					readerDoneEvent.Reset ();
+				if (--ctstate.WriterRecursiveCount == 0) {
+					bool isUpgradable = ctstate.LockState.Has (LockState.Upgradable);
+					ctstate.LockState ^= LockState.Write;
+
+					int value = Interlocked.Add (ref rwlock, isUpgradable ? RwRead - RwWrite : -RwWrite);
+					writerDoneEvent.Set ();
+					if (isUpgradable && value >> RwReadBit == 1)
+						readerDoneEvent.Reset ();
+				}
 			}
 		}
 
@@ -404,13 +406,14 @@ namespace System.Threading {
 				if (!ctstate.LockState.Has (LockState.Upgradable | LockState.Read))
 					throw new SynchronizationLockException ("The current thread has not entered the lock in upgradable mode");
 
-				upgradableTaken.Value = false;
-				upgradableEvent.Set ();
+				if (--ctstate.UpgradeableRecursiveCount == 0) {
+					upgradableTaken.Value = false;
+					upgradableEvent.Set ();
 
-				ctstate.LockState ^= LockState.Upgradable;
-				--ctstate.UpgradeableRecursiveCount;
-				if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
-					readerDoneEvent.Set ();
+					ctstate.LockState ^= LockState.Upgradable;
+					if (Interlocked.Add (ref rwlock, -RwRead) >> RwReadBit == 0)
+						readerDoneEvent.Set ();
+				}
 			}
 		}
 

+ 55 - 0
mcs/class/System.Core/Test/System.Threading/ReaderWriterLockSlimTest.cs

@@ -576,5 +576,60 @@ namespace MonoTests.System.Threading
 			Assert.AreEqual (false, wLock, "#1b");
 			Assert.AreEqual (0, rWrite, "#3b");
 		}
+
+		[Test]
+		public void RecursiveEnterExitReadTest ()
+		{
+			var v = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);
+
+			v.EnterReadLock ();
+			v.EnterReadLock ();
+			v.EnterReadLock ();
+
+			Assert.IsTrue (v.IsReadLockHeld);
+			Assert.AreEqual (3, v.RecursiveReadCount);
+
+			v.ExitReadLock ();
+
+			Assert.IsTrue (v.IsReadLockHeld);
+			Assert.AreEqual (2, v.RecursiveReadCount);
+		}
+
+		[Test]
+		public void RecursiveEnterExitWriteTest ()
+		{
+			var v = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);
+
+			v.EnterWriteLock ();
+			v.EnterWriteLock ();
+			v.EnterWriteLock ();
+
+			Assert.IsTrue (v.IsWriteLockHeld);
+			Assert.AreEqual (3, v.RecursiveWriteCount);
+
+			v.ExitWriteLock ();
+			v.ExitWriteLock ();
+
+			Assert.IsTrue (v.IsWriteLockHeld);
+			Assert.AreEqual (1, v.RecursiveWriteCount);
+		}
+
+		[Test]
+		public void RecursiveEnterExitUpgradableTest ()
+		{
+			var v = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);
+
+			v.EnterUpgradeableReadLock ();
+			v.EnterUpgradeableReadLock ();
+			v.EnterUpgradeableReadLock ();
+
+			Assert.IsTrue (v.IsUpgradeableReadLockHeld);
+			Assert.AreEqual (3, v.RecursiveUpgradeCount);
+
+			v.ExitUpgradeableReadLock ();
+
+			Assert.IsTrue (v.IsUpgradeableReadLockHeld);
+			Assert.AreEqual (2, v.RecursiveUpgradeCount);
+		}
 	}
 }