public void Locksmith_WhenReadLockReleased_ReleasesAWaitingWriterIfPresent() { const int initialValue = 10; const int successValue = 25; int writeResult = initialValue; ManualResetEvent writerExitedEvent = new ManualResetEvent(false); Locksmith.EnterReadLock(ref lockState, readLockTestObject); { StartNewThreadThatRuns(SetWriteResultToFailureValue); Thread.Sleep(50); Assert.That(WritersAreWaiting); Assert.That(writeResult, Is.EqualTo(initialValue)); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); writerExitedEvent.WaitOne(); Assert.That(writeResult, Is.EqualTo(successValue)); return; void SetWriteResultToFailureValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { writeResult = successValue; } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); writerExitedEvent.Set(); } }
public void Locksmith_WhenWriteLockReleased_ReleasesAllWaitingReaders() { int numberOfThreadsToCreate = 100; int numberOfReads = 0; CountdownEvent readersExitedEvent = new CountdownEvent(numberOfThreadsToCreate); Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { for (int i = 0; i < numberOfThreadsToCreate; i++) { StartNewThreadThatRuns(IncrementNumReadsInsideReadLock); } Thread.Sleep(1000); Assert.That(numberOfReads, Is.EqualTo(0)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); readersExitedEvent.Wait(); Assert.That(numberOfReads, Is.EqualTo(numberOfThreadsToCreate)); return; void IncrementNumReadsInsideReadLock() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { Interlocked.Increment(ref numberOfReads); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); readersExitedEvent.Signal(); } }
public void Locksmith_WhenWriteLockReleased_ReleasesAWaitingReaderIfPresent() { const int initialValue = 10; const int expectedValue = 42; int readResult = initialValue; ManualResetEvent readerExitedEvent = new ManualResetEvent(false); Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { StartNewThreadThatRuns(SetReadResultToExpectedValue); Thread.Sleep(100); Assert.That(ReadersAreWaiting); Assert.That(readResult, Is.EqualTo(initialValue)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); //- This should be more than enough time for the other thread to wake up and set the value. readerExitedEvent.WaitOne(); Assert.That(readResult, Is.EqualTo(expectedValue)); return; void SetReadResultToExpectedValue() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { readResult = expectedValue; } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); readerExitedEvent.Set(); } }
public void Locksmith_WhenReadLockIsUpgradedWithOnlyOneReader_PreventsNewReadersFromEntering() { const int failureValue = 25; const int expectedValue = 10; int result = expectedValue; Locksmith.EnterReadLock(ref lockState, readLockTestObject); Locksmith.Upgrade(ref lockState, writeLockTestObject); { StartNewThreadThatRuns(SetReadResultToFailureValue); Thread.Sleep(1000); Assert.That(result, Is.EqualTo(expectedValue)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); return; void SetReadResultToFailureValue() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); result = failureValue; Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); } }
public void Locksmith_WhenReadLockedAndReservedForWriter_CausesNewReadersToWait() { const int initialValue = 10; const int valueAddedByReader = 50; const int valueAddedByWriter = 20; int result = initialValue; CountdownEvent exitedLocksEvent = new CountdownEvent(2); Locksmith.EnterReadLock(ref lockState, readLockTestObject); { StartNewThreadThatRuns(AddWriterValue); Thread.Sleep(300); StartNewThreadThatRuns(AddReaderValue); Thread.Sleep(300); Assert.That(result, Is.EqualTo(initialValue)); Assert.That(lockState.IsReservedForWriter()); Assert.That(lockState.HasWaitingReaders()); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); exitedLocksEvent.Wait(); return; void AddReaderValue() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); //- You passed the wrong object ya dolt... { int currentResult; int valueToSet; do { currentResult = result; valueToSet = currentResult + valueAddedByReader; } while (Interlocked.CompareExchange(ref result, valueToSet, currentResult) != currentResult); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); exitedLocksEvent.Signal(); } void AddWriterValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { int currentResult, valueToSet; do { currentResult = result; valueToSet = currentResult + valueAddedByWriter; }while (Interlocked.CompareExchange(ref result, valueToSet, currentResult) != currentResult); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); exitedLocksEvent.Signal(); } }
public void Locksmith_WhileUnlocked_AllowsAWriterToEnter() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { Thread.SpinWait(1); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); }
public void Locksmith_WhenWriteLocked_ThrowsIfExitReadLockIsCalled() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); Assert.Throws <SynchronizationLockException>(ExitLockWithExitReadLock); Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); return; void ExitLockWithExitReadLock() => Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); }
public void Locksmith_WhenReadLockIsUpgraded_ThrowsIfExitReadLockIsCalled() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); Locksmith.Upgrade(ref lockState, writeLockTestObject); Assert.Throws <SynchronizationLockException>(TryToCallExitReadLock); Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); return; void TryToCallExitReadLock() => Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); }
public void Locksmith_WhenReadLockIsUpgradedWhileOtherReadersAreActive_WaitsUntilAllOtherReadersExitToConvertLock() { const int numberOfOtherReaders = 10; int numberOfReads = 0; int numberOfExits = 0; CountdownEvent readEvent = new CountdownEvent(numberOfOtherReaders); CountdownEvent exitEvent = new CountdownEvent(numberOfOtherReaders); ManualResetEvent upgradeEvent = new ManualResetEvent(false); Locksmith.EnterReadLock(ref lockState, readLockTestObject); for (int i = 0; i < numberOfOtherReaders; i++) { StartNewThreadThatRuns(IncrementNumReadsInsideReadLock); } readEvent.Wait(); Assert.That(numberOfReads, Is.EqualTo(numberOfOtherReaders)); upgradeEvent.Set(); Locksmith.Upgrade(ref lockState, writeLockTestObject); { Assert.That(lockState.IsReadLocked() == false, $"{lockState}"); exitEvent.Wait(); Assert.That(numberOfExits, Is.EqualTo(numberOfOtherReaders)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); return; void IncrementNumReadsInsideReadLock() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { Interlocked.Increment(ref numberOfReads); readEvent.Signal(); upgradeEvent.WaitOne(); Thread.Sleep(1000); Assert.That(ReservedForUpgrade && IsReadLocked); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); Interlocked.Increment(ref numberOfExits); exitEvent.Signal(); } }
public void Locksmith_WhenReadLockReleased_ReleasesOnlyOneWaitingWriter() { const int initialValue = 10; const int valueToAdd = 15; const int successValue = initialValue + valueToAdd; int result = initialValue; ManualResetEvent resultTestedEvent = new ManualResetEvent(false); Locksmith.EnterReadLock(ref lockState, readLockTestObject); { StartNewThreadThatRuns(SetWriteResultToFailureValue); StartNewThreadThatRuns(SetWriteResultToFailureValue); Thread.Sleep(300); Assert.That(NumberOfWaitingWriters, Is.EqualTo(2)); Assert.That(result, Is.EqualTo(initialValue)); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); Thread.Sleep(500); Assert.That(result, Is.EqualTo(successValue)); resultTestedEvent.Set(); return; void SetWriteResultToFailureValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { int currentResult; int valueToSet; do { currentResult = result; valueToSet = currentResult + valueToAdd; }while (Interlocked.CompareExchange(ref result, valueToSet, currentResult) != currentResult); resultTestedEvent.WaitOne(); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } }
public bool IfValueEqualsFirstSetToSecond(T valueToTestFor, T valueToSetTo) { Locksmith.EnterReadLock(ref lockState, readLockobject); { if (lockedValue.Equals(valueToTestFor)) { Locksmith.Upgrade(ref lockState, this); { lockedValue = valueToSetTo; } Locksmith.ExitWriteLock(ref lockState, readLockobject, this); return(true); } } Locksmith.ExitReadLock(ref lockState, readLockobject, this); return(false); }
public void Locksmith_WhenAReaderHasDecidedToWait_WillNotDeadlockIfTheReaderCantCallWaitBeforeAWriterExits() { const int initialValue = 10; const int expectedValue = 42; int valueBeingTested = initialValue; ManualResetEvent readerExitedEvent = new ManualResetEvent(false); Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); lock (readLockTestObject) //- This should prevent the reader from being able to lock the same object, which it needs to do in order to call Monitor.Wait() { StartNewThreadThatRuns(SetReadResultToTestInt); //- Hold the lock long enough that the reader will decide to start waiting Thread.Sleep(500); Assert.That(ReadersAreWaiting); //- Make sure they didn't manage to set the value despite the lock somehow. Assert.That(valueBeingTested, Is.EqualTo(initialValue)); //- Exit the write lock, which should call Monitor.PulseAll(). // lock() is re-entrant so the fact that we already locked the read lock object, // shouldn't prevent this thread from reacquiring the lock to call Monitor.PulseAll(). Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } //- Give the reader time to work if they managed to avoid the deadlock readerExitedEvent.WaitOne(); Assert.That(valueBeingTested, Is.EqualTo(expectedValue)); return; void SetReadResultToTestInt() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { valueBeingTested = expectedValue; } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); readerExitedEvent.Set(); } }
public void Locksmith_WhileWriteLocked_PreventsOtherWritersFromEntering() { const int initialValue = 10; const int expectedValue = 42; int valueBeingTested = initialValue; Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { StartNewThreadThatRuns(SetValueBeingTestedToExpectedValue); Thread.Sleep(1000); Assert.That(valueBeingTested, Is.EqualTo(initialValue)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); void SetValueBeingTestedToExpectedValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); valueBeingTested = expectedValue; Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } }
public void Locksmith_WhileReadLocked_PreventsWritersFromEntering() { const int failureValue = 25; const int expectedValue = 10; int writeResult = expectedValue; Locksmith.EnterReadLock(ref lockState, readLockTestObject); { StartNewThreadThatRuns(SetWriteResultToFailureValue); Thread.Sleep(1000); Assert.That(writeResult, Is.EqualTo(expectedValue)); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); return; void SetWriteResultToFailureValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); writeResult = failureValue; Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } }
public void Locksmith_WhenWriteLockReleased_ReleasesAWaitingWriterIfPresent() { const int initialValue = 10; const int expectedValue = 42; int valueBeingTested = initialValue; ManualResetEvent writerExitedEvent = new ManualResetEvent(false); Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { StartNewThreadThatRuns(SetValueBeingTestedToExpectedValue); //- We'll wait a while to give the other thread a chance to set the value, so we can see if the lock works. Thread.Sleep(100); Assert.That(WritersAreWaiting); Assert.That(valueBeingTested, Is.EqualTo(initialValue)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); //- This should be more than enough time for the other thread to wake up and set the value. writerExitedEvent.WaitOne(); Assert.That(valueBeingTested, Is.EqualTo(expectedValue)); return; void SetValueBeingTestedToExpectedValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { valueBeingTested = expectedValue; } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); writerExitedEvent.Set(); } }
public void Locksmith_WhenReadLockIsUpgradedWhileOtherReadersAreActive_PreventsNewReadersFromEntering() { const int numberOfOtherReaders = 10; int numberOfReadersEntered = 0; int numberOfExits = 0; CountdownEvent readEvent = new CountdownEvent(numberOfOtherReaders); CountdownEvent exitEvent = new CountdownEvent(numberOfOtherReaders); ManualResetEvent outerUpgradeEvent = new ManualResetEvent(false); ManualResetEvent innerUpgradeEvent = new ManualResetEvent(false); for (int i = 0; i < numberOfOtherReaders; i++) { StartNewThreadThatRuns(IncrementNumReadsInsideReadLock_FirstSet); } readEvent.Wait(); Assert.That(numberOfReadersEntered, Is.EqualTo(numberOfOtherReaders)); StartNewThreadThatRuns(EnterReadLockAndUpgrade); exitEvent.Wait(); Assert.That(numberOfExits, Is.EqualTo(numberOfOtherReaders)); for (int i = 0; i < numberOfOtherReaders; i++) { StartNewThreadThatRuns(IncrementNumReadsInsideReadLock_SecondSet); } Thread.Sleep(1000); Assert.That(numberOfReadersEntered, Is.EqualTo(numberOfOtherReaders)); innerUpgradeEvent.Set(); return; void EnterReadLockAndUpgrade() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); outerUpgradeEvent.Set(); Locksmith.Upgrade(ref lockState, writeLockTestObject); { innerUpgradeEvent.WaitOne(); Assert.That(numberOfExits, Is.EqualTo(numberOfOtherReaders)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } void IncrementNumReadsInsideReadLock_FirstSet() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { Interlocked.Increment(ref numberOfReadersEntered); readEvent.Signal(); outerUpgradeEvent.WaitOne(); Thread.Sleep(1000); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); Interlocked.Increment(ref numberOfExits); exitEvent.Signal(); } void IncrementNumReadsInsideReadLock_SecondSet() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { Interlocked.Increment(ref numberOfReadersEntered); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); Interlocked.Increment(ref numberOfExits); } }
void SetIntToValueInsideWriteLock(ref int intToSet, int valueToSetTo) { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); intToSet = valueToSetTo; Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); }
public void Locksmith_WhenWriteLockReleased_ReleasesWaitingWritersBeforeWaitingReaders() { const int initialValue = 10; const int valueAddedByReader = 50; const int valueAddedByWriter = 20; const int successValue = initialValue + valueAddedByWriter; int result = initialValue; ManualResetEvent valueSetEvent = new ManualResetEvent(false); ManualResetEvent valueTestedEvent = new ManualResetEvent(false); Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { StartNewThreadThatRuns(AddWriterValue); Thread.Sleep(200); Assert.That(WritersAreWaiting); StartNewThreadThatRuns(AddReaderValue); Thread.Sleep(200); Assert.That(ReadersAreWaiting); Assert.That(result, Is.EqualTo(initialValue)); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); valueSetEvent.WaitOne(); Assert.That(ReadersAreWaiting); Assert.That(WritersAreWaiting == false); Assert.That(result, Is.EqualTo(successValue)); valueTestedEvent.Set(); return; void AddReaderValue() { Locksmith.EnterReadLock(ref lockState, readLockTestObject); { int currentResult, valueToSet; do { currentResult = result; valueToSet = currentResult + valueAddedByReader; } while (Interlocked.CompareExchange(ref result, valueToSet, currentResult) != currentResult); } Locksmith.ExitReadLock(ref lockState, readLockTestObject, writeLockTestObject); } void AddWriterValue() { Locksmith.EnterWriteLock(ref lockState, writeLockTestObject); { int currentResult; int valueToSet; do { currentResult = result; valueToSet = currentResult + valueAddedByWriter; }while (Interlocked.CompareExchange(ref result, valueToSet, currentResult) != currentResult); valueSetEvent.Set(); valueTestedEvent.WaitOne(); } Locksmith.ExitWriteLock(ref lockState, readLockTestObject, writeLockTestObject); } }