I encountered a scenario where ReaderWriterLockSlim seems to get broken after a series of legal actions.
The flow is:
After stage 3 above, a debugger shows that lock1.CurrentReadCount is corrupt - seems to have overflowed down to 0x7FFFFFF.
I wonder if anyone had encountered this, or maybe I'm missing something.
The code that reproduces it:
[TestMethod]
public void ReproTest()
{
var rwlock = new ReaderWriterLockSlim();
rwlock.EnterWriteLock();
bool taken = false;
var reader = new Thread(() =>
{
try
{
rwlock.EnterReadLock();
s_logger.Info("Enter");
}
catch (ThreadInterruptedException)
{
rwlock.ExitReadLock();
}
});
reader.Name = "Reader";
reader.Start();
Thread.Sleep(1000);
reader.Interrupt();
Thread.Sleep(1000);
rwlock.ExitWriteLock();
while (!taken)
{
taken = rwlock.TryEnterReadLock(1000);
}
Thread.Sleep(1000);
}
This looks like a bug in the framework (tested on v3.5 and v4.0). The ExitReadLock()
should throw a SynchronizationLockException
, but doesn't in this case. Indeed, you can trigger a very similar issue much more simply with the following:
rwlock.EnterReadLock();
rwlock.ExitReadLock();
// This should throw a SynchronizationLockException but doesn't
rwlock.ExitReadLock();
// At this point, rwlock.CurrentReaderCount = 0x0fffffff
(In fact, ExitReadLock()
will corrupt the lock if it's called without a matching EnterReadLock()
on any thread that has previously entered the lock.)
The issue only occurs when the ReaderWriterLockSlim
is created using the parameterless constructor, or with LockRecursionPolicy.NoRecursion
. If created with LockRecursionPolicy.SupportsRecursion
, it will not be corrupted by the unmatched ExitReadLock()
.
If you expect the reader thread to be interrupted whilst waiting for entry into lock, I would suggest changing the reader thread method to:
var reader = new Thread(() =>
{
var entered = false;
try
{
rwlock.EnterReadLock();
entered = true;
s_logger.Info("Enter");
}
finally
{
if (entered) rwlock.ExitReadLock();
}
});