Question

I got this exception
The read lock is being released without being held.
at System.Threading.ReaderWriterLockSlim.ExitReadLock() at .. GetBreed(String)

Below is the only place in code that accesses the lock. As you can see, there is no recursion. I'm having trouble understanding how this exception could occur.

static readonly Dictionary<string, BreedOfDog> Breeds 
     = new Dictionary<string,BreedOfDog>();

static BreedOfDog GetBreed(string name)
{
        try
        {
            rwLock.EnterReadLock();
            BreedOfDog bd;
            if (Breeds.TryGetValue(name, out bd))
            {
                return bd;
            }
        }
        finally
        {
            rwLock.ExitReadLock();
        }

        try
        {
            rwLock.EnterWriteLock();
            BreedOfDog bd;
            //make sure it hasn't been added in the interim
            if (Breeds.TryGetValue(t, out bd)
            {
                return bd;
            }
            bd = new BreedOfDog(name); //expensive to fetch all the data needed to run  the constructor, hence the caching

            Breeds[name] = bd;
            return bd;
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
}  
Was it helpful?

Solution

I'm guessing you have something re-entrant, and it is throwing an exception when obtaining the lock. There is a catch-22 whether you "take the lock", "try" vs "try", "take the lock", but the "take the lock", "try" has fewer failure cases (the "aborted between take and try" is so vanishingly unlikely you don't need to stress).

Move the "take the lock" outside the "try", and see what the actual exception is.

The problem is most likely that you are failing to take the lock (probably re-entrancy), then trying to unlock something you didn't take. This could mean that the exception surfaces in the orginal code that took the lock, due to trying to release twice when only taken once.

Note: Monitor has new overloads with "ref bool" parameters to help with this scenario - but not the other lock types.

OTHER TIPS

Use LockRecursionPolicy.SupportsRecursion when instantiating the RWLS. If the error goes away then you actually do have some type of recursion involved. Perhaps it is in code that you did not post?

And if you are really concerned about getting maximum concurrency out of this (as I suspect you are since you are using a RWLS) then you could use the double-checked locking pattern. Notice how your original code already has that feel to it? So why beat around bush? Just do it.

In the following code notice how I always treat the Breeds reference as immutable and then inside the lock I recheck, copy, change, and swap out the reference.

static volatile Dictionary<string, BreedOfDog> Breeds = new Dictionary<string,BreedOfDog>();
static readonly object LockObject = new object();

static BreedOfDog GetBreed(string name)
{
  BreedOfDog bd;
  if (!Breeds.TryGetValue(name, out bd))
  {
    lock (LockObject)
    {
      if (!Breeds.TryGetValue(name, out bd))
      {
        bd = new BreedOfDog(name);
        var copy = new Dictionary<string, BreedOfDog>(Breeds);
        copy[name] = bd;
        Breeds = copy;
      }
    }
  }
  return bd;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top