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_WINDOWS_INTERMODULE_SINGLETON_HPP Chris@16: #define BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_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@101: #include Chris@16: Chris@16: #if !defined(BOOST_INTERPROCESS_WINDOWS) Chris@16: #error "This header can't be included from non-windows operating systems" Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #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: //This global map will be implemented using 3 sync primitives: Chris@16: // Chris@16: //1) A named mutex that will implement global mutual exclusion between Chris@16: // threads from different modules/dlls Chris@16: // Chris@16: //2) A semaphore that will act as a global counter for modules attached to the global map Chris@16: // so that the global map can be destroyed when the last module is detached. Chris@16: // Chris@16: //3) A semaphore that will be hacked to hold the address of a heap-allocated map in the Chris@16: // max and current semaphore count. Chris@16: class windows_semaphore_based_map Chris@16: { Chris@101: typedef boost::container::map map_type; Chris@16: Chris@16: public: Chris@16: windows_semaphore_based_map() Chris@16: { Chris@16: map_type *m = new map_type; Chris@16: boost::uint32_t initial_count = 0; Chris@16: boost::uint32_t max_count = 0; Chris@16: Chris@16: //Windows user address space sizes: Chris@16: //32 bit windows: [32 bit processes] 2GB or 3GB (31/32 bits) Chris@16: //64 bit windows: [32 bit processes] 2GB or 4GB (31/32 bits) Chris@16: // [64 bit processes] 2GB or 8TB (31/43 bits) Chris@16: // Chris@16: //Windows semaphores use 'long' parameters (32 bits in LLP64 data model) and Chris@16: //those values can't be negative, so we have 31 bits to store something Chris@16: //in max_count and initial count parameters. Chris@16: //Also, max count must be bigger than 0 and bigger or equal than initial count. Chris@16: if(sizeof(void*) == sizeof(boost::uint32_t)){ Chris@16: //This means that for 32 bit processes, a semaphore count (31 usable bits) is Chris@16: //enough to store 4 byte aligned memory (4GB -> 32 bits - 2 bits = 30 bits). Chris@16: //The max count will hold the pointer value and current semaphore count Chris@16: //will be zero. Chris@16: // Chris@16: //Relying in UB with a cast through union, but all known windows compilers Chris@16: //accept this (C11 also accepts this). Chris@16: union caster_union Chris@16: { Chris@16: void *addr; Chris@16: boost::uint32_t addr_uint32; Chris@16: } caster; Chris@16: caster.addr = m; Chris@16: //memory is at least 4 byte aligned in windows Chris@16: BOOST_ASSERT((caster.addr_uint32 & boost::uint32_t(3)) == 0); Chris@16: max_count = caster.addr_uint32 >> 2; Chris@16: } Chris@16: else if(sizeof(void*) == sizeof(boost::uint64_t)){ Chris@16: //Relying in UB with a cast through union, but all known windows compilers Chris@16: //accept this (C11 accepts this). Chris@16: union caster_union Chris@16: { Chris@16: void *addr; Chris@16: boost::uint64_t addr_uint64; Chris@16: } caster; Chris@16: caster.addr = m; Chris@16: //We'll encode the address using 30 bits in each 32 bit high and low parts. Chris@16: //High part will be the sem max count, low part will be the sem initial count. Chris@16: //(restrictions: max count > 0, initial count >= 0 and max count >= initial count): Chris@16: // Chris@16: // - Low part will be shifted two times (4 byte alignment) so that top Chris@16: // two bits are cleared (the top one for sign, the next one to Chris@16: // assure low part value is always less than the high part value. Chris@16: // - The top bit of the high part will be cleared and the next bit will be 1 Chris@16: // (so high part is always bigger than low part due to the quasi-top bit). Chris@16: // Chris@16: // This means that the addresses we can store must be 4 byte aligned Chris@16: // and less than 1 ExbiBytes ( 2^60 bytes, ~1 ExaByte). User-level address space in Windows 64 Chris@16: // is much less than this (8TB, 2^43 bytes): "1 EByte (or it was 640K?) ought to be enough for anybody" ;-). Chris@16: caster.addr = m; Chris@16: BOOST_ASSERT((caster.addr_uint64 & boost::uint64_t(3)) == 0); Chris@16: max_count = boost::uint32_t(caster.addr_uint64 >> 32); Chris@16: initial_count = boost::uint32_t(caster.addr_uint64); Chris@16: initial_count = initial_count/4; Chris@16: //Make sure top two bits are zero Chris@16: BOOST_ASSERT((max_count & boost::uint32_t(0xC0000000)) == 0); Chris@16: //Set quasi-top bit Chris@16: max_count |= boost::uint32_t(0x40000000); Chris@16: } Chris@16: bool created = false; Chris@16: const permissions & perm = permissions(); Chris@16: std::string pid_creation_time, name; Chris@16: get_pid_creation_time_str(pid_creation_time); Chris@16: name = "bipc_gmap_sem_lock_"; Chris@16: name += pid_creation_time; Chris@16: bool success = m_mtx_lock.open_or_create(name.c_str(), perm); Chris@16: name = "bipc_gmap_sem_count_"; Chris@16: name += pid_creation_time; Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: { Chris@16: success = success && m_sem_count.open_or_create Chris@16: ( name.c_str(), static_cast(0), winapi_semaphore_wrapper::MaxCount, perm, created); Chris@16: name = "bipc_gmap_sem_map_"; Chris@16: name += pid_creation_time; Chris@16: success = success && m_sem_map.open_or_create Chris@16: (name.c_str(), initial_count, max_count, perm, created); Chris@16: if(!success){ Chris@16: delete m; Chris@16: //winapi_xxx wrappers do the cleanup... Chris@16: throw int(0); Chris@16: } Chris@16: if(!created){ Chris@16: delete m; Chris@16: } Chris@16: else{ Chris@16: BOOST_ASSERT(&get_map_unlocked() == m); Chris@16: } Chris@16: m_sem_count.post(); Chris@16: } Chris@16: } Chris@16: Chris@16: map_type &get_map_unlocked() Chris@16: { Chris@16: if(sizeof(void*) == sizeof(boost::uint32_t)){ Chris@16: union caster_union Chris@16: { Chris@16: void *addr; Chris@16: boost::uint32_t addr_uint32; Chris@16: } caster; Chris@16: caster.addr = 0; Chris@16: caster.addr_uint32 = m_sem_map.limit(); Chris@16: caster.addr_uint32 = caster.addr_uint32 << 2; Chris@16: return *static_cast(caster.addr); Chris@16: } Chris@16: else{ Chris@16: union caster_union Chris@16: { Chris@16: void *addr; Chris@16: boost::uint64_t addr_uint64; Chris@16: } caster; Chris@16: boost::uint32_t max_count(m_sem_map.limit()), initial_count(m_sem_map.value()); Chris@16: //Clear quasi-top bit Chris@16: max_count &= boost::uint32_t(0xBFFFFFFF); Chris@16: caster.addr_uint64 = max_count; Chris@16: caster.addr_uint64 = caster.addr_uint64 << 32; Chris@16: caster.addr_uint64 |= boost::uint64_t(initial_count) << 2; Chris@16: return *static_cast(caster.addr); Chris@16: } Chris@16: } Chris@16: Chris@16: ref_count_ptr *find(const char *name) Chris@16: { Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: map_type &map = this->get_map_unlocked(); Chris@101: map_type::iterator it = map.find(boost::container::string(name)); Chris@16: if(it != map.end()){ Chris@16: return &it->second; Chris@16: } Chris@16: else{ Chris@16: return 0; Chris@16: } Chris@16: } Chris@16: Chris@16: ref_count_ptr * insert(const char *name, const ref_count_ptr &ref) Chris@16: { Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: map_type &map = this->get_map_unlocked(); Chris@101: map_type::iterator it = map.insert(map_type::value_type(boost::container::string(name), ref)).first; Chris@16: return &it->second; Chris@16: } Chris@16: Chris@16: bool erase(const char *name) Chris@16: { Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: map_type &map = this->get_map_unlocked(); Chris@101: return map.erase(boost::container::string(name)) != 0; Chris@16: } Chris@16: Chris@16: template Chris@16: void atomic_func(F &f) Chris@16: { Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: f(); Chris@16: } Chris@16: Chris@16: ~windows_semaphore_based_map() Chris@16: { Chris@16: scoped_lock lck(m_mtx_lock); Chris@16: m_sem_count.wait(); Chris@16: if(0 == m_sem_count.value()){ Chris@16: map_type &map = this->get_map_unlocked(); Chris@16: BOOST_ASSERT(map.empty()); Chris@16: delete ↦ Chris@16: } Chris@16: //First close sems to protect this with the external mutex Chris@16: m_sem_map.close(); Chris@16: m_sem_count.close(); Chris@16: //Once scoped_lock unlocks the mutex, the destructor will close the handle... Chris@16: } Chris@16: Chris@16: private: Chris@16: winapi_mutex_wrapper m_mtx_lock; Chris@16: winapi_semaphore_wrapper m_sem_map; Chris@16: winapi_semaphore_wrapper m_sem_count; Chris@16: }; Chris@16: Chris@16: template<> Chris@16: struct thread_safe_global_map_dependant Chris@16: { Chris@16: static void apply_gmem_erase_logic(const char *, const char *){} Chris@16: Chris@16: static bool remove_old_gmem() Chris@16: { return true; } Chris@16: Chris@16: struct lock_file_logic Chris@16: { Chris@16: lock_file_logic(windows_semaphore_based_map &) Chris@16: : retry_with_new_map(false) Chris@16: {} Chris@16: Chris@16: void operator()(void){} Chris@16: bool retry() const { return retry_with_new_map; } Chris@16: private: Chris@16: const bool retry_with_new_map; Chris@16: }; Chris@16: Chris@16: static void construct_map(void *addr) Chris@16: { Chris@16: ::new (addr)windows_semaphore_based_map; Chris@16: } Chris@16: Chris@16: struct unlink_map_logic Chris@16: { Chris@16: unlink_map_logic(windows_semaphore_based_map &) Chris@16: {} Chris@16: void operator()(){} Chris@16: }; Chris@16: Chris@16: static ref_count_ptr *find(windows_semaphore_based_map &map, const char *name) Chris@16: { Chris@16: return map.find(name); Chris@16: } Chris@16: Chris@16: static ref_count_ptr * insert(windows_semaphore_based_map &map, const char *name, const ref_count_ptr &ref) Chris@16: { Chris@16: return map.insert(name, ref); Chris@16: } Chris@16: Chris@16: static bool erase(windows_semaphore_based_map &map, const char *name) Chris@16: { Chris@16: return map.erase(name); Chris@16: } Chris@16: Chris@16: template Chris@16: static void atomic_func(windows_semaphore_based_map &map, F &f) Chris@16: { Chris@16: map.atomic_func(f); Chris@16: } Chris@16: }; Chris@16: Chris@16: } //namespace intermodule_singleton_helpers { Chris@16: Chris@101: template Chris@16: class windows_intermodule_singleton Chris@16: : public intermodule_singleton_impl Chris@16: < C Chris@16: , LazyInit Chris@16: , Phoenix Chris@16: , intermodule_singleton_helpers::windows_semaphore_based_map Chris@16: > Chris@16: {}; 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_WINDOWS_INTERMODULE_SINGLETON_HPP