Subject: Re: [boost] Boost sprint: #3407 boost::call_once not re-entrant (at least in win32)
From: Gottlob Frege (gottlobfrege_at_[hidden])
Date: 2009-11-23 09:46:50
On Mon, Nov 23, 2009 at 6:46 AM, Anthony Williams <anthony.ajw_at_[hidden]> wrote:
> Gottlob Frege <gottlobfrege_at_[hidden]> writes:
> Crikey that's a lot of code for a simple feature!
Yes, unfortunately. I've done it different ways in the past. I could
probably give you a version that didn't build a linked list of nodes.
But there are trade-offs.
> 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.
Yes, I've done it this way. You will have problems destroying the
event. ie knowing who and when to destroy it. I don't think leaving
the event handle leaking would be OK. You need to count it. It
really isn't *too* hard, until you run into exceptions. Then you need
to open the event and reset the once_flag. But now you need another
event for the new attempt (you can't use the same event as you are not
sure if all threads from the first attempt have even waited yet). So
where do you store the event!? Using nodes solves that.
You can also solve it by using what I call a 'branding gate' - the
gist of it at least, from memory: as each thread enters, it increments
a waiting count and/or the 'running' flag (lo/hi halves of an int,
say). But on retry (after an exception) you don't just reset the
running flag, you increment it - and 'running' becomes whether the
flag is odd or even. This way every entry or wait is 'branded' with a
unique value. You can then use the unique value to look up a named
mutex (or named event). Windows takes care of ref counting for you.
You still end up creating a global named handle, but it can be
deferred until contention is detected.
See also the discussion thread starting at
also ended in a node-based solution, although I think it used event
handles on each node (iiuc). I never added my branding gate version
to the discussion (hadn't figured it out at the time).
A branding gate version does at least look somewhat simpler. Still
not as straightforward as what you currently have.
P.S. the other version you have (for posix) using the thread-local
(based on the Google version) looks interesting as well. I think it
would be nice to incorporate that, but avoid the global lock that is
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk