Boost logo

Boost :

From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2007-08-26 14:39:44


On Aug 26, 2007, at 10:20 AM, David Abrahams wrote:

>
> on Fri Aug 24 2007, Howard Hinnant <howard.hinnant-AT-gmail.com>
> wrote:
>
>>> 2. I think the concept of unique_lock is too fuzzy. I know what
>>> unique_ptr (and auto_ptr, and shared_ptr, and scoped_ptr) mean.
>>> With unique_lock, I can't quite tell. This might ultimately end up
>>> being fixed by a naming change, but I think there's an underlying
>>> conceptual problem, or at the very least, a missing rationale.
>>
>> I will try to better fill out the rationale. And am certainly open
>> to
>> a name change.
>>
>> In a nutshell, the current unique_lock<Mutex> is an evolution of the
>> current boost::detail::thread::scoped_lock<Mutex>. It has been taken
>> out of namespace detail, renamed, given move semantics, and slightly
>> more flexibility in how it handles the mutex. The only invariant
>> that
>> has changed from the boost design is that one can have a unique_lock
>> which doesn't reference a mutex. This was a consequence of a moved-
>> from unique_lock.
>
> That's not the rationale I'm looking for. I don't mean "how did we
> get here?" I mean, what *is* this thing, conceptually?

Conceptually a unique_lock is the sole RAII owner of the exclusive
lock state of an object that meets certain Mutex concepts. At a
minimum, the Mutex must support:

    void unlock();

Otherwise the unique_lock can not be destructed. If the
unique_lock<Mutex>(Mutex&) constructor is instantiated, or if
unique_lock<Mutex>::lock() is instantiated, then Mutex must support:

    void lock();

If the unique_lock<Mutex>(Mutex&, try_lock_type) constructor is
instantiated, or if unique_lock<Mutex>::try_lock() is instantiated,
then Mutex must support:

    bool try_lock();

If the unique_lock<Mutex>(Mutex&, nanoseconds) constructor is
instantiated, or if unique_lock<Mutex>::timed_lock(nanoseconds) is
instantiated, then Mutex must support:

    bool timed_lock(nanoseconds);

If the unique_lock<Mutex>(tr2::upgrade_lock<Mutex>&&) constructor is
instantiated, then Mutex must support:

    void unlock_upgrade_and_lock();

If the unique_lock<Mutex>(tr2::upgrade_lock<Mutex>&&, try_lock_type)
constructor is instantiated, then Mutex must support:

    bool try_unlock_upgrade_and_lock();

If the unique_lock<Mutex>(tr2::upgrade_lock<Mutex>&&, nanoseconds)
constructor is instantiated, then Mutex must support:

    bool timed_unlock_upgrade_and_lock(nanoseconds);

If the unique_lock<Mutex>(tr2::shared_lock<Mutex>&&, try_lock_type)
constructor is instantiated, then Mutex must support:

    bool try_unlock_shared_and_lock();

If the unique_lock<Mutex>(tr2::shared_lock<Mutex>&&, nanoseconds)
constructor is instantiated, then Mutex must support:

    bool timed_unlock_shared_and_lock(nanoseconds);

> What can I
> understand about a function that accepts one as an argument (for
> unique_ptr it's very clear, and exceedingly so for scoped_lock, since
> you can't do that)?

To accept a unique_lock (which currently owns the exclusive state) by
value in a function parameter means transferring the ownership of that
exclusive lock state from the current scope, to the scope within the
called function.

> What does it mean to return one from a function
> or store it in a container?

It means to transfer the sole ownership of the exclusive lock state
from within the function, to the calling scope.

> I understand what scoped_lock means. If unique_lock doesn't mean "no
> ownership or sole ownership of the mutex it references" then what does
> it mean, and what's the justification for calling it "unique?"

unique_lock means "no ownership or sole ownership of the mutex it
references".

I think your concern began with my std::lock example:

On Aug 22, 2007, at 10:36 AM, David Abrahams wrote:

> on Tue Aug 21 2007, Howard Hinnant <howard.hinnant-AT-gmail.com>
> wrote:
>
>> This line:
>>
>> unique_lock<_L1> __u1(__l1);
>>
>> implicitly calls __.l1.lock() inside of the unique_lock constructor.
>> If __l1 is a mutex, the deed is done. If __l1 is a lock, hopefully
>> that will forward to the referenced mutex's lock() function in the
>> proper manner. And in the process, that should set the lock's owns()
>> data to true as well.
>
> That's part of what I found counfounding about the name "unique_."
> Now you have two locks (__l1 and __u1) that "own" the mutex.

__u1 now uniquely owns the exclusive lock state of __l1. __u1 does
not know or care what it means for __l1 to be in an exclusive locked
state. It only knows that it uniquely owns it. That is, until
further notice, no other object in the program has executed
__l1.lock(), or __l1.try_lock() (or any of the other functions which
can put a mutex into a exclusive locked state) without having already
executed __l1.unlock(). Anything that happens inside of __l1 when
__l1 is in its exclusively locked state is an implementation detail of
__l1, which __u1 is not privy to.

-Howard


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