mr-edd.co.uk :: horsing around with the C++ programming language

Improving on: Dr. Dobbs - Associate Mutexes with Data to Prevent Races

[29th May 2010]

Herb Sutter has recently posted the next installment in his Effective Concurrency series. This time he outlines a technique to associate data with a particular mutex. The idea is to ensure that race conditions are eliminated by construction.

It's a very nice technique, but I think we can go a little bit further in shoring-up the protection mechanism. In Herb's example code, there are run time assertions that determine whether the associated mutex is locked prior to accessing the data.

If we re-phrase the code, we can actually ensure that at compile time.

For example, let's say we have a class, my_data which holds all the data associated with an invariant that a particular mutex is protecting. We can create guardian and guardian_lock classes that would be used something like this:

struct my_data
{
    std::size_t number_of_lizards;
    my_data() : number_of_lizards(0) { }
};

int main()
{
    // guardian object contains a private my_data object
    guardian<my_data> dat;


    // We can access the my_data object by creating a guardian_lock.
    // As long as the guardian_lock exists, a mutex is held inside
    // the guardian.
    {
       guardian_lock<my_data> lock(dat);
       lock->number_of_lizards = 12;
       std::cout << lock->number_of_lizards << '\n';
    }
    
    return 0;
}

The implementation is actually very simple:

#include <boost/thread/mutex.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/remove_const.hpp>

template<typename T>
class guardian_lock;

template<typename T>
class guardian
{
    public:
        guardian(const T &initial = T()) : guarded_(initial) { }

    private:
        friend class guardian_lock<T>;
        friend class guardian_lock<const T>;

        T guarded_;
        mutable boost::mutex mtx_; // has to be lockable if guarded_ is const
};


template<typename T>
class guardian_lock
{
    public:
        typedef typename boost::mpl::if_<
            boost::is_const<T>,
            const guardian<typename boost::remove_const<T>::type>,
            guardian<T>
        >
        ::type guardian_type;

        explicit guardian_lock(guardian_type &g) : g_(g) { g_.mtx_.lock(); }
        ~guardian_lock() { g_.mtx_.unlock(); }

        T &get() const { return g_.guarded_; }
        T *operator-> () const { return &g_.guarded_; }

    private:
        guardian_type &g_;
};

The only tricky bit is related to constness; we need to ensure that for const T, a guardian_lock<T> holds a reference to a const guardian<T>, but a quick application of boost::mpl::if_ helps us around that rather nicely.

You'll note that there are no asserts or run time checks anywhere to be found.

It's still susceptible to the following problem that Herb mentioned in his article:

guardian<my_data> dat;

std::size_t *lizards = 0;
{
    guardian_lock<my_data> lock(dat);
    lizards = &lock->number_of_lizards;
}

*lizards += 1; // oops, no longer holding the lock, potential race

But I don't think there's any sensible way around this, so just don't do that!

Downloads

Comments

Vagn Johansen

[30/05/2010 at 09:12:27]

You have also talking some steps backwards.

Sutter code wrapped the member variables with accessor function
so additional checks could be performed.

Edd

[30/05/2010 at 12:57:31]

Hi Vagn.

Which additional checks are you referring to? If you mean the "assert(mut_.is_held())", then there's no need for those here; if you can access the protected object, then that implies (by construction) that a lock on the associated mutex is held.

But perhaps I haven't understood what you mean?

Fabio

[12/01/2011 at 10:29:30]

Hi Edd,

Very interesting even if I would change:

   guardian(const T &initial = T()) : guarded_(initial) { }

with

   guardian() { }
   guardian(const T &initial) : guarded_(initial) { }

so I can protect a noncopyable class such as:

struct my_data : boost::noncopyable
{
}

Edd

[13/01/2011 at 01:15:44]

Indeed, that's a nice addition.

Gabriel

[27/09/2012 at 13:41:09]

Variation on this (developped independently a year later) : http://codereview.stackexchange.com/questions/15632/force-locking-for-thread-safety

(optional)
(optional)
(required, hint)

Links can be added like [this one -> http://www.mr-edd.co.uk], to my homepage.
Phrases and blocks of code can be enclosed in {{{triple braces}}}.
Any HTML markup will be escaped.