windows_intermodule_singleton.hpp 10 KB

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