Rollie
Rollie

Reputation: 4752

Is there a facility in boost to allow for write-biased locking?

If I have the following code:

#include <boost/date_time.hpp>
#include <boost/thread.hpp>

boost::shared_mutex g_sharedMutex;

void reader()
{
    boost::shared_lock<boost::shared_mutex> lock(g_sharedMutex);
    boost::this_thread::sleep(boost::posix_time::seconds(10));
}

void makeReaders()
{
    while (1)
    {
        boost::thread ar(reader);
        boost::this_thread::sleep(boost::posix_time::seconds(3));
    }
}

boost::thread mr(makeReaders);
boost::this_thread::sleep(boost::posix_time::seconds(5));
boost::unique_lock<boost::shared_mutex> lock(g_sharedMutex);

...

the unique lock will never be acquired, because there are always going to be readers. I want a unique_lock that, when it starts waiting, prevents any new read locks from gaining access to the mutex (called a write-biased or write-preferred lock, based on my wiki searching). Is there a simple way to do this with boost? Or would I need to write my own?

Upvotes: 1

Views: 461

Answers (1)

Ze Blob
Ze Blob

Reputation: 2957

Note that I won't comment on the win32 implementation because it's way more involved and I don't have the time to go through it in detail. That being said, it's interface is the same as the pthread implementation which means that the following answer should be equally valid.

The relevant pieces of the pthread implementation of boost::shared_mutex as of v1.51.0:

void lock_shared()
{
    boost::this_thread::disable_interruption do_not_disturb;
    boost::mutex::scoped_lock lk(state_change);

    while(state.exclusive || state.exclusive_waiting_blocked)
    {
        shared_cond.wait(lk);
    }
    ++state.shared_count;
}

void lock()
{
    boost::this_thread::disable_interruption do_not_disturb;
    boost::mutex::scoped_lock lk(state_change);

    while(state.shared_count || state.exclusive)
    {
        state.exclusive_waiting_blocked=true;
        exclusive_cond.wait(lk);
    }
    state.exclusive=true;
}

The while loop conditions are the most relevant part for you. For the lock_shared function (read lock), notice how the while loop will not terminate as long as there's a thread trying to acquire (state.exclusive_waiting_blocked) or already owns (state.exclusive) the lock. This essentially means that write locks have priority over read locks.

For the lock function (write lock), the while loop will not terminate as long as there's at least one thread that currently owns the read lock (state.shared_count) or another thread owns the write lock (state.exclusive). This essentially gives you the usual mutual exclusion guarantees.

As for deadlocks, well the read lock will always return as long as the write locks are guaranteed to be unlocked once they are acquired. As for the write lock, it's guaranteed to return as long as the read locks and the write locks are always guaranteed to be unlocked once acquired.

In case you're wondering, the state_change mutex is used to ensure that there's no concurrent calls to either of these functions. I'm not going to go through the unlock functions because they're a bit more involved. Feel free to look them over yourself, you have the source after all (boost/thread/pthread/shared_mutex.hpp) :)

All in all, this is pretty much a text book implementation and they've been extensively tested in a wide range of scenarios (libs/thread/test/test_shared_mutex.cpp and massive use across the industry). I wouldn't worry too much as long you use them idiomatically (no recursive locking and always lock using the RAII helpers). If you still don't trust the implementation, then you could write a randomized test that simulates whatever test case you're worried about and let it run overnight on hundreds of thread. That's usually a good way to tease out deadlocks.

Now why would you see that a read lock is acquired after a write lock is requested? Difficult to say without seeing the diagnostic code that you're using. Chances are that the read lock is acquired after your print statement (or whatever you're using) is completed and before state_change lock is acquired in the write thread.

Upvotes: 2

Related Questions