Using ReaderWriterLockSlim’s EnterUpgradeableReadLock

time to read 4 min | 784 words

I got a comment in this post suggesting that the code can make use of the EnterUpgradeableReadLock method to simplify this code:

public static string Intern(string str)
{
    string val;
    
    locker.EnterReadLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
    }
    finally
    {
        locker.ExitReadLock();
    }
    
    locker.EnterWriteLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
            
        strings.Add(str,str);
        return str;
    }
    finally
    {
        locker.ExitWriteLock();
    }
}

First, let us look at the code that is making use of EnterUpgradeableReadLock:

public static string Intern(string str)
{
    string val;
    
    locker.EnterUpgradeableReadLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
            
        locker.EnterWriteLock();
        try
        {
            strings.Add(str,str);
        }
        finally
        {
            locker.ExitWriteLock();
        }
        return str;
    }
    finally
    {
        locker.ExitUpgradeableReadLock();
    }
}

And, well, it is somewhat simpler, I’ll admit.

The reason that Upgradable Read was introduce is that in the 2.0 ReaderWriterLock, there was a lot of confusion about how you upgrade from read lock to write lock. Essentially, the upgrade process would give up the lock, allowing other threads to sneak in. The Upgradable Read is an explicit way to handle that, since it doesn’t free the lock when you upgrade.

But there is one huge problem with this code. Only one thread is able to enter upgradable read lock. That means that in code such as this, where this is the only access that we have, we have in effect turned the reader writer lock into a single write lock. Since only one thread can enter upgradable read, it means that we might have well used a standard lock(syncLock) statement.

My original code is slightly more complex, since it has to check the presence of the value twice, but it also have far more parallelism, since multiple threads can read from the strings table at the same time, which is not possible in the Upgradable Read mode.

Upgradable Read is only useful if you have multiple ways of accessing the data, some that are purely read and some (rare) that are upgradable reads. If most / all of your calls go through upgradable read code paths, you are better off using read/write and handling the lock release explicitly.