# Boost :

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

On Aug 27, 2007, at 12:13 AM, Gottlob Frege wrote:

> a unique_lock references an objet, and has exclusive ownership of its
> locked state. Slightly different. And 'ref' != 'own'. Or 'own' is
> ambiguous between owning the object and owning the state of the
> object.

Not disagreeing with you at all. Below is my attempt at a
clarification of the difference between referencing a mutex, and
holding the lock on the mutex, and how the current syntax and
semantics behaves with respect to those two states. Hopefully what is
below *is* a clarification, and not further confusion.

In the current syntax:

A
unique_lock<Mutex> lk;

references a mutex if:

lk.mutex() != 0

and owns/holds the lock if:

lk.owns() == true

An invariant is that if the lock does not reference a mutex:

lk.mutex() == 0

then lk.owns() will be false.

The rationale for allowing lk.mutex() to become null is two-fold:

1. A unique_lock default constructor could be handy, say for new'ing
an array of unique_lock. One can always move assign a default
constructed unique_lock to give it a reference to a mutex:

mutex mut;
unique_lock<mutex> lk;
assert(lk.mutex() == 0);
assert(!lk.owns());
lk = unique_lock<mutex>(mut);
assert(lk.mutex() != 0);
assert(lk.owns());
unique_lock<mutex> other(std::move(lk));
assert(lk.mutex() == 0);
assert(!lk.owns());

2. A move construct, and move assign from a unique_lock leaves the
source referencing no mutex. We could have left the source
referencing the mutex, but just not owning the lock. However that
would be slightly more error prone. For example:

void bar(unique_lock<mutex> lk); // bar accepts lock ownership

void foo()
{
unique_lock<mutex> lk(mut, defer_lock);
// Normally when you can assert that
// you are referencing a mutex *and*
// you do not own the locked state of the mutex, then...
assert(lk.mutex() != 0)
assert(!lk.owns());
// ... it is safe to lock:
lk.lock();
// However if move construction did not drop the reference ...
bar(move(lk));
// ... then the following asserts would both be true but
assert(lk.mutex() != 0)
assert(!lk.owns());
// ... locking the lock is no longer safe
lk.lock(); // probable error
// This thread may still own the lock on mut.
// bar() could have passed ownership to a global,
// or to some object member data.
// Therefore it is best if ...
assert(lk.mutex() == 0);
// ... after the call to bar()
}

-Howard