Boost logo

Boost Users :

Subject: [Boost-users] boost::shared_locks and boost::upgrade_locks
From: Kelvin Chung (kelvSYC_at_[hidden])
Date: 2011-12-07 19:05:38


I'm trying to implement a read-write lock, and I was told to use
boost::upgrade_lock. However, only one thread can have a
boost::upgrade_lock, and everyone else gets blocked, so it's not much
on the read end. (Correct me if I am wrong, but boost::upgrade_lock
only waits if someone has exclusive, but someone with
boost::upgrade_lock will cause everyone else to block)

But reads are cheap in my case - I just need to use locks for a cache.
So, suppose I have the following:

class Cache {
        std::map<Input, Output> cache;
        boost::shared_mutex mutex;
public:
        Output query(const Input& in);
};

It would appear that the proper way to implement Cache::query() is as follows:

Output query(const Input& in) {
        boost::shared_lock<boost::shared_mutex> readLock(mutex);
        if (cache.count(in) == 0) {
                readLock.unlock();
                boost::unique_lock<boost::shared_mutex> writeLock(mutex);
                
                // Check to see if the result was added while waiting for lock
                if (cache.count(in) != 0) return cache[in];
                
                cache[in] = compute_output(in);
                return cache[in];
        } else {
                return cache[in];
        }
}

Is this correct? Or maybe it should be this:

Output query(const Input& in) {
        boost::shared_lock<boost::shared_mutex> readLock(mutex);
        if (cache.count(in) == 0) {
                readLock.unlock();
                boost::upgrade_lock<boost::shared_mutex> rereadLock(mutex);
                
                // Check to see if the result was added while waiting for lock
                if (cache.count(in) != 0) {
                        return cache[in];
                } else {
                        boost::upgrade_to_unique_lock<boost::shared_mutex> writeLock(rereadLock);
                        
                        cache[in] = compute_output(in);
                        return cache[in];
                }
        } else {
                return cache[in];
        }
}

A side question is that if compute_output() is an expensive operation,
should that be moved to the part between releasing the read lock and
getting the write/reread lock?


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net