public static void BasicLockTest()
{
var trwl = new TestReaderWriterLock();
TestLockCookie tlc;
var threadReady = new AutoResetEvent(false);
var continueThread = new AutoResetEvent(false);
Action checkForThreadErrors, waitForThread;
Thread t =
ThreadTestHelpers.CreateGuardedThread(out checkForThreadErrors, out waitForThread, () =>
{
TestLockCookie tlc2;
Action switchToMainThread =
() =>
{
threadReady.Set();
continueThread.CheckedWait();
};
switchToMainThread();
// Multiple readers from multiple threads
{
trwl.AcquireReaderLock();
trwl.AcquireReaderLock();
switchToMainThread();
trwl.ReleaseReaderLock();
switchToMainThread();
trwl.ReleaseReaderLock();
switchToMainThread();
trwl.AcquireReaderLock();
trwl.ReleaseReaderLock();
switchToMainThread();
}
// What can be done when a read lock is held
{
trwl.AcquireReaderLock();
switchToMainThread();
// Any thread can take a read lock
trwl.AcquireReaderLock();
trwl.ReleaseReaderLock();
switchToMainThread();
// No thread can take a write lock
trwl.AcquireWriterLock(TimeoutExceptionHResult);
trwl.AcquireReaderLock();
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
trwl.ReleaseReaderLock();
switchToMainThread();
trwl.ReleaseReaderLock();
switchToMainThread();
// Owning thread releases read lock when upgrading
trwl.AcquireWriterLock();
trwl.ReleaseWriterLock();
switchToMainThread();
// Owning thread cannot upgrade if there are other readers or writers
trwl.AcquireReaderLock();
switchToMainThread();
trwl.ReleaseReaderLock();
trwl.AcquireWriterLock();
switchToMainThread();
trwl.ReleaseWriterLock();
switchToMainThread();
}
// What can be done when a write lock is held
{
// Write lock acquired through AcquireWriteLock is exclusive
trwl.AcquireWriterLock();
switchToMainThread();
trwl.ReleaseWriterLock();
switchToMainThread();
// Write lock acquired through upgrading is also exclusive
trwl.AcquireReaderLock();
tlc2 = trwl.UpgradeToWriterLock();
switchToMainThread();
trwl.DowngradeFromWriterLock(tlc2);
trwl.ReleaseReaderLock();
switchToMainThread();
// Write lock acquired through restore is also exclusive
trwl.AcquireWriterLock();
tlc = trwl.ReleaseLock();
trwl.RestoreLock(tlc);
switchToMainThread();
trwl.ReleaseWriterLock();
switchToMainThread();
}
});
t.IsBackground = true;
t.Start();
Action beginSwitchToBackgroundThread = () => continueThread.Set();
Action endSwitchToBackgroundThread =
() =>
{
try
{
threadReady.CheckedWait();
}
finally
{
checkForThreadErrors();
}
};
Action switchToBackgroundThread =
() =>
{
beginSwitchToBackgroundThread();
endSwitchToBackgroundThread();
};
endSwitchToBackgroundThread();
// Multiple readers from muliple threads
{
trwl.AcquireReaderLock();
trwl.AcquireReaderLock();
switchToBackgroundThread(); // AcquireReaderLock * 2
trwl.ReleaseReaderLock();
switchToBackgroundThread(); // ReleaseReaderLock
// Release/restore the read lock while a read lock is held by another thread
tlc = trwl.ReleaseLock();
trwl.RestoreLock(tlc);
switchToBackgroundThread(); // ReleaseReaderLock
// Downgrade to read lock allows another thread to acquire read lock
tlc = trwl.UpgradeToWriterLock();
trwl.DowngradeFromWriterLock(tlc);
switchToBackgroundThread(); // AcquireReaderLock, ReleaseReaderLock
trwl.ReleaseReaderLock();
}
// What can be done when a read lock is held
{
switchToBackgroundThread(); // AcquireReaderLock
{
// Any thread can take a read lock
trwl.AcquireReaderLock();
trwl.ReleaseReaderLock();
switchToBackgroundThread(); // same as above
// No thread can take a write lock
trwl.AcquireWriterLock(TimeoutExceptionHResult);
trwl.AcquireReaderLock();
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
switchToBackgroundThread(); // same as above
trwl.ReleaseReaderLock();
// Other threads cannot upgrade to a write lock, but the owning thread can
trwl.AcquireReaderLock();
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
trwl.ReleaseReaderLock();
}
switchToBackgroundThread(); // ReleaseReaderLock
// Owning thread releases read lock when upgrading
trwl.AcquireReaderLock();
beginSwitchToBackgroundThread(); // AcquireWriterLock: background thread gets blocked
trwl.UpgradeToWriterLock(); // unblocks background thread: ReleaseWriterLock
trwl.ReleaseWriterLock();
endSwitchToBackgroundThread();
// Owning thread cannot upgrade if there are other readers or writers
trwl.AcquireReaderLock();
switchToBackgroundThread(); // AcquireReaderLock
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
trwl.ReleaseReaderLock();
switchToBackgroundThread(); // ReleaseReaderLock, AcquireWriterLock
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
switchToBackgroundThread(); // ReleaseWriterLock
}
// What can be done when a write lock is held
{
trwl.AcquireWriterLock();
TestLockCookie restoreToWriteLockTlc = trwl.ReleaseLock();
Action verifyCannotAcquireLock =
() =>
{
trwl.AcquireReaderLock(TimeoutExceptionHResult);
trwl.AcquireWriterLock(TimeoutExceptionHResult);
trwl.UpgradeToWriterLock(TimeoutExceptionHResult);
};
Action verifyCanAcquireLock =
() =>
{
trwl.AcquireReaderLock();
tlc = trwl.UpgradeToWriterLock();
trwl.DowngradeFromWriterLock(tlc);
trwl.ReleaseReaderLock();
trwl.AcquireWriterLock();
trwl.ReleaseWriterLock();
trwl.RestoreLock(restoreToWriteLockTlc.Clone());
trwl.ReleaseWriterLock();
};
// Write lock acquired through AcquireWriteLock is exclusive
switchToBackgroundThread(); // AcquireWriterLock
verifyCannotAcquireLock();
switchToBackgroundThread(); // ReleaseWriterLock
verifyCanAcquireLock();
// Write lock acquired through upgrading is also exclusive
switchToBackgroundThread(); // AcquireReaderLock, UpgradeToWriterLock
verifyCannotAcquireLock();
switchToBackgroundThread(); // DowngradeFromWriterLock, ReleaseReaderLock
verifyCanAcquireLock();
// Write lock acquired through restore is also exclusive
switchToBackgroundThread(); // AcquireWriterLock, ReleaseLock, RestoreLock
verifyCannotAcquireLock();
switchToBackgroundThread(); // ReleaseWriterLock
verifyCanAcquireLock();
}
beginSwitchToBackgroundThread();
waitForThread();
trwl.Dispose();
}