Johnv2020
Johnv2020

Reputation: 2146

IOException getting thrown by ReaderWriteLockSlim?

I have a static class which is accessed by multiple threads and uses a ReaderWriterLockSlim in various methods to maintain thread safety. This works fine most of the time, however very intermittently I'm seeing an IOException handle is invalid error being thrown by one particular method as shown in the stack trace below. This has me greatly confused as I didn't even know that System.IO was involved in a ReaderWriterLock.

Any help at all in understanding where the error may be comming from would be greatly appreciated.

Stack Trace:

System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Threading.EventWaitHandle.Reset()
at System.Threading.ReaderWriterLockSlim.WaitOnEvent(EventWaitHandle waitEvent, UInt32& numWaiters, Int32 millisecondsTimeout)
at System.Threading.ReaderWriterLockSlim.TryEnterUpgradeableReadLock(Int32 millisecondsTimeout)

Code:

class Class1
{
    private static ReaderWriterLockSlim readwriteLock = new ReaderWriterLockSlim();
    private const int readwriterlocktimeoutms = 5000;

    private static void myMethod(int ID)
    {
        bool IsTaken = false;
        bool isWriteLockTaken = false;

        if (!readwriteLock.TryEnterUpgradeableReadLock(readwriterlocktimeoutms))
        {
            throw new Exception("SafeGetSuItem: Error acquiring read lock");
        }
        else { IsTaken = true; }
        try
        {                
         // do some work which may require upgrading to a write lock depending on particular conditions
        }
        finally
        {
            if (IsTaken)
            {
                try
                {
                    readwriteLock.ExitUpgradeableReadLock();
                    IsTaken = false;
                }
                catch
                {
                    throw;
                }
            }                
        }
    }

}


enter code here

bool IsWriteTaken = false;
        try
        {
            if (!readerwriterlock.TryEnterWriteLock(readerwriterlocktimeout))
            {
                // log the error
            }
            else
            {
                IsWriteTaken = true;
            }

            if (IsWriteTaken)
            {
                // do some work
            }
        }
        finally
        {
            if (IsWriteTaken)
            {
                try
                {
                    readerwriterlock.ExitWriteLock();
                }
                catch
                {
                    throw;
                }
            }
        }

Upvotes: 3

Views: 709

Answers (1)

Brian Gideon
Brian Gideon

Reputation: 48949

This is a little weird. You may have stumbled upon a bug in the WaitHandle class. I picked this apart via Reflector and this is what I am seeing.

  • Calling Dispose on the ReaderWriterLockSlim will call Close on the EventWaitHandle listed in the stack trace.
  • Calling Close on a EventWaitHandle attempts to dispose the underlying SafeHandle.
  • Calling Reset on a EventWaitHandle calls directly into the ResetEvent Win32 API from kernel32.dll and passes in the SafeHandle.
  • I see no synchronization mechanisms in place that prevent a race between the disposing of a SafeHandle and having that instance consumed by the Win32 API.

Are you calling Dispose on the ReaderWriterLockSlim instance from another thread while TryEnterUpgradeableReadLock could be executing? This seems like the most likely scenario to me. Actually, this is the only scenario that I am seeing that would lead to an IOException being thrown.

It seems to me, based solely on my cursory analysis of the BCL code, that the IOException could be by-design, but it would make a whole lot more sense if Microsoft could somehow generate a ObjectDisposedException instead which happens on every single other attempt I have made to reproduce the problem. I would go ahead and report this to Microsoft.

Upvotes: 1

Related Questions