Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2005-04-29 08:11:28

On Apr 28, 2005, at 11:58 PM, Matt Hurd wrote:

>> Howard Hinnant <hinnant_at_[hidden]> wrote:
>>> On Apr 28, 2005, at 9:31 PM, Michael Glassford wrote:
>>> What happens when you request a shared lock from a non-shared mutex?
>>> It just gives you an exclusive lock?
>> Imho, a compile time error.
> I disagree, which suggests perhaps it should be configurable to make
> us both happy.
>> A mutex has certain capabilities. Some mutexes may support shared
>> access, some not. If a shared lock tries to "lock" a mutex, that
>> means
>> that it is trying to tell the mutex to "lock_sharable". If the mutex
>> doesn't support that function, it should complain and loudly.
> I can see performance issues, but I fail to see a correctness issue.
> Do you have an example?
> I can imagine a failure on code that requires concurrent shared access
> from two or more threads, but I've never seen such code.

The best I can do for now is rather vague:

Imagine a system where there is nothing but readers, but one specific
reader needs to occasionally promote itself to exclusive access for a
write. This specific reader is always either in write mode or read
mode, never completely releasing the lock. All other readers are
sometimes in read mode, and sometimes not holding a lock.

The above system will work fine as long as all of the sharable locks
really are sharable. Specifically, if the one promotable lock is
really holding an exclusive lock in read mode (by mistake), then all
other reader threads are permanently locked out of the resource.

It could be that I misunderstood the original question. In an attempt
to clarify...

A homogenous (generic) interface is achieved at the lock level. That
is, sharable_lock has a member function called lock() which calls
lock_sharable() on the underlying mutex. A scoped (or exclusive) lock
has a member function called lock() which calls lock() (or
lock_exclusive()) on the underlying mutex.

Code that is generic in locks, for example:

// atomically lock two locks (without fear of deadlock)
template <class TryLock1, class TryLock2>
    void lock(TryLock1& l1, TryLock2& l2);

will work whether the two locks are exclusive, sharable, promotable, or
some mix, as they all share the same generic interface (lock(),
try_lock(), etc.).

So in a sense, if mutexes retain a heterogeneous interface,
implementing only what they can truly deliver, and locks wrap the mutex
with a homogenous interface, then we really do have the choice you
speak of.

>> PS: Here is my wishlist of mutex capabilities. But this list isn't
>> meant to imply that all mutexes must support all of these
>> capabilities.
>> Locks should be able to pick and choose what they need out of a
>> mutex,
>> using only a subset of this functionality when appropriate. A lock
>> templated on a mutex will only require those mutex capabilities
>> actually instantiated by the lock client.
>> threads_move.html#Summary%20of%20mutex%20operations
> I'll have a look. It is certainly impressive looking. There is a lot
> of capability I've never needed there...

It is really nothing more than:

> This gives you a substitutable taxonomy of
> null->exclusive->shareable->promotable.

except null is missing. I've never used null, except when I throw a
master switch that says: Ok, everybody out of the pool except one guy!
(single thread mode and everything becomes a null lock) :-)

I'm also suggesting lock transfers therein, but using the syntax of
move semantics suggested in the committee papers. Boost could use that
syntax, but it would be much more of a pain to support in the current
language, and a few minor things still wouldn't work, for example:

template <class Lock, class Mutex>
source_lock(Mutex& m)
     Lock<Mutex> lock(m);
     // do something with lock here
     // ...
     return lock;

That is, the above code is illegal today assuming locks aren't
copyable. But legal tomorrow assuming that locks are movable.

However, boost could make syntax like this work:

upgradable_lock read_lock(mut); // mut read locked
scoped_lock write_lock(move(read_lock)); // mut promoted to write lock

It would involve auto_ptr_ref-like tricks in the locks. For example
(of such tricks applied to smart pointers):


Boost list run by bdawes at, gregod at, cpdaniel at, john at