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