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