////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2009-2009. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/interprocess for documentation. // ////////////////////////////////////////////////////////////////////////////// #ifndef BOOST_INTERPROCESS_INTERMODULE_SINGLETON_HPP #define BOOST_INTERPROCESS_INTERMODULE_SINGLETON_HPP #if defined(_MSC_VER)&&(_MSC_VER>=1200) #pragma once #endif #include #include #include #ifdef BOOST_INTERPROCESS_WINDOWS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (BOOST_INTERPROCESS_WINDOWS) #include #include #include #else #include #include #include #endif namespace boost{ namespace interprocess{ namespace detail{ namespace file_locking_helpers { inline void get_pid_creation_time_str(std::string &s) { std::stringstream stream; stream << get_current_process_id() << '_'; stream.precision(6); stream << std::fixed << get_current_process_creation_time(); s = stream.str(); } inline void create_tmp_subdir_and_get_pid_based_filepath(const char *subdir_name, const char *file_prefix, OS_process_id_t pid, std::string &s, bool creation_time = false) { //Let's create a lock file for each process gmem that will mark if //the process is alive or not create_tmp_and_clean_old(s); s += "/"; s += subdir_name; if(!open_or_create_directory(s.c_str())){ throw interprocess_exception(error_info(system_error_code())); } s += "/"; s += file_prefix; if(creation_time){ std::string sstamp; get_pid_creation_time_str(sstamp); s += sstamp; } else{ pid_str_t pid_str; get_pid_str(pid_str, pid); s += pid_str; } } inline bool check_if_filename_complies_with_pid (const char *filename, const char *prefix, OS_process_id_t pid, std::string &file_suffix, bool creation_time = false) { //Check if filename complies with lock file name pattern std::string fname(filename); std::string fprefix(prefix); if(fname.size() <= fprefix.size()){ return false; } fname.resize(fprefix.size()); if(fname != fprefix){ return false; } //If not our lock file, delete it if we can lock it fname = filename; fname.erase(0, fprefix.size()); pid_str_t pid_str; get_pid_str(pid_str, pid); file_suffix = pid_str; if(creation_time){ std::size_t p = fname.find('_'); if (p == std::string::npos){ return false; } std::string save_suffix(fname); fname.erase(p); fname.swap(file_suffix); bool ret = (file_suffix == fname); file_suffix.swap(save_suffix); return ret; } else{ fname.swap(file_suffix); return (file_suffix == fname); } } } //file_locking_helpers namespace intermodule_singleton_helpers { const int GMemMarkToBeRemoved = -1; const int GMemNotPresent = -2; inline const char *get_lock_file_subdir_name() { return "gmem"; } inline const char *get_lock_file_base_name() { return "lck"; } inline void create_and_get_singleton_lock_file_path(std::string &s) { file_locking_helpers::create_tmp_subdir_and_get_pid_based_filepath (get_lock_file_subdir_name(), get_lock_file_base_name(), get_current_process_id(), s, true); } inline const char *get_shm_base_name() { return "bip.gmem.shm."; } inline void get_shm_name(std::string &shm_name) { file_locking_helpers::get_pid_creation_time_str(shm_name); shm_name.insert(0, get_shm_base_name()); } inline std::size_t get_shm_size() { return 65536; } template struct managed_sh_dependant { static void apply_gmem_erase_logic(const char *filepath, const char *filename); static bool remove_old_gmem() { std::string refcstrRootDirectory; tmp_folder(refcstrRootDirectory); refcstrRootDirectory += "/"; refcstrRootDirectory += get_lock_file_subdir_name(); return for_each_file_in_dir(refcstrRootDirectory.c_str(), apply_gmem_erase_logic); } }; #if (defined BOOST_INTERPROCESS_WINDOWS) template<> struct managed_sh_dependant { static void apply_gmem_erase_logic(const char *, const char *){} static bool remove_old_gmem() { return true; } }; struct locking_file_serial_id { int fd; unsigned long dwVolumeSerialNumber; unsigned long nFileIndexHigh; unsigned long nFileIndexLow; //This reference count counts the number of modules attached //to the shared memory and lock file. This serves to unlink //the locking file and shared memory when all modules are //done with the global memory (shared memory) volatile boost::uint32_t modules_attached_to_gmem_count; }; inline bool lock_locking_file(int fd) { int ret = 0; while(ret != 0 && errno == EDEADLK){ ret = _locking(fd, _LK_LOCK, 1/*lock_file_contents_length()*/); } return 0 == ret; } inline bool try_lock_locking_file(int fd) { return 0 == _locking(fd, _LK_NBLCK , 1); } inline int open_or_create_and_lock_file(const char *name) { permissions p; p.set_unrestricted(); while(1){ file_handle_t handle = create_or_open_file(name, read_write, p); int fd = _open_osfhandle((intptr_t)handle, _O_TEXT); if(fd < 0){ close_file(handle); return fd; } if(!try_lock_locking_file(fd)){ _close(fd); return -1; } struct _stat s; if(0 == _stat(name, &s)){ return fd; } else{ _close(fd); } } } inline int try_open_and_lock_file(const char *name) { file_handle_t handle = open_existing_file(name, read_write); int fd = _open_osfhandle((intptr_t)handle, _O_TEXT); if(fd < 0){ close_file(handle); return fd; } if(!try_lock_locking_file(fd)){ _close(fd); return -1; } return fd; } inline void close_lock_file(int fd) { _close(fd); } inline bool is_valid_fd(int fd) { struct _stat s; return EBADF != _fstat(fd, &s); } inline bool is_normal_file(int fd) { if(_isatty(fd)) return false; struct _stat s; if(0 != _fstat(fd, &s)) return false; return 0 != (s.st_mode & _S_IFREG); } inline std::size_t get_size(int fd) { struct _stat s; if(0 != _fstat(fd, &s)) return 0u; return (std::size_t)s.st_size; } inline bool fill_file_serial_id(int fd, locking_file_serial_id &id) { winapi::interprocess_by_handle_file_information info; if(!winapi::get_file_information_by_handle((void*)_get_osfhandle(fd), &info)) return false; id.fd = fd; id.dwVolumeSerialNumber = info.dwVolumeSerialNumber; id.nFileIndexHigh = info.nFileIndexHigh; id.nFileIndexLow = info.nFileIndexLow; id.modules_attached_to_gmem_count = 1; //Initialize attached count return true; } inline bool compare_file_serial(int fd, const locking_file_serial_id &id) { winapi::interprocess_by_handle_file_information info; if(!winapi::get_file_information_by_handle((void*)_get_osfhandle(fd), &info)) return false; return id.dwVolumeSerialNumber == info.dwVolumeSerialNumber && id.nFileIndexHigh == info.nFileIndexHigh && id.nFileIndexLow == info.nFileIndexLow; } #else //UNIX struct locking_file_serial_id { int fd; dev_t st_dev; ino_t st_ino; //This reference count counts the number of modules attached //to the shared memory and lock file. This serves to unlink //the locking file and shared memory when all modules are //done with the global memory (shared memory) volatile boost::uint32_t modules_attached_to_gmem_count; }; inline bool lock_locking_file(int fd) { int ret = 0; while(ret != 0 && errno != EINTR){ struct flock lock; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 1; ret = fcntl (fd, F_SETLKW, &lock); } return 0 == ret; } inline bool try_lock_locking_file(int fd) { struct flock lock; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 1; return 0 == fcntl (fd, F_SETLK, &lock); } inline int open_or_create_and_lock_file(const char *name) { permissions p; p.set_unrestricted(); while(1){ int fd = create_or_open_file(name, read_write, p); if(fd < 0){ return fd; } if(!try_lock_locking_file(fd)){ close(fd); return -1; } struct stat s; if(0 == stat(name, &s)){ return fd; } else{ close(fd); } } } inline int try_open_and_lock_file(const char *name) { int fd = open_existing_file(name, read_write); if(fd < 0){ return fd; } if(!try_lock_locking_file(fd)){ close(fd); return -1; } return fd; } inline void close_lock_file(int fd) { close(fd); } inline bool is_valid_fd(int fd) { struct stat s; return EBADF != fstat(fd, &s); } inline bool is_normal_file(int fd) { struct stat s; if(0 != fstat(fd, &s)) return false; return 0 != (s.st_mode & S_IFREG); } inline std::size_t get_size(int fd) { struct stat s; if(0 != fstat(fd, &s)) return 0u; return (std::size_t)s.st_size; } inline bool fill_file_serial_id(int fd, locking_file_serial_id &id) { struct stat s; if(0 != fstat(fd, &s)) return false; id.fd = fd; id.st_dev = s.st_dev; id.st_ino = s.st_ino; id.modules_attached_to_gmem_count = 1; //Initialize attached count return true; } inline bool compare_file_serial(int fd, const locking_file_serial_id &id) { struct stat info; if(0 != fstat(fd, &info)) return false; return id.st_dev == info.st_dev && id.st_ino == info.st_ino; } #endif template struct gmem_erase_func { gmem_erase_func(const char *shm_name, const char *singleton_lock_file_path, ManagedShMem & shm) :shm_name_(shm_name), singleton_lock_file_path_(singleton_lock_file_path), shm_(shm) {} void operator()() { locking_file_serial_id *pserial_id = shm_.template find("lock_file_fd").first; if(pserial_id){ pserial_id->fd = GMemMarkToBeRemoved; } delete_file(singleton_lock_file_path_); shared_memory_object::remove(shm_name_); } const char * const shm_name_; const char * const singleton_lock_file_path_; ManagedShMem & shm_; }; //This function applies shared memory erasure logic based on the passed lock file. template void managed_sh_dependant:: apply_gmem_erase_logic(const char *filepath, const char *filename) { int fd = GMemMarkToBeRemoved; try{ std::string str; //If the filename is current process lock file, then avoid it if(file_locking_helpers::check_if_filename_complies_with_pid (filename, get_lock_file_base_name(), get_current_process_id(), str, true)){ return; } //Open and lock the other process' lock file fd = try_open_and_lock_file(filepath); if(fd < 0){ return; } //If done, then the process is dead so take global shared memory name //(the name is based on the lock file name) and try to apply erasure logic str.insert(0, get_shm_base_name()); try{ ManagedShMem shm(open_only, str.c_str()); gmem_erase_func func(str.c_str(), filepath, shm); shm.try_atomic_func(func); } catch(interprocess_exception &e){ //If shared memory is not found erase the lock file if(e.get_error_code() == not_found_error){ delete_file(filepath); } } } catch(...){ } if(fd >= 0){ close_lock_file(fd); } } } //namespace intermodule_singleton_helpers { namespace intermodule_singleton_helpers { //The lock file logic creates uses a unique instance to a file template struct lock_file_logic { lock_file_logic(ManagedShMem &shm) : mshm(shm) { shm.atomic_func(*this); } void operator()(void) { retry_with_new_shm = false; //First find the file locking descriptor id locking_file_serial_id *pserial_id = mshm.template find("lock_file_fd").first; int fd; //If not found schedule a creation if(!pserial_id){ fd = GMemNotPresent; } //Else get it else{ fd = pserial_id->fd; } //If we need to create a new one, do it if(fd == GMemNotPresent){ std::string lck_str; //Create a unique current pid based lock file path create_and_get_singleton_lock_file_path(lck_str); //Open or create and lock file int fd = intermodule_singleton_helpers::open_or_create_and_lock_file(lck_str.c_str()); //If failed, write a bad file descriptor to notify other modules that //something was wrong and unlink shared memory. Mark the function object //to tell caller to retry with another shared memory if(fd < 0){ this->register_lock_file(GMemMarkToBeRemoved); std::string s; get_shm_name(s); shared_memory_object::remove(s.c_str()); retry_with_new_shm = true; } //If successful, register the file descriptor else{ this->register_lock_file(fd); } } //If the fd was invalid (maybe a previous try failed) notify caller that //should retry creation logic, since this shm might have been already //unlinked since the shm was removed else if (fd == GMemMarkToBeRemoved){ retry_with_new_shm = true; } //If the stored fd is not valid (a open fd, a normal file with the //expected size, or does not have the same file id number, //then it's an old shm from an old process with the same pid. //If that's the case, mark it as invalid else if(!is_valid_fd(fd) || !is_normal_file(fd) || 0 != get_size(fd) || !compare_file_serial(fd, *pserial_id)){ pserial_id->fd = GMemMarkToBeRemoved; std::string s; get_shm_name(s); shared_memory_object::remove(s.c_str()); retry_with_new_shm = true; } else{ //If the lock file is ok, increment reference count of //attached modules to shared memory atomic_inc32(&pserial_id->modules_attached_to_gmem_count); } } private: locking_file_serial_id * register_lock_file(int fd) { locking_file_serial_id *pinfo = mshm.template construct("lock_file_fd")(); fill_file_serial_id(fd, *pinfo); return pinfo; } public: ManagedShMem &mshm; bool retry_with_new_shm; }; #if defined (BOOST_INTERPROCESS_WINDOWS) template<> struct lock_file_logic { lock_file_logic(managed_windows_shared_memory &) : retry_with_new_shm(false) {} void operator()(void){} const bool retry_with_new_shm; }; #endif } //namespace intermodule_singleton_helpers { //This class contains common code for all singleton types, so that we instantiate this //code just once per module. This class also holds a reference counted shared memory //to be used by all instances template class intermodule_singleton_common { public: typedef void*(singleton_constructor_t)(ManagedShMem &); typedef void (singleton_destructor_t)(void *, ManagedShMem &); static const ::boost::uint32_t Uninitialized = 0u; static const ::boost::uint32_t Initializing = 1u; static const ::boost::uint32_t Initialized = 2u; static const ::boost::uint32_t Broken = 3u; static void finalize_singleton_logic(void *ptr, singleton_destructor_t destructor) { if(ptr) destructor(ptr, get_shm()); //If this is the last singleton of this module //apply shm destruction. //Note: singletons are destroyed when the module is unloaded //so no threads should be executing or holding references //to this module if(1 == atomic_dec32(&this_module_singleton_count)){ destroy_shm(); } } static void initialize_singleton_logic (void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t ini_func); private: static ManagedShMem &get_shm() { return *static_cast(static_cast(&shm_mem)); } enum { MemSize = ((sizeof(ManagedShMem)-1)/sizeof(max_align))+1u }; static void initialize_shm(); static void destroy_shm(); //Static data, zero-initalized without any dependencies //this_module_singleton_count is the number of singletons used by this module static volatile boost::uint32_t this_module_singleton_count; //this_module_shm_initialized is the state of this module's shm class object static volatile boost::uint32_t this_module_shm_initialized; static max_align shm_mem[MemSize]; }; template volatile boost::uint32_t intermodule_singleton_common::this_module_singleton_count; template volatile boost::uint32_t intermodule_singleton_common::this_module_shm_initialized; template max_align intermodule_singleton_common::shm_mem[intermodule_singleton_common::MemSize]; template void intermodule_singleton_common::initialize_shm() { //Obtain unique shm name and size std::string s; intermodule_singleton_helpers::get_shm_name(s); const char *ShmName = s.c_str(); const std::size_t ShmSize = intermodule_singleton_helpers::get_shm_size();; while(1){ //Try to pass shm state to initializing ::boost::uint32_t tmp = atomic_cas32(&this_module_shm_initialized, Initializing, Uninitialized); if(tmp >= Initialized){ break; } //If some other thread is doing the work wait else if(tmp == Initializing){ thread_yield(); } else{ //(tmp == Uninitialized) //If not initialized try it again? try{ //Remove old shared memory from the system intermodule_singleton_helpers::managed_sh_dependant::remove_old_gmem(); //in-place construction of the shared memory class ::new (&get_shm())ManagedShMem(open_or_create, ShmName, ShmSize); //Use shared memory internal lock to initialize the lock file //that will mark this gmem as "in use". intermodule_singleton_helpers::lock_file_logic f(get_shm()); //If function failed (maybe a competing process has erased the shared //memory between creation and file locking), retry with a new instance. if(f.retry_with_new_shm){ get_shm().~ManagedShMem(); atomic_write32(&this_module_shm_initialized, Uninitialized); } else{ //Locking succeeded, so this shared memory module-instance is ready atomic_write32(&this_module_shm_initialized, Initialized); break; } } catch(...){ // throw; } } } } template struct unlink_shmlogic { unlink_shmlogic(ManagedShMem &mshm) : mshm_(mshm) { mshm.atomic_func(*this); } void operator()() { intermodule_singleton_helpers::locking_file_serial_id *pserial_id = mshm_.template find ("lock_file_fd").first; BOOST_ASSERT(0 != pserial_id); if(1 == atomic_dec32(&pserial_id->modules_attached_to_gmem_count)){ int fd = pserial_id->fd; if(fd > 0){ pserial_id->fd = intermodule_singleton_helpers::GMemMarkToBeRemoved; std::string s; intermodule_singleton_helpers::create_and_get_singleton_lock_file_path(s); delete_file(s.c_str()); intermodule_singleton_helpers::close_lock_file(fd); intermodule_singleton_helpers::get_shm_name(s); shared_memory_object::remove(s.c_str()); } } } ManagedShMem &mshm_; }; #if defined (BOOST_INTERPROCESS_WINDOWS) template<> struct unlink_shmlogic { unlink_shmlogic(managed_windows_shared_memory &) {} void operator()(){} }; #endif template void intermodule_singleton_common::destroy_shm() { if(!atomic_read32(&this_module_singleton_count)){ //This module is being unloaded, so destroy //the shared memory object of this module //and unlink the shared memory if it's the last unlink_shmlogic f(get_shm()); (get_shm()).~ManagedShMem(); atomic_write32(&this_module_shm_initialized, Uninitialized); //Do some cleanup for other processes old gmem instances intermodule_singleton_helpers::managed_sh_dependant::remove_old_gmem(); } } //Initialize this_module_singleton_ptr, creates the shared memory if needed and also creates an unique //opaque type in shared memory through a singleton_constructor_t function call, //initializing the passed pointer to that unique instance. // //We have two concurrency types here. a)the shared memory/singleton creation must //be safe between threads of this process but in different modules/dlls. b) //the pointer to the singleton is per-module, so we have to protect this //initization between threads of the same module. // //All static variables declared here are shared between inside a module //so atomic operations will synchronize only threads of the same module. template void intermodule_singleton_common::initialize_singleton_logic (void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t constructor) { //If current module is not initialized enter to lock free logic if(atomic_read32(&this_module_singleton_initialized) != Initialized){ //Now a single thread of the module will succeed in this CAS. //trying to pass from Uninitialized to Initializing ::boost::uint32_t previous_module_singleton_initialized = atomic_cas32 (&this_module_singleton_initialized, Initializing, Uninitialized); //If the thread succeeded the CAS (winner) it will compete with other //winner threads from other modules to create the shared memory if(previous_module_singleton_initialized == Uninitialized){ try{ //Now initialize shm, this function solves concurrency issues //between threads of several modules initialize_shm(); //Increment the module reference count that reflects how many //singletons this module holds, so that we can safely destroy //module shared memory object when no singleton is left atomic_inc32(&this_module_singleton_count); //Now try to create the singleton in shared memory. //This function solves concurrency issues //between threads of several modules void *tmp = constructor(get_shm()); //Insert a barrier before assigning the pointer to //make sure this assignment comes after the initialization atomic_write32(&this_module_singleton_initialized, Initializing); //Assign the singleton address to the module-local pointer ptr = tmp; //Memory barrier inserted, all previous operations should complete //before this one. Now marked as initialized atomic_inc32(&this_module_singleton_initialized); } catch(...){ //Mark singleton failed to initialize atomic_write32(&this_module_singleton_initialized, Broken); throw; } } //If previous state was initializing, this means that another winner thread is //trying to initialize the singleton. Just wait until completes its work. else if(previous_module_singleton_initialized == Initializing){ while(1){ previous_module_singleton_initialized = atomic_read32(&this_module_singleton_initialized); if(previous_module_singleton_initialized >= Initialized){ //Already initialized, or exception thrown by initializer thread break; } else if(previous_module_singleton_initialized == Initializing){ detail::thread_yield(); } else{ //This can't be happening! BOOST_ASSERT(0); } } } else if(previous_module_singleton_initialized == Initialized){ //Nothing to do here, the singleton is ready } //If previous state was greater than initialized, then memory is broken //trying to initialize the singleton. else{//(previous_module_singleton_initialized > Initialized) throw interprocess_exception("boost::interprocess::intermodule_singleton initialization failed"); } } BOOST_ASSERT(ptr != 0); } //Now this class is a singleton, initializing the singleton in //the first get() function call if LazyInit is false. If true //then the singleton will be initialized when loading the module. template class intermodule_singleton_impl { public: static C& get() //Let's make inlining easy { if(!this_module_singleton_ptr){ if(lifetime.dummy_function()) //This forces lifetime instantiation, for reference counted destruction intermodule_singleton_common::initialize_singleton_logic (this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor); } return *static_cast(this_module_singleton_ptr); } private: struct ref_count_ptr { ref_count_ptr(C *p, boost::uint32_t count) : ptr(p), singleton_ref_count(count) {} C *ptr; //This reference count serves to count the number of attached //modules to this singleton volatile boost::uint32_t singleton_ref_count; }; //These statics will be zero-initialized without any constructor call dependency //this_module_singleton_ptr will be a module-local pointer to the singleton static void* this_module_singleton_ptr; //this_module_singleton_count will be used to synchronize threads of the same module //for access to a singleton instance, and to flag the state of the //singleton. static volatile boost::uint32_t this_module_singleton_initialized; //This class destructor will trigger singleton destruction struct lifetime_type_lazy { bool dummy_function() { return m_dummy == 0; } ~lifetime_type_lazy() { intermodule_singleton_common::finalize_singleton_logic (this_module_singleton_ptr, singleton_destructor); } //Dummy volatile so that the compiler can't resolve its value at compile-time //and can't avoid lifetime_type instantiation if dummy_function() is called. static volatile int m_dummy; }; struct lifetime_type_static : public lifetime_type_lazy { lifetime_type_static() { intermodule_singleton_common::initialize_singleton_logic (this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor); } }; typedef typename if_c ::type lifetime_type; static lifetime_type lifetime; //A functor to be executed inside shared memory lock that just //searches for the singleton in shm and if not present creates a new one. //If singleton constructor throws, the exception is propagated struct init_atomic_func { init_atomic_func(ManagedShMem &m) : mshm(m) {} void operator()() { ref_count_ptr *rcount = mshm.template find(unique_instance).first; if(!rcount){ C *p = new C(); try{ rcount = mshm.template construct(unique_instance)(p, 0u); } catch(...){ delete p; throw; } } atomic_inc32(&rcount->singleton_ref_count); ret_ptr = rcount->ptr; } ManagedShMem &mshm; void *ret_ptr; }; //A functor to be executed inside shared memory lock that just //deletes the singleton in shm if the attached count reaches to zero struct fini_atomic_func { fini_atomic_func(ManagedShMem &m) : mshm(m) {} void operator()() { ref_count_ptr *rcount = mshm.template find(unique_instance).first; //The object must exist BOOST_ASSERT(rcount); //Check if last reference if(atomic_dec32(&rcount->singleton_ref_count) == 1){ //If last, destroy the object BOOST_ASSERT(rcount->ptr != 0); delete rcount->ptr; //Now destroy shm entry bool destroyed = mshm.template destroy(unique_instance); (void)destroyed; BOOST_ASSERT(destroyed == true); } } ManagedShMem &mshm; void *ret_ptr; }; //A wrapper to execute init_atomic_func static void *singleton_constructor(ManagedShMem &mshm) { init_atomic_func f(mshm); mshm.atomic_func(f); return f.ret_ptr; } //A wrapper to execute fini_atomic_func static void singleton_destructor(void *p, ManagedShMem &mshm) { (void)p; fini_atomic_func f(mshm); mshm.atomic_func(f); } }; template volatile int intermodule_singleton_impl::lifetime_type_lazy::m_dummy; //These will be zero-initialized by the loader template void *intermodule_singleton_impl::this_module_singleton_ptr; template volatile boost::uint32_t intermodule_singleton_impl::this_module_singleton_initialized; template typename intermodule_singleton_impl::lifetime_type intermodule_singleton_impl::lifetime; template class portable_intermodule_singleton : public intermodule_singleton_impl {}; #ifdef BOOST_INTERPROCESS_WINDOWS template class windows_intermodule_singleton : public intermodule_singleton_impl {}; #endif //Now this class is a singleton, initializing the singleton in //the first get() function call if LazyInit is false. If true //then the singleton will be initialized when loading the module. template class intermodule_singleton #ifdef BOOST_INTERPROCESS_WINDOWS : public windows_intermodule_singleton // : public portable_intermodule_singleton #else : public portable_intermodule_singleton #endif {}; } //namespace detail{ } //namespace interprocess{ } //namespace boost{ #include #endif