windows_intermodule_singleton.hpp 10 KB

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