
Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams wrote:
Here's a win32-only static once_init implementation. This could be used as a general purpose static-init template, or as a basis for making boost::mutex work both as a static or non-static object, with a default constructor.
Comments please,
Anthony, I did not yet have had the time to walk through all details of your proposal, but the main problem why this isn't thread safe is:
void f() { static once_init<A> a; a->f();
}
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. On MSVC 7.1, the compiler will call the constructor in the first thread that starts the function, and not any others. It will also not wait for the constructor to finish. For objects of static storage duration, the memory is initialized to 0 upon startup. Since the constructor doesn't read the data, and only sets it with the final InterlockedExchange, the constructor doesn't care what the initial data value is. However, the lazy_init does care --- it uses an interlocked read to ensure that it correctly reads the value in memory. If it is non-zero, then the constructor must have run already, else we're racing against the constructor, which is running in another thread. If the constructor has run, we're fine. If we're racing, we run the same init routine as the constructor, which is designed to only run once. The init routine tries to create a manual-reset event object, initially in the non-signalled state. This is a named object, so the same one is returned from every call, but GetLastError() returns ERROR_ALREADY_EXISTS if it already exists. If it already exists, we wait for it, else we're the first thread to run the init routine (we won the race), so we actually do the initialization, store the event handle for use by the destructor, and as the flag that construction is done, and signal the event, so the other racing threads can resume, safe in the knowledge that everything is fine. For the destructor, the thread that actually runs the constructor will schedule a destructor call with atexit (on MSVC), so the destructor runs once, too. I should have explained the logic when I posted it, but I was short of time. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk