Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2004-07-15 21:30:37


On Jul 15, 2004, at 7:42 PM, Matt Hurd wrote:

> I'm not sure I see the case for deferring anything a stack based
> scoped lock with a:
> void folly ()
> {
> {
> scoped_lock l(m);
> do_stuff();
> }
>
> {
> scoped_lock l(m);
> do_more_stuff();
> }
> }
> is still preferable to me than allowing explicit locking of a lock.

I have a couple of places where I need a "deferred" lock: Namely I
have 2 generic templated lock classes that take other locks as template
parameters, and store references to those locks:

template <class Lock1, class Lock2> class transfer_lock;
template <class Lock1, class Lock2> class lock_both;

The constructor for transfer_lock takes two locks, referencing the same
mutex, the first of which must be unlocked, and the second locked. The
constructor for lock_both takes two locks, referencing different
mutexes, both of which must be unlocked.

Example use:

mutex m;
scoped_lock lock1(m1, deferred); // or whatever syntax for not locking
scoped_lock lock2(m2, deferred);
lock_both<scoped_lock, scoped_lock> lock(lock1, lock2);
// m1 and m2 atomically locked here, without fear of deadlock

Without the ability to construct but defer locking of lock1 and lock2,
the construction of lock_both becomes inefficient and awkward.

> I worry about paying for the overhead, perhaps in space for a scoped
> time lock that records it timing versus a blocking lock that needs no
> such information, but I'm not sure that it is a big deal as the
> difference is negligible and these will typically be stack based.
> Implementation will reveal all I guess.

There is no overhead in the lock types, at least with the current boost
design. All locks I've coded contain a reference to the mutex, and a
bool indicating locked status (even the read_lock, write_lock and
upgradable_read_lock). But there is overhead in the mutex types, and
it can vary significantly among the different types: (basic, try,
timed, recursive, read/write). Sometimes the overhead is on the stack,
sometimes not (depends on the OS and on the mutex implementation).

Here is a sample survey of sizeof() on one of my platforms I need to
support:

sizeof(mutex) = 44
sizeof(try_mutex) = 44
sizeof(timed_mutex) = 76
sizeof(recursive_mutex) = 44
sizeof(recursive_try_mutex) = 44
sizeof(recursive_timed_mutex) = 80
sizeof(rw_mutex) = 108

On another platform it looks like:

sizeof(mutex) = 44
sizeof(try_mutex) = 44
sizeof(timed_mutex) = 76
sizeof(recursive_mutex) = 80
sizeof(recursive_try_mutex) = 80
sizeof(recursive_timed_mutex) = 80
sizeof(rw_mutex) = 108

On a third platform I support there are differences between mutex and
try_mutex, but the difference is not well illustrated by sizeof()
because the "mutex type" is just a 4 byte handle in some cases (but not
others).

sizeof(mutex) = 24
sizeof(try_mutex) = 4
sizeof(timed_mutex) = 4
sizeof(recursive_mutex) = 24
sizeof(recursive_try_mutex) = 4
sizeof(recursive_timed_mutex) = 4
sizeof(rw_mutex) = not implemented

And of course all of this is subject to change. But if I'm forced to
include all capability into one mutex type, then there is definitely
going to be a price paid on some platforms by those who only want a
mutex (which is also by far the most often needed type).

-Howard


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