|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2004-07-22 09:22:35
On Jul 22, 2004, at 7:54 AM, Peter Dimov wrote:
> Howard Hinnant wrote:
>>
>> I don't dislike these helper functions. But I do dislike the idea
>> that
>> they should be the only way to construct-and-try or
>> construct-with-time. The problem is that we have 3 different types of
>> locks, and I want to be able to construct-and-try in a generic-lock
>> function:
>>
>> template <class Lock>
>> void
>> foo()
>> {
>> Lock lk(m, try_lock);
>> ...
>> }
>>
>> The above code should work for scoped_lock, sharable_lock and
>> upgradable_lock.
>
> template<class Lock> void foo()
> {
> bool const deferred = false;
>
> Lock lock( m, deferred );
>
> if( lock.try_lock() )
> {
> // locked
> }
> }
>
> as usual. Clean separation between lock type (scoped/exclusive,
> shareable,
> upgradable) and lock operations (unconditional blocking lock, try lock,
> timed lock, unlock).
Ok. By that reasoning the try_lock(Mutex&) helper function is also
unnecessary. The ability to construct an unlocked lock is fundamental,
and everything else can be built in terms of that, including locked and
timed-lock constructors/helpers. But the other constructors / helpers
are convenient. Let's review:
// no try-ctor, no try-helper func
template <class Lock>
void
foo1()
{
Lock lk(m, false);
if (lk.try_lock())
...
}
// use a try_lock helper func
template <class Lock>
void
foo2()
{
if (Lock lk = try_lock(m))
...
}
Notation is a little more compact, but it only works for whatever type
Lock that try_lock() returns (presumably scoped_lock).
// use a generic try_lock helper func
template <class Lock>
void
foo3()
{
if (Lock lk = try_lock<Lock>(m))
...
}
This is now generalized to work for all Lock types.
// use a try-lock ctor
template <class Lock>
void
foo4()
{
if (Lock lk = Lock(m, try_lock))
...
}
This also works for all Lock types.
Solution 1 is slightly less efficient than solutions 3 and 4 (solution
2 isn't really a solution). The reason is that foo1 expands to:
m_ = m;
locked_ = lock_it;
if (locked_)
m_.lock();
locked_ = m_.try_lock();
if (locked_)
...
I.e. the Lock(m, bool) constructor checks the value of the bool to
decide whether to lock or not.
In contrast, solutions 3 and 4 look more like:
m_ = m;
locked_ = m_.try_lock();
if (locked_)
...
I.e. no attempt is made to call m_.lock().
Actually solution 3 will only be this efficient if the try_lock helper
function doesn't construct its Lock via the code shown in foo1, but
instead uses something equivalent to the Lock(m, try_lock) constructor.
So 3 and 4 look the best to me, both in terms of readability, and in
terms of more optimized code (size and speed). And it seems like the
best way to efficiently implement 3 is by using the same constructor
that 4 uses. If you follow up to this point, then given 4 exists
anyway, is 3 worth the trouble?
-Howard
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk