Boost logo

Boost :

Subject: Re: [boost] [threads] adopt_lock_t question
From: Anthony Williams (anthony.ajw_at_[hidden])
Date: 2008-11-03 05:33:18


Jens Finkhäuser <jens_at_[hidden]> writes:

> Hi!
>
> On Mon, Nov 03, 2008 at 08:00:08AM +0000, Anthony Williams wrote:
>> Jens FinkhÀuser <jens_at_[hidden]> writes:
>>
>> > Hi!
>> >
>> > I've come across something that puzzles me. I've got code that,
>> > condensed to relevant parts, looks like this:
>> >
>> > recursive_mutex m;
>> > lock_guard<recursive_mutex> l1(m);
>> > lock_guard<recursive_mutex> l2(m, adopt_lock_t());
>>
>> That is an error. adopt_lock_t means that l2 takes ownership of the
>> already-locked mutex (i.e. doesn't lock it again), but it doesn't
>> force l1 to relinquish ownership, so both l2 and l1 think they own the
>> one and only lock. When l2 is destroyed it will unlock the mutex, and
>> then when l1 is destroyed it will unlock the mutex AGAIN, which is
>> undefined behaviour because it doesn't own the mutex at this point.
>>
>> <snip/>
>
> Thanks for your reply. I understand your argument, and what the code
> *does*, just not the reasoning: how then is adopt_lock_t *supposed* to
> work? It's not as if you can tell lock_guard to relinquish ownership of
> a mutex.

No, you can't. If you need to do that, use unique_lock and the
release() member function.

> If there is no need to adopt a mutex because there's only one lock
> in the code, adopt_lock_t is superfluous. If there are two locks
> involved, you'll always run into an issue where one lock gets to
> unlock the mutex first - and the other one fails.

Yes. Don't do that.

adopt_lock_t is for where you've locked the mutex *directly* using
mutex.lock(), or where you've acquired a locked mutex from a
unique_lock using unique_lock::release().

It works quite well with the boost::lock free functions:

boost::mutex m1,m2;
boost::lock(m1,m2); // lock both mutexes
boost::lock_guard<boost::mutex> l1(m1,boost::adopt_lock);
boost::lock_guard<boost::mutex> l2(m2,boost::adopt_lock);

In this case, you can also use unique_lock with the defer_lock
constructor:

boost::unique_lock<boost::mutex> l1(m1,boost::defer_lock);
boost::unique_lock<boost::mutex> l2(m2,boost::defer_lock);
boost::lock(l1,l2); // lock both mutexes

Anthony

-- 
Anthony Williams
Author of C++ Concurrency in Action | http://www.manning.com/williams
Custom Software Development | http://www.justsoftwaresolutions.co.uk
Just Software Solutions Ltd, Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK

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