Boost logo

Boost :

From: Anthony Williams (anthony_w.geo_at_[hidden])
Date: 2006-10-07 14:11:12


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

-- 
Anthony Williams
Software Developer
Just Software Solutions Ltd
http://www.justsoftwaresolutions.co.uk
//////////
#include <cstring>
#include <cstddef>
#include "boost/aligned_storage.hpp"
#include "boost/thread/win32/interlocked_read.hpp"
#include <boost/static_assert.hpp>
#include <boost/thread/win32/thread_primitives.hpp>
#include <boost/detail/interlocked.hpp>
#include "boost/thread/mutex.hpp"
#include <windows.h>
#include <iostream>
#include "boost/thread/thread.hpp"
#ifdef BOOST_NO_STDC_NAMESPACE
namespace std
{
    using ::strlen;
    using ::memcpy;
    using ::ptrdiff_t;
}
#endif
template<typename T>
struct once_init
{
    once_init()
    {
        init_once();
    }
    ~once_init()
    {
        (*this)->~T();
        CloseHandle(init_event);
    }
    
    void lazy_init()
    {
        if(!::boost::detail::interlocked_read(&init_event))
        {
            init_once();
        }
    }
    T* operator->()
    {
        lazy_init();
        return reinterpret_cast<T*>(data.address());
    }
    
private:
    void* init_event;
    boost::aligned_storage<sizeof(T)> data;
    BOOST_STATIC_CONSTANT(unsigned,name_fixed_length=48);
    BOOST_STATIC_CONSTANT(unsigned,event_name_length=name_fixed_length+sizeof(void*)*2+sizeof(unsigned long)*2+1);
    template <class I>
    void int_to_string(I p, char* buf)
    {
        unsigned i=0;
        for(; i < sizeof(I)*2; ++i)
        {
            buf[i] = 'A' + static_cast<char>((p >> (i*4)) & 0x0f);
        }
        buf[i] = 0;
    }
    void create_event_name(char (&name)[event_name_length])
    {
        static const char fixed_name[]="{F21E1417-EF70-4665-9758-5DBC70AE2FCC}-init-flag";
        BOOST_STATIC_ASSERT(sizeof(fixed_name) == (name_fixed_length+1));
        
        std::memcpy(name,fixed_name,sizeof(fixed_name));
        BOOST_ASSERT(sizeof(name) == std::strlen(name) + sizeof(void*)*2 + sizeof(unsigned long)*2 + 1);
        
        BOOST_STATIC_ASSERT(sizeof(void*) == sizeof(std::ptrdiff_t));
        int_to_string(reinterpret_cast<std::ptrdiff_t>(this), name + name_fixed_length);
        int_to_string(GetCurrentProcessId(), name + name_fixed_length + sizeof(void*)*2);
        BOOST_ASSERT(sizeof(name) == std::strlen(name) + 1);
    }
    void init_once()
    {
        char event_name[event_name_length];
        create_event_name(event_name);
        
        void* const event=CreateEventA(NULL,true,false,event_name);
        
        BOOST_ASSERT(event);
        if(GetLastError()==ERROR_ALREADY_EXISTS)
        {
            WaitForSingleObject(event,BOOST_INFINITE);
        }
        else
        {
            new(reinterpret_cast<T*>(data.address())) T();
            BOOST_INTERLOCKED_EXCHANGE_POINTER(&init_event,event);
            SetEvent(event);
        }
        
    }
};
struct A
{
    A()
    {
        std::cout<<GetCurrentThreadId()<<"initialize "<<this<<std::endl;
        Sleep(1000);
    }
    ~A()
    {
        std::cout<<GetCurrentThreadId()<<"destroy "<<this<<std::endl;
    }
    void f()
    {
        std::cout<<GetCurrentThreadId()<<"f "<<this<<std::endl;
    }
    
    int i;
};
void f()
{
    static once_init<A> a;
    a->f();
    
}
int main()
{
    boost::thread t1(f);
    boost::thread t2(f);
    t2.join();
    t1.join();
}

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