Boost logo

Boost :

Subject: Re: [boost] Boost sprint: #3407 boost::call_once not re-entrant (at least in win32)
From: Anthony Williams (anthony.ajw_at_[hidden])
Date: 2009-11-23 06:46:51


Gottlob Frege <gottlobfrege_at_[hidden]> writes:

> On Sun, Nov 22, 2009 at 11:49 AM, Vicente Botet Escriba
> <vicente.botet_at_[hidden]> wrote:
>>
>> As this works as expected, Anthony could just add on the documentation why
>> reentrant call_once functions are not a good idea. The explanation of Steven
>> could be a good starting point:
>>
>> "I'll also point out that there is no good behavior for a recursive call to
>> call_once. If it calls the function again, then f will be called twice. If
>> it does not, then whatever initialization that call_once is doing may be
>> incomplete. In either case, you have to know whether a call can be recursive
>> and carefully design for it. If it happens accidentally, you're probably
>> toast regardless of how it behaves. In the face of recursive calls it is
>> impossible to maintain the post-condition that the function has run exactly
>> once."
>>
>> After this modification IMO the ticket can be closed.

Thanks Vicente, I agree. I've updated the docs.

> However, I was in the process of writing a (better?) version of
> call_once that avoided creating the system wide mutex (on Windows),
> using a process-local Event instead (and even that is not created
> unless there is contention - ie the second thread in creates the
> event).
> While tackling this, I also decided to tackle ways of handling
> exceptions (should 'f' be recalled if the first attempt threw an
> exception?), as well as optionally allowing recursion.
>
> The code is currently Windows-only, although it could be ported most anywhere.
> I'd be interested to know:
>
> - if the exception handling and/or recursion handling are worthwhile options

I don't believe so.

> and more importantly
> - if the avoidance of creating a named mutex is worthwhile (I think so)

Yes, that's a worthwhile goal; creating such a mutex is not cheap.

> Code attached. Not 100% tested yet (or 'boostified'), but I'm pretty
> sure the algorithm is sound.

Crikey that's a lot of code for a simple feature!

I would just go for adding a "running" state to the flag value. If the
flag is not "complete", set it to "running" using CAS. If we set it,
then run, otherwise create an event, store it in the once_flag object
(using CAS in case another waiter already stored an event), check the
flag is still "running" and then wait for our event. When the event is
triggered, loop again (to run the function if it threw last time).

Once the thread that runs the function has completed the function call
it can then set the flag to "complete", and set the event if there is
one. If the function throws then the flag can be set back to "not
started" and the event set if there is one.

Anthony

-- 
Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/
just::thread C++0x thread library   | http://www.stdthread.co.uk
Just Software Solutions Ltd         | http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

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