Boost logo

Boost :

From: Raymond Haeb (ray.haeb_at_[hidden])
Date: 2006-10-08 20:05:13


Anthony Williams wrote:
> Roland Schwarz <roland.schwarz_at_[hidden]> writes:
>
>> Anthony Williams wrote:
>>> Roland Schwarz <roland.schwarz_at_[hidden]> writes:
>>>> class once_init does have a user declared constructor and a destructor!
>>>> And this definitely is _not_thread safe.
>>> Yes, agreed. That's why the operator-> also does a lazy_init().
>>>
>>> This is completely platform- and implementation-dependent code, precisely the
>>> sort of thing that needs to be wrapped in a library, and not written by the
>>> general public.
>> I am not convinced yet, that this code is thread safe even on MSVC:
>> You can happen to have multiple ctor calls waiting on
>> WaitForSingleObject(event,BOOST_INFINITE);
>> in init_once, true?
>
>> Then when the the event is signalled (after the object has been
>> constructed) all pending ctors (possibly just having waited)
>> return. Now if you look at the assembly code level, you will
>> see that each will schedule a call to atexit, which makes your
>> code end up with multiple calls to dtor. This is bad. isn't it?
>> (Not to mention, that I think I found out that the call to atexit
>> itself isn't thread safe :-(, at least for posix, don't know
>> for MSVC yet.)
>
> Only the thread(s) that runs the constructor will schedule atexit.

But at least in:

void f()
{
    static once_init<A> a;
    a->f();
}

both threads (may) call the contructor of once_init<A>. So it is
possible, that the destructor of once_init<A> is scheduled multiple
times for atexit. Or even atexit may be called multiple times
simultaneously. And with:

~once_init()
{
   (*this)->~T();
   CloseHandle(init_event);
}

~T() also.

> Anyway, I'm sure this is fine if the destructor is changed to:
>
> ~once_init()
> {
> if(init_event)
> {
> (*this)->~T();
> CloseHandle(init_event);
> init_event=0;
> }
> }

Not really, the compiler is allowed to rearrange anything in it as long
as the observable behavior (according to a single threaded machine) is
the same. Observable are only the calls to "library I/O functions" and
accesses to volatile data. This means, that the compiler is allowed to
rearrange anything inside the if clause as long as CloseHandle is called
with the right value (and the other IO functions inside ~T() are called
in the right order and with the right parameters). And even if you make
init_event volatile it does not change much: init_event=0 is after
CloseHandle but not necessarily after ~T().

Raymond Häb


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