$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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