Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: // Chris@16: // (C) Copyright Ion Gaztanaga 2009-2012. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // See http://www.boost.org/libs/interprocess for documentation. Chris@16: // Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: #ifndef BOOST_INTERPROCESS_INTERMODULE_SINGLETON_COMMON_HPP Chris@16: #define BOOST_INTERPROCESS_INTERMODULE_SINGLETON_COMMON_HPP Chris@16: Chris@101: #ifndef BOOST_CONFIG_HPP Chris@101: # include Chris@101: #endif Chris@101: # Chris@101: #if defined(BOOST_HAS_PRAGMA_ONCE) Chris@16: #pragma once Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #include //alignment_of, aligned_storage Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost{ Chris@16: namespace interprocess{ Chris@16: namespace ipcdetail{ Chris@16: Chris@16: namespace intermodule_singleton_helpers { Chris@16: Chris@16: inline void get_pid_creation_time_str(std::string &s) Chris@16: { Chris@16: std::stringstream stream; Chris@16: stream << get_current_process_id() << '_'; Chris@16: stream.precision(6); Chris@16: stream << std::fixed << get_current_process_creation_time(); Chris@16: s = stream.str(); Chris@16: } Chris@16: Chris@16: inline const char *get_map_base_name() Chris@16: { return "bip.gmem.map."; } Chris@16: Chris@16: inline void get_map_name(std::string &map_name) Chris@16: { Chris@16: get_pid_creation_time_str(map_name); Chris@16: map_name.insert(0, get_map_base_name()); Chris@16: } Chris@16: Chris@16: inline std::size_t get_map_size() Chris@16: { return 65536; } Chris@16: Chris@16: template Chris@16: struct thread_safe_global_map_dependant; Chris@16: Chris@16: } //namespace intermodule_singleton_helpers { Chris@16: Chris@16: //This class contains common code for all singleton types, so that we instantiate this Chris@16: //code just once per module. This class also holds a thread soafe global map Chris@16: //to be used by all instances protected with a reference count Chris@16: template Chris@16: class intermodule_singleton_common Chris@16: { Chris@16: public: Chris@16: typedef void*(singleton_constructor_t)(ThreadSafeGlobalMap &); Chris@16: typedef void (singleton_destructor_t)(void *, ThreadSafeGlobalMap &); Chris@16: Chris@16: static const ::boost::uint32_t Uninitialized = 0u; Chris@16: static const ::boost::uint32_t Initializing = 1u; Chris@16: static const ::boost::uint32_t Initialized = 2u; Chris@16: static const ::boost::uint32_t Broken = 3u; Chris@16: static const ::boost::uint32_t Destroyed = 4u; Chris@16: Chris@16: //Initialize this_module_singleton_ptr, creates the global map if needed and also creates an unique Chris@16: //opaque type in global map through a singleton_constructor_t function call, Chris@16: //initializing the passed pointer to that unique instance. Chris@16: // Chris@16: //We have two concurrency types here. a)the global map/singleton creation must Chris@16: //be safe between threads of this process but in different modules/dlls. b) Chris@16: //the pointer to the singleton is per-module, so we have to protect this Chris@16: //initization between threads of the same module. Chris@16: // Chris@16: //All static variables declared here are shared between inside a module Chris@16: //so atomic operations will synchronize only threads of the same module. Chris@16: static void initialize_singleton_logic Chris@16: (void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t constructor, bool phoenix) Chris@16: { Chris@16: //If current module is not initialized enter to lock free logic Chris@16: if(atomic_read32(&this_module_singleton_initialized) != Initialized){ Chris@16: //Now a single thread of the module will succeed in this CAS. Chris@16: //trying to pass from Uninitialized to Initializing Chris@16: ::boost::uint32_t previous_module_singleton_initialized = atomic_cas32 Chris@16: (&this_module_singleton_initialized, Initializing, Uninitialized); Chris@16: //If the thread succeeded the CAS (winner) it will compete with other Chris@16: //winner threads from other modules to create the global map Chris@16: if(previous_module_singleton_initialized == Destroyed){ Chris@16: //Trying to resurrect a dead Phoenix singleton. Just try to Chris@16: //mark it as uninitialized and start again Chris@16: if(phoenix){ Chris@16: atomic_cas32(&this_module_singleton_initialized, Uninitialized, Destroyed); Chris@16: previous_module_singleton_initialized = atomic_cas32 Chris@16: (&this_module_singleton_initialized, Initializing, Uninitialized); Chris@16: } Chris@16: //Trying to resurrect a non-Phoenix dead singleton is an error Chris@16: else{ Chris@16: throw interprocess_exception("Boost.Interprocess: Dead reference on non-Phoenix singleton of type"); Chris@16: } Chris@16: } Chris@16: if(previous_module_singleton_initialized == Uninitialized){ Chris@16: try{ Chris@16: //Now initialize the global map, this function must solve concurrency Chris@16: //issues between threads of several modules Chris@16: initialize_global_map_handle(); Chris@16: //Now try to create the singleton in global map. Chris@16: //This function solves concurrency issues Chris@16: //between threads of several modules Chris@101: ThreadSafeGlobalMap *const pmap = get_map_ptr(); Chris@101: void *tmp = constructor(*pmap); Chris@16: //Increment the module reference count that reflects how many Chris@16: //singletons this module holds, so that we can safely destroy Chris@16: //module global map object when no singleton is left Chris@16: atomic_inc32(&this_module_singleton_count); Chris@16: //Insert a barrier before assigning the pointer to Chris@16: //make sure this assignment comes after the initialization Chris@16: atomic_write32(&this_module_singleton_initialized, Initializing); Chris@16: //Assign the singleton address to the module-local pointer Chris@16: ptr = tmp; Chris@16: //Memory barrier inserted, all previous operations should complete Chris@16: //before this one. Now marked as initialized Chris@16: atomic_write32(&this_module_singleton_initialized, Initialized); Chris@16: } Chris@16: catch(...){ Chris@16: //Mark singleton failed to initialize Chris@16: atomic_write32(&this_module_singleton_initialized, Broken); Chris@16: throw; Chris@16: } Chris@16: } Chris@16: //If previous state was initializing, this means that another winner thread is Chris@16: //trying to initialize the singleton. Just wait until completes its work. Chris@16: else if(previous_module_singleton_initialized == Initializing){ Chris@16: spin_wait swait; Chris@16: while(1){ Chris@16: previous_module_singleton_initialized = atomic_read32(&this_module_singleton_initialized); Chris@16: if(previous_module_singleton_initialized >= Initialized){ Chris@16: //Already initialized, or exception thrown by initializer thread Chris@16: break; Chris@16: } Chris@16: else if(previous_module_singleton_initialized == Initializing){ Chris@16: swait.yield(); Chris@16: } Chris@16: else{ Chris@16: //This can't be happening! Chris@16: BOOST_ASSERT(0); Chris@16: } Chris@16: } Chris@16: } Chris@16: else if(previous_module_singleton_initialized == Initialized){ Chris@16: //Nothing to do here, the singleton is ready Chris@16: } Chris@16: //If previous state was greater than initialized, then memory is broken Chris@16: //trying to initialize the singleton. Chris@16: else{//(previous_module_singleton_initialized > Initialized) Chris@16: throw interprocess_exception("boost::interprocess::intermodule_singleton initialization failed"); Chris@16: } Chris@16: } Chris@16: BOOST_ASSERT(ptr != 0); Chris@16: } Chris@16: Chris@16: static void finalize_singleton_logic(void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_destructor_t destructor) Chris@16: { Chris@16: //Protect destruction against lazy singletons not initialized in this execution Chris@16: if(ptr){ Chris@16: //Note: this destructor might provoke a Phoenix singleton Chris@16: //resurrection. This means that this_module_singleton_count Chris@16: //might change after this call. Chris@101: ThreadSafeGlobalMap * const pmap = get_map_ptr(); Chris@101: destructor(ptr, *pmap); Chris@16: ptr = 0; Chris@16: Chris@16: //Memory barrier to make sure pointer is nulled. Chris@16: //Mark this singleton as destroyed. Chris@16: atomic_write32(&this_module_singleton_initialized, Destroyed); Chris@16: Chris@16: //If this is the last singleton of this module Chris@16: //apply map destruction. Chris@16: //Note: singletons are destroyed when the module is unloaded Chris@16: //so no threads should be executing or holding references Chris@16: //to this module Chris@16: if(1 == atomic_dec32(&this_module_singleton_count)){ Chris@16: destroy_global_map_handle(); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@101: static ThreadSafeGlobalMap *get_map_ptr() Chris@16: { Chris@101: return static_cast(static_cast(mem_holder.map_mem)); Chris@16: } Chris@16: Chris@16: static void initialize_global_map_handle() Chris@16: { Chris@16: //Obtain unique map name and size Chris@16: spin_wait swait; Chris@16: while(1){ Chris@16: //Try to pass map state to initializing Chris@16: ::boost::uint32_t tmp = atomic_cas32(&this_module_map_initialized, Initializing, Uninitialized); Chris@16: if(tmp == Initialized || tmp == Broken){ Chris@16: break; Chris@16: } Chris@16: else if(tmp == Destroyed){ Chris@16: tmp = atomic_cas32(&this_module_map_initialized, Uninitialized, Destroyed); Chris@16: continue; Chris@16: } Chris@16: //If some other thread is doing the work wait Chris@16: else if(tmp == Initializing){ Chris@16: swait.yield(); Chris@16: } Chris@16: else{ //(tmp == Uninitialized) Chris@16: //If not initialized try it again? Chris@16: try{ Chris@16: //Remove old global map from the system Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant::remove_old_gmem(); Chris@16: //in-place construction of the global map class Chris@101: ThreadSafeGlobalMap * const pmap = get_map_ptr(); Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@101: ::construct_map(static_cast(pmap)); Chris@16: //Use global map's internal lock to initialize the lock file Chris@16: //that will mark this gmem as "in use". Chris@16: typename intermodule_singleton_helpers::thread_safe_global_map_dependant:: Chris@101: lock_file_logic f(*pmap); Chris@16: //If function failed (maybe a competing process has erased the shared Chris@16: //memory between creation and file locking), retry with a new instance. Chris@16: if(f.retry()){ Chris@101: pmap->~ThreadSafeGlobalMap(); Chris@16: atomic_write32(&this_module_map_initialized, Destroyed); Chris@16: } Chris@16: else{ Chris@16: //Locking succeeded, so this global map module-instance is ready Chris@16: atomic_write32(&this_module_map_initialized, Initialized); Chris@16: break; Chris@16: } Chris@16: } Chris@16: catch(...){ Chris@16: // Chris@16: throw; Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: static void destroy_global_map_handle() Chris@16: { Chris@16: if(!atomic_read32(&this_module_singleton_count)){ Chris@16: //This module is being unloaded, so destroy Chris@16: //the global map object of this module Chris@16: //and unlink the global map if it's the last Chris@101: ThreadSafeGlobalMap * const pmap = get_map_ptr(); Chris@16: typename intermodule_singleton_helpers::thread_safe_global_map_dependant:: Chris@101: unlink_map_logic f(*pmap); Chris@101: pmap->~ThreadSafeGlobalMap(); Chris@16: atomic_write32(&this_module_map_initialized, Destroyed); Chris@16: //Do some cleanup for other processes old gmem instances Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant::remove_old_gmem(); Chris@16: } Chris@16: } Chris@16: Chris@16: //Static data, zero-initalized without any dependencies Chris@16: //this_module_singleton_count is the number of singletons used by this module Chris@16: static volatile boost::uint32_t this_module_singleton_count; Chris@16: Chris@16: //this_module_map_initialized is the state of this module's map class object. Chris@16: //Values: Uninitialized, Initializing, Initialized, Broken Chris@16: static volatile boost::uint32_t this_module_map_initialized; Chris@16: Chris@16: //Raw memory to construct the global map manager Chris@101: static union mem_holder_t Chris@16: { Chris@101: unsigned char map_mem [sizeof(ThreadSafeGlobalMap)]; Chris@101: ::boost::container::container_detail::max_align_t aligner; Chris@16: } mem_holder; Chris@16: }; Chris@16: Chris@16: template Chris@16: volatile boost::uint32_t intermodule_singleton_common::this_module_singleton_count; Chris@16: Chris@16: template Chris@16: volatile boost::uint32_t intermodule_singleton_common::this_module_map_initialized; Chris@16: Chris@16: template Chris@16: typename intermodule_singleton_common::mem_holder_t Chris@16: intermodule_singleton_common::mem_holder; Chris@16: Chris@16: //A reference count to be stored in global map holding the number Chris@16: //of singletons (one per module) attached to the instance pointed by Chris@16: //the internal ptr. Chris@16: struct ref_count_ptr Chris@16: { Chris@16: ref_count_ptr(void *p, boost::uint32_t count) Chris@16: : ptr(p), singleton_ref_count(count) Chris@16: {} Chris@16: void *ptr; Chris@16: //This reference count serves to count the number of attached Chris@16: //modules to this singleton Chris@16: volatile boost::uint32_t singleton_ref_count; Chris@16: }; Chris@16: Chris@16: Chris@16: //Now this class is a singleton, initializing the singleton in Chris@16: //the first get() function call if LazyInit is true. If false Chris@16: //then the singleton will be initialized when loading the module. Chris@16: template Chris@16: class intermodule_singleton_impl Chris@16: { Chris@16: public: Chris@16: Chris@16: static C& get() //Let's make inlining easy Chris@16: { Chris@16: if(!this_module_singleton_ptr){ Chris@16: if(lifetime.dummy_function()){ //This forces lifetime instantiation, for reference counted destruction Chris@16: atentry_work(); Chris@16: } Chris@16: } Chris@16: return *static_cast(this_module_singleton_ptr); Chris@16: } Chris@16: Chris@16: private: Chris@16: Chris@16: static void atentry_work() Chris@16: { Chris@16: intermodule_singleton_common::initialize_singleton_logic Chris@16: (this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor, Phoenix); Chris@16: } Chris@16: Chris@16: static void atexit_work() Chris@16: { Chris@16: intermodule_singleton_common::finalize_singleton_logic Chris@16: (this_module_singleton_ptr, this_module_singleton_initialized, singleton_destructor); Chris@16: } Chris@16: Chris@16: //These statics will be zero-initialized without any constructor call dependency Chris@16: //this_module_singleton_ptr will be a module-local pointer to the singleton Chris@16: static void* this_module_singleton_ptr; Chris@16: Chris@16: //this_module_singleton_count will be used to synchronize threads of the same module Chris@16: //for access to a singleton instance, and to flag the state of the Chris@16: //singleton. Chris@16: static volatile boost::uint32_t this_module_singleton_initialized; Chris@16: Chris@16: //This class destructor will trigger singleton destruction Chris@16: struct lifetime_type_lazy Chris@16: { Chris@16: bool dummy_function() Chris@16: { return m_dummy == 0; } Chris@16: Chris@16: ~lifetime_type_lazy() Chris@16: { Chris@101: //if(!Phoenix){ Chris@101: //atexit_work(); Chris@101: //} Chris@16: } Chris@16: Chris@16: //Dummy volatile so that the compiler can't resolve its value at compile-time Chris@16: //and can't avoid lifetime_type instantiation if dummy_function() is called. Chris@16: static volatile int m_dummy; Chris@16: }; Chris@16: Chris@16: struct lifetime_type_static Chris@16: : public lifetime_type_lazy Chris@16: { Chris@16: lifetime_type_static() Chris@16: { atentry_work(); } Chris@16: }; Chris@16: Chris@16: typedef typename if_c Chris@16: ::type lifetime_type; Chris@16: Chris@16: static lifetime_type lifetime; Chris@16: Chris@16: //A functor to be executed inside global map lock that just Chris@16: //searches for the singleton in map and if not present creates a new one. Chris@16: //If singleton constructor throws, the exception is propagated Chris@16: struct init_atomic_func Chris@16: { Chris@16: init_atomic_func(ThreadSafeGlobalMap &m) Chris@101: : m_map(m), ret_ptr() Chris@16: {} Chris@16: Chris@16: void operator()() Chris@16: { Chris@16: ref_count_ptr *rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::find(m_map, typeid(C).name()); Chris@16: if(!rcount){ Chris@16: C *p = new C; Chris@16: try{ Chris@16: ref_count_ptr val(p, 0u); Chris@16: rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::insert(m_map, typeid(C).name(), val); Chris@16: } Chris@16: catch(...){ Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::erase(m_map, typeid(C).name()); Chris@16: delete p; Chris@16: throw; Chris@16: } Chris@16: } Chris@101: //if(Phoenix){ Chris@16: std::atexit(&atexit_work); Chris@101: //} Chris@16: atomic_inc32(&rcount->singleton_ref_count); Chris@16: ret_ptr = rcount->ptr; Chris@16: } Chris@16: void *data() const Chris@16: { return ret_ptr; } Chris@16: Chris@16: private: Chris@16: ThreadSafeGlobalMap &m_map; Chris@16: void *ret_ptr; Chris@16: }; Chris@16: Chris@16: //A functor to be executed inside global map lock that just Chris@16: //deletes the singleton in map if the attached count reaches to zero Chris@16: struct fini_atomic_func Chris@16: { Chris@16: fini_atomic_func(ThreadSafeGlobalMap &m) Chris@16: : m_map(m) Chris@16: {} Chris@16: Chris@16: void operator()() Chris@16: { Chris@16: ref_count_ptr *rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::find(m_map, typeid(C).name()); Chris@16: //The object must exist Chris@16: BOOST_ASSERT(rcount); Chris@16: BOOST_ASSERT(rcount->singleton_ref_count > 0); Chris@16: //Check if last reference Chris@16: if(atomic_dec32(&rcount->singleton_ref_count) == 1){ Chris@16: //If last, destroy the object Chris@16: BOOST_ASSERT(rcount->ptr != 0); Chris@16: C *pc = static_cast(rcount->ptr); Chris@16: //Now destroy map entry Chris@16: bool destroyed = intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::erase(m_map, typeid(C).name()); Chris@16: (void)destroyed; BOOST_ASSERT(destroyed == true); Chris@16: delete pc; Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@16: ThreadSafeGlobalMap &m_map; Chris@16: }; Chris@16: Chris@16: //A wrapper to execute init_atomic_func Chris@16: static void *singleton_constructor(ThreadSafeGlobalMap &map) Chris@16: { Chris@16: init_atomic_func f(map); Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::atomic_func(map, f); Chris@16: return f.data(); Chris@16: } Chris@16: Chris@16: //A wrapper to execute fini_atomic_func Chris@16: static void singleton_destructor(void *p, ThreadSafeGlobalMap &map) Chris@16: { (void)p; Chris@16: fini_atomic_func f(map); Chris@16: intermodule_singleton_helpers::thread_safe_global_map_dependant Chris@16: ::atomic_func(map, f); Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: volatile int intermodule_singleton_impl::lifetime_type_lazy::m_dummy = 0; Chris@16: Chris@16: //These will be zero-initialized by the loader Chris@16: template Chris@16: void *intermodule_singleton_impl::this_module_singleton_ptr = 0; Chris@16: Chris@16: template Chris@16: volatile boost::uint32_t intermodule_singleton_impl::this_module_singleton_initialized = 0; Chris@16: Chris@16: template Chris@16: typename intermodule_singleton_impl::lifetime_type Chris@16: intermodule_singleton_impl::lifetime; Chris@16: Chris@16: } //namespace ipcdetail{ Chris@16: } //namespace interprocess{ Chris@16: } //namespace boost{ Chris@16: Chris@16: #include Chris@16: Chris@16: #endif //#ifndef BOOST_INTERPROCESS_INTERMODULE_SINGLETON_COMMON_HPP