Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2007-12-18 13:53:58


On Dec 18, 2007, at 1:24 PM, Frank Mori Hess wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On Tuesday 18 December 2007 11:48 am, Howard Hinnant wrote:
>> That being said, a non-movable boost::mutex does not prohibit a
>> wrapper around that type from being movable, if you're willing to
>> accept alternative semantics. I'm not familiar with the boost move
>> library, but the C++0X syntax would simply be:
>>
>> class my_wrapper
>> {
>> std::mutex mut_; // or boost::mutex
>> public:
>> my_wrapper() {}
>> my_wrapper(my_wrapper&&) {} // same as default ctor
>> my_wrapper& operator=(my_wrapper&&) {return *this;} // do
>> nothing
>>
>> void lock() {mut_.lock();}
>> bool try_lock() {return mut_.try_lock();}
>> void unlock() {mut_.unlock();}
>> };
>
> What I'm thinking of is more along the lines of
>
> template<typename Mutex>
> class my_wrapper
> {
> Mutex mut_;
> public:
> my_wrapper(Mutex &&mut): mut_(mut);
> // ...
> };
>
> So I can have a my_wrapper<Mutex> without worrying about whether the
> template
> Mutex type has some weird constructor, or even if it is default
> constructible. I guess the best I could do along those lines is to
> have a
> constructor that allows the user to pass in a dynamically allocated
> Mutex,
> which the wrapper would take ownership of. Note, I've never really
> done
> anything with rvalue references or moveable types, so maybe this is
> an abuse
> of them?

This sort of sounds like the proposed std::unique_lock<Mutex> (which
Anthony has generously prototyped on the boost trunk):

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2447.htm#MutexsLocksWording

template <class Mutex>
class unique_lock
{
public:
     typedef Mutex mutex_type;
private:
     mutex_type* m_;
     bool owns_lock_;

     unique_lock(unique_lock const&) = delete;
     unique_lock& operator=(unique_lock const&) = delete;

public:
     ...
     explicit unique_lock(mutex_type& m)
         : m_(&m),
           owns_lock_(true)
     {
         m_->lock();
     }
     ...
     ~unique_lock()
     {
         if (owns_lock_)
             m_->unlock();
     }

     unique_lock(unique_lock&& u) // transfers mutex lock ownership
to this
         : m_(u.m_),
           owns_lock_(u.owns_lock_)
     {
         u.m_ = 0;
         u.owns_lock_ = false;
     }

     unique_lock& operator=(unique_lock&& u) // transfers mutex lock
ownership to this
     {
         if (owns_lock_)
             m_->unlock();
         owns_lock_ = u.owns_lock_;
         m_ = u.m_;
         u.owns_lock_ = false;
         u.m_ = 0;
         return *this;
     }

     void lock();
     bool try_lock();
     ...
};

This is slightly differs from what you suggest in that the lifetime of
the Mutex isn't managed, but only the lock ownership. But the
"wrapper" is moveable. Use cases might look like:

std::mutex mut; // mutex constructed and destructed with static
storage duration

std::unique_lock<std::mutex>
get_a_lock()
{
     // might do something more complex in here besides
     // simply returning the lock
     return std::unique_lock<std::mutex>(mut); // lock mut and return
a wrapper to it
}

void foo()
{
     std::unique_lock<std::mutex> the_lock = get_a_lock(); // movable
lock allows returning from factory function
     // mut locked in here
} // mut unlocked here

Some of this is C++0X, like the ability to return movable but non-
copyable items from factory functions. Is this getting close to what
you were wanting to do?

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk