1 ////////////////////////////////////////////////////////////////////////////// 2 // 3 // (C) Copyright Ion Gaztanaga 2009-2012. Distributed under the Boost 4 // Software License, Version 1.0. (See accompanying file 5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 // 7 // See http://www.boost.org/libs/interprocess for documentation. 8 // 9 ////////////////////////////////////////////////////////////////////////////// 10 11 #ifndef BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP 12 #define BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP 13 14 #ifndef BOOST_CONFIG_HPP 15 # include <boost/config.hpp> 16 #endif 17 # 18 #if defined(BOOST_HAS_PRAGMA_ONCE) 19 #pragma once 20 #endif 21 22 #include <boost/interprocess/detail/config_begin.hpp> 23 #include <boost/interprocess/detail/workaround.hpp> 24 #include <boost/container/string.hpp> 25 26 #if !defined(BOOST_INTERPROCESS_WINDOWS) 27 #error "This header can't be included from non-windows operating systems" 28 #endif 29 30 #include <boost/assert.hpp> 31 #include <boost/interprocess/detail/intermodule_singleton_common.hpp> 32 #include <boost/interprocess/sync/windows/winapi_semaphore_wrapper.hpp> 33 #include <boost/interprocess/sync/windows/winapi_mutex_wrapper.hpp> 34 #include <boost/interprocess/sync/scoped_lock.hpp> 35 #include <boost/cstdint.hpp> 36 #include <string> 37 #include <boost/container/map.hpp> 38 39 namespace boost{ 40 namespace interprocess{ 41 namespace ipcdetail{ 42 43 namespace intermodule_singleton_helpers { 44 45 //This global map will be implemented using 3 sync primitives: 46 // 47 //1) A named mutex that will implement global mutual exclusion between 48 // threads from different modules/dlls 49 // 50 //2) A semaphore that will act as a global counter for modules attached to the global map 51 // so that the global map can be destroyed when the last module is detached. 52 // 53 //3) A semaphore that will be hacked to hold the address of a heap-allocated map in the 54 // max and current semaphore count. 55 class windows_semaphore_based_map 56 { 57 typedef boost::container::map<boost::container::string, ref_count_ptr> map_type; 58 59 public: windows_semaphore_based_map()60 windows_semaphore_based_map() 61 { 62 map_type *m = new map_type; 63 boost::uint32_t initial_count = 0; 64 boost::uint32_t max_count = 0; 65 66 //Windows user address space sizes: 67 //32 bit windows: [32 bit processes] 2GB or 3GB (31/32 bits) 68 //64 bit windows: [32 bit processes] 2GB or 4GB (31/32 bits) 69 // [64 bit processes] 2GB or 8TB (31/43 bits) 70 // 71 //Windows semaphores use 'long' parameters (32 bits in LLP64 data model) and 72 //those values can't be negative, so we have 31 bits to store something 73 //in max_count and initial count parameters. 74 //Also, max count must be bigger than 0 and bigger or equal than initial count. 75 if(sizeof(void*) == sizeof(boost::uint32_t)){ 76 //This means that for 32 bit processes, a semaphore count (31 usable bits) is 77 //enough to store 4 byte aligned memory (4GB -> 32 bits - 2 bits = 30 bits). 78 //The max count will hold the pointer value and current semaphore count 79 //will be zero. 80 // 81 //Relying in UB with a cast through union, but all known windows compilers 82 //accept this (C11 also accepts this). 83 union caster_union 84 { 85 void *addr; 86 boost::uint32_t addr_uint32; 87 } caster; 88 caster.addr = m; 89 //memory is at least 4 byte aligned in windows 90 BOOST_ASSERT((caster.addr_uint32 & boost::uint32_t(3)) == 0); 91 max_count = caster.addr_uint32 >> 2; 92 } 93 else if(sizeof(void*) == sizeof(boost::uint64_t)){ 94 //Relying in UB with a cast through union, but all known windows compilers 95 //accept this (C11 accepts this). 96 union caster_union 97 { 98 void *addr; 99 boost::uint64_t addr_uint64; 100 } caster; 101 caster.addr = m; 102 //We'll encode the address using 30 bits in each 32 bit high and low parts. 103 //High part will be the sem max count, low part will be the sem initial count. 104 //(restrictions: max count > 0, initial count >= 0 and max count >= initial count): 105 // 106 // - Low part will be shifted two times (4 byte alignment) so that top 107 // two bits are cleared (the top one for sign, the next one to 108 // assure low part value is always less than the high part value. 109 // - The top bit of the high part will be cleared and the next bit will be 1 110 // (so high part is always bigger than low part due to the quasi-top bit). 111 // 112 // This means that the addresses we can store must be 4 byte aligned 113 // and less than 1 ExbiBytes ( 2^60 bytes, ~1 ExaByte). User-level address space in Windows 64 114 // is much less than this (8TB, 2^43 bytes): "1 EByte (or it was 640K?) ought to be enough for anybody" ;-). 115 caster.addr = m; 116 BOOST_ASSERT((caster.addr_uint64 & boost::uint64_t(3)) == 0); 117 max_count = boost::uint32_t(caster.addr_uint64 >> 32); 118 initial_count = boost::uint32_t(caster.addr_uint64); 119 initial_count = initial_count/4; 120 //Make sure top two bits are zero 121 BOOST_ASSERT((max_count & boost::uint32_t(0xC0000000)) == 0); 122 //Set quasi-top bit 123 max_count |= boost::uint32_t(0x40000000); 124 } 125 bool created = false; 126 const permissions & perm = permissions(); 127 std::string pid_creation_time, name; 128 get_pid_creation_time_str(pid_creation_time); 129 name = "bipc_gmap_sem_lock_"; 130 name += pid_creation_time; 131 bool success = m_mtx_lock.open_or_create(name.c_str(), perm); 132 name = "bipc_gmap_sem_count_"; 133 name += pid_creation_time; 134 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 135 { 136 success = success && m_sem_count.open_or_create 137 ( name.c_str(), static_cast<long>(0), winapi_semaphore_wrapper::MaxCount, perm, created); 138 name = "bipc_gmap_sem_map_"; 139 name += pid_creation_time; 140 success = success && m_sem_map.open_or_create 141 (name.c_str(), initial_count, max_count, perm, created); 142 if(!success){ 143 delete m; 144 //winapi_xxx wrappers do the cleanup... 145 throw int(0); 146 } 147 if(!created){ 148 delete m; 149 } 150 else{ 151 BOOST_ASSERT(&get_map_unlocked() == m); 152 } 153 m_sem_count.post(); 154 } 155 } 156 get_map_unlocked()157 map_type &get_map_unlocked() 158 { 159 if(sizeof(void*) == sizeof(boost::uint32_t)){ 160 union caster_union 161 { 162 void *addr; 163 boost::uint32_t addr_uint32; 164 } caster; 165 caster.addr = 0; 166 caster.addr_uint32 = m_sem_map.limit(); 167 caster.addr_uint32 = caster.addr_uint32 << 2; 168 return *static_cast<map_type*>(caster.addr); 169 } 170 else{ 171 union caster_union 172 { 173 void *addr; 174 boost::uint64_t addr_uint64; 175 } caster; 176 boost::uint32_t max_count(m_sem_map.limit()), initial_count(m_sem_map.value()); 177 //Clear quasi-top bit 178 max_count &= boost::uint32_t(0xBFFFFFFF); 179 caster.addr_uint64 = max_count; 180 caster.addr_uint64 = caster.addr_uint64 << 32; 181 caster.addr_uint64 |= boost::uint64_t(initial_count) << 2; 182 return *static_cast<map_type*>(caster.addr); 183 } 184 } 185 find(const char * name)186 ref_count_ptr *find(const char *name) 187 { 188 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 189 map_type &map = this->get_map_unlocked(); 190 map_type::iterator it = map.find(boost::container::string(name)); 191 if(it != map.end()){ 192 return &it->second; 193 } 194 else{ 195 return 0; 196 } 197 } 198 insert(const char * name,const ref_count_ptr & ref)199 ref_count_ptr * insert(const char *name, const ref_count_ptr &ref) 200 { 201 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 202 map_type &map = this->get_map_unlocked(); 203 map_type::iterator it = map.insert(map_type::value_type(boost::container::string(name), ref)).first; 204 return &it->second; 205 } 206 erase(const char * name)207 bool erase(const char *name) 208 { 209 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 210 map_type &map = this->get_map_unlocked(); 211 return map.erase(boost::container::string(name)) != 0; 212 } 213 214 template<class F> atomic_func(F & f)215 void atomic_func(F &f) 216 { 217 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 218 f(); 219 } 220 ~windows_semaphore_based_map()221 ~windows_semaphore_based_map() 222 { 223 scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock); 224 m_sem_count.wait(); 225 if(0 == m_sem_count.value()){ 226 map_type &map = this->get_map_unlocked(); 227 BOOST_ASSERT(map.empty()); 228 delete ↦ 229 } 230 //First close sems to protect this with the external mutex 231 m_sem_map.close(); 232 m_sem_count.close(); 233 //Once scoped_lock unlocks the mutex, the destructor will close the handle... 234 } 235 236 private: 237 winapi_mutex_wrapper m_mtx_lock; 238 winapi_semaphore_wrapper m_sem_map; 239 winapi_semaphore_wrapper m_sem_count; 240 }; 241 242 template<> 243 struct thread_safe_global_map_dependant<windows_semaphore_based_map> 244 { apply_gmem_erase_logicboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant245 static void apply_gmem_erase_logic(const char *, const char *){} 246 remove_old_gmemboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant247 static bool remove_old_gmem() 248 { return true; } 249 250 struct lock_file_logic 251 { lock_file_logicboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant::lock_file_logic252 lock_file_logic(windows_semaphore_based_map &) 253 : retry_with_new_map(false) 254 {} 255 operator ()boost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant::lock_file_logic256 void operator()(void){} retryboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant::lock_file_logic257 bool retry() const { return retry_with_new_map; } 258 private: 259 const bool retry_with_new_map; 260 }; 261 construct_mapboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant262 static void construct_map(void *addr) 263 { 264 ::new (addr)windows_semaphore_based_map; 265 } 266 267 struct unlink_map_logic 268 { unlink_map_logicboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant::unlink_map_logic269 unlink_map_logic(windows_semaphore_based_map &) 270 {} operator ()boost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant::unlink_map_logic271 void operator()(){} 272 }; 273 findboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant274 static ref_count_ptr *find(windows_semaphore_based_map &map, const char *name) 275 { 276 return map.find(name); 277 } 278 insertboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant279 static ref_count_ptr * insert(windows_semaphore_based_map &map, const char *name, const ref_count_ptr &ref) 280 { 281 return map.insert(name, ref); 282 } 283 eraseboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant284 static bool erase(windows_semaphore_based_map &map, const char *name) 285 { 286 return map.erase(name); 287 } 288 289 template<class F> atomic_funcboost::interprocess::ipcdetail::intermodule_singleton_helpers::thread_safe_global_map_dependant290 static void atomic_func(windows_semaphore_based_map &map, F &f) 291 { 292 map.atomic_func(f); 293 } 294 }; 295 296 } //namespace intermodule_singleton_helpers { 297 298 template<typename C, bool LazyInit = true, bool Phoenix = false> 299 class windows_intermodule_singleton 300 : public intermodule_singleton_impl 301 < C 302 , LazyInit 303 , Phoenix 304 , intermodule_singleton_helpers::windows_semaphore_based_map 305 > 306 {}; 307 308 } //namespace ipcdetail{ 309 } //namespace interprocess{ 310 } //namespace boost{ 311 312 #include <boost/interprocess/detail/config_end.hpp> 313 314 #endif //#ifndef BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP 315