Is it safe to move boost::unique_lock out of a function as a return value using move emulation on C++03 compiler?

39 views Asked by At

I have a following moveable but not copyable class that can be used to syncronize access to some shared resources:

class wrapper_with_lock{
    private:
        BOOST_MOVABLE_BUT_NOT_COPYABLE(wrapper_with_lock)
        boost::unique_lock l;
    public:
        int* data1;//These point to data that needs to have synchronized access
        char* data2;//....
        wrapper_with_lock(boost::mutex& m) : l(m){}//Constructor acquires the lock
        wrapper_with_lock(BOOST_RV_REF(wrapper_with_lock) x) {
            l = boost::move(x.l);//Move the lock
            data1 = x.data1;//Move the pointers
            x.data1 = 0;
            ....
        }
        wrapper_with_lock& operator=(BOOST_RV_REF(wrapper_with_lock) x) // Move assign
        {
            if (this != &x){
                l = boost::move(x.l);//Move the lock and other data
                ....
            }
            return *this;
        }
}

The idea here being that this structure can be moved around, holding the mutex and it automatically frees the lock after going out of scope. The intended usage is as follows:

wrapper_with_lock do_some_init(boost::mutex& m){
    wrapper_with_lock w(m);
    *(w.data1) = 1234;//Do something initially with the data etc...
    //Return the lock holding object by moving it (should move the internal lock).
    //The lock should be valid and properly moved to the caller
    //of this function inside the wrapper
    return boost::move(w);
}

The question is that is this wanted behaviour of moving the lock guaranteed when using the move emulation of boost library with a C++03 compiler that we are stuck with in this project? The old compiler does not have any support for the newer standards.

1

There are 1 answers

2
sehe On BEST ANSWER

Yes. Here's how you can verify that yourself:

Live On Coliru

#include <boost/move/move.hpp>
#include <boost/thread.hpp>
#include <boost/thread/locks.hpp>

class wrapper_with_lock {
  private:
    BOOST_MOVABLE_BUT_NOT_COPYABLE(wrapper_with_lock)
    boost::unique_lock<boost::mutex> l;

  public:
    int*  data1; // These point to data that needs to have synchronized access
    char* data2; //....

    ~wrapper_with_lock() {
        delete[] data1;
        delete[] data2;
    }

    wrapper_with_lock(boost::mutex& m)
        : l(m)
        , data1(new int[1024])
        , data2(new char[1024]) {} // Constructor acquires the lock

    wrapper_with_lock(BOOST_RV_REF(wrapper_with_lock) x) {
        bool was_locked = x.locked();
        assert(was_locked); // actually invariant of wrapper_with_lock?

        l = boost::move(x.l); // Move the lock

        assert(!x.locked());
        assert(was_locked == locked());

        data1   = x.data1;          // Move the pointers
        x.data1 = 0;
        data2   = x.data2;          // Move the pointers
        x.data2 = 0;
    }

    bool locked() const { return l.owns_lock(); }

    wrapper_with_lock& operator=(BOOST_RV_REF(wrapper_with_lock) x) // Move assign
    {
        if (this != &x) {
            bool was_locked = x.locked();
            assert(was_locked);   // actually invariant of wrapper_with_lock?

            l = boost::move(x.l); // Move the lock and other data

            assert(!x.locked());
            assert(was_locked == locked());
            //....
            data1   = x.data1; // Move the pointers
            x.data1 = 0;
            data2   = x.data2; // Move the pointers
            x.data2 = 0;
        }
        return *this;
    }
};

wrapper_with_lock do_some_init(boost::mutex& m){
    wrapper_with_lock w(m);
    *w.data1 = 1234; // Do something initially with the data etc...

    // Return the lock holding object by moving it (should move the internal lock).
    // The lock should be valid and properly moved to the caller
    // of this function inside the wrapper
    return boost::move(w);
}

int main() {
    boost::mutex m;
    wrapper_with_lock l = do_some_init(m);
    assert(l.locked());
}

Do look at these though: Locking Pointers and Synchronized Values