Mercurial > hg > vamp-build-and-test
comparison DEPENDENCIES/generic/include/boost/interprocess/detail/intermodule_singleton_common.hpp @ 16:2665513ce2d3
Add boost headers
author | Chris Cannam |
---|---|
date | Tue, 05 Aug 2014 11:11:38 +0100 |
parents | |
children | c530137014c0 |
comparison
equal
deleted
inserted
replaced
15:663ca0da4350 | 16:2665513ce2d3 |
---|---|
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_INTERMODULE_SINGLETON_COMMON_HPP | |
12 #define BOOST_INTERPROCESS_INTERMODULE_SINGLETON_COMMON_HPP | |
13 | |
14 #if defined(_MSC_VER)&&(_MSC_VER>=1200) | |
15 #pragma once | |
16 #endif | |
17 | |
18 #include <boost/interprocess/detail/config_begin.hpp> | |
19 #include <boost/interprocess/detail/workaround.hpp> | |
20 | |
21 #include <boost/interprocess/detail/atomic.hpp> | |
22 #include <boost/interprocess/detail/os_thread_functions.hpp> | |
23 #include <boost/interprocess/exceptions.hpp> | |
24 #include <boost/type_traits/type_with_alignment.hpp> | |
25 #include <boost/interprocess/detail/mpl.hpp> | |
26 #include <boost/interprocess/sync/spin/wait.hpp> | |
27 #include <boost/assert.hpp> | |
28 #include <cstddef> | |
29 #include <cstdio> | |
30 #include <cstdlib> | |
31 #include <cstring> | |
32 #include <string> | |
33 #include <sstream> | |
34 | |
35 namespace boost{ | |
36 namespace interprocess{ | |
37 namespace ipcdetail{ | |
38 | |
39 namespace intermodule_singleton_helpers { | |
40 | |
41 inline void get_pid_creation_time_str(std::string &s) | |
42 { | |
43 std::stringstream stream; | |
44 stream << get_current_process_id() << '_'; | |
45 stream.precision(6); | |
46 stream << std::fixed << get_current_process_creation_time(); | |
47 s = stream.str(); | |
48 } | |
49 | |
50 inline const char *get_map_base_name() | |
51 { return "bip.gmem.map."; } | |
52 | |
53 inline void get_map_name(std::string &map_name) | |
54 { | |
55 get_pid_creation_time_str(map_name); | |
56 map_name.insert(0, get_map_base_name()); | |
57 } | |
58 | |
59 inline std::size_t get_map_size() | |
60 { return 65536; } | |
61 | |
62 template<class ThreadSafeGlobalMap> | |
63 struct thread_safe_global_map_dependant; | |
64 | |
65 } //namespace intermodule_singleton_helpers { | |
66 | |
67 //This class contains common code for all singleton types, so that we instantiate this | |
68 //code just once per module. This class also holds a thread soafe global map | |
69 //to be used by all instances protected with a reference count | |
70 template<class ThreadSafeGlobalMap> | |
71 class intermodule_singleton_common | |
72 { | |
73 public: | |
74 typedef void*(singleton_constructor_t)(ThreadSafeGlobalMap &); | |
75 typedef void (singleton_destructor_t)(void *, ThreadSafeGlobalMap &); | |
76 | |
77 static const ::boost::uint32_t Uninitialized = 0u; | |
78 static const ::boost::uint32_t Initializing = 1u; | |
79 static const ::boost::uint32_t Initialized = 2u; | |
80 static const ::boost::uint32_t Broken = 3u; | |
81 static const ::boost::uint32_t Destroyed = 4u; | |
82 | |
83 //Initialize this_module_singleton_ptr, creates the global map if needed and also creates an unique | |
84 //opaque type in global map through a singleton_constructor_t function call, | |
85 //initializing the passed pointer to that unique instance. | |
86 // | |
87 //We have two concurrency types here. a)the global map/singleton creation must | |
88 //be safe between threads of this process but in different modules/dlls. b) | |
89 //the pointer to the singleton is per-module, so we have to protect this | |
90 //initization between threads of the same module. | |
91 // | |
92 //All static variables declared here are shared between inside a module | |
93 //so atomic operations will synchronize only threads of the same module. | |
94 static void initialize_singleton_logic | |
95 (void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t constructor, bool phoenix) | |
96 { | |
97 //If current module is not initialized enter to lock free logic | |
98 if(atomic_read32(&this_module_singleton_initialized) != Initialized){ | |
99 //Now a single thread of the module will succeed in this CAS. | |
100 //trying to pass from Uninitialized to Initializing | |
101 ::boost::uint32_t previous_module_singleton_initialized = atomic_cas32 | |
102 (&this_module_singleton_initialized, Initializing, Uninitialized); | |
103 //If the thread succeeded the CAS (winner) it will compete with other | |
104 //winner threads from other modules to create the global map | |
105 if(previous_module_singleton_initialized == Destroyed){ | |
106 //Trying to resurrect a dead Phoenix singleton. Just try to | |
107 //mark it as uninitialized and start again | |
108 if(phoenix){ | |
109 atomic_cas32(&this_module_singleton_initialized, Uninitialized, Destroyed); | |
110 previous_module_singleton_initialized = atomic_cas32 | |
111 (&this_module_singleton_initialized, Initializing, Uninitialized); | |
112 } | |
113 //Trying to resurrect a non-Phoenix dead singleton is an error | |
114 else{ | |
115 throw interprocess_exception("Boost.Interprocess: Dead reference on non-Phoenix singleton of type"); | |
116 } | |
117 } | |
118 if(previous_module_singleton_initialized == Uninitialized){ | |
119 try{ | |
120 //Now initialize the global map, this function must solve concurrency | |
121 //issues between threads of several modules | |
122 initialize_global_map_handle(); | |
123 //Now try to create the singleton in global map. | |
124 //This function solves concurrency issues | |
125 //between threads of several modules | |
126 void *tmp = constructor(get_map()); | |
127 //Increment the module reference count that reflects how many | |
128 //singletons this module holds, so that we can safely destroy | |
129 //module global map object when no singleton is left | |
130 atomic_inc32(&this_module_singleton_count); | |
131 //Insert a barrier before assigning the pointer to | |
132 //make sure this assignment comes after the initialization | |
133 atomic_write32(&this_module_singleton_initialized, Initializing); | |
134 //Assign the singleton address to the module-local pointer | |
135 ptr = tmp; | |
136 //Memory barrier inserted, all previous operations should complete | |
137 //before this one. Now marked as initialized | |
138 atomic_write32(&this_module_singleton_initialized, Initialized); | |
139 } | |
140 catch(...){ | |
141 //Mark singleton failed to initialize | |
142 atomic_write32(&this_module_singleton_initialized, Broken); | |
143 throw; | |
144 } | |
145 } | |
146 //If previous state was initializing, this means that another winner thread is | |
147 //trying to initialize the singleton. Just wait until completes its work. | |
148 else if(previous_module_singleton_initialized == Initializing){ | |
149 spin_wait swait; | |
150 while(1){ | |
151 previous_module_singleton_initialized = atomic_read32(&this_module_singleton_initialized); | |
152 if(previous_module_singleton_initialized >= Initialized){ | |
153 //Already initialized, or exception thrown by initializer thread | |
154 break; | |
155 } | |
156 else if(previous_module_singleton_initialized == Initializing){ | |
157 swait.yield(); | |
158 } | |
159 else{ | |
160 //This can't be happening! | |
161 BOOST_ASSERT(0); | |
162 } | |
163 } | |
164 } | |
165 else if(previous_module_singleton_initialized == Initialized){ | |
166 //Nothing to do here, the singleton is ready | |
167 } | |
168 //If previous state was greater than initialized, then memory is broken | |
169 //trying to initialize the singleton. | |
170 else{//(previous_module_singleton_initialized > Initialized) | |
171 throw interprocess_exception("boost::interprocess::intermodule_singleton initialization failed"); | |
172 } | |
173 } | |
174 BOOST_ASSERT(ptr != 0); | |
175 } | |
176 | |
177 static void finalize_singleton_logic(void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_destructor_t destructor) | |
178 { | |
179 //Protect destruction against lazy singletons not initialized in this execution | |
180 if(ptr){ | |
181 //Note: this destructor might provoke a Phoenix singleton | |
182 //resurrection. This means that this_module_singleton_count | |
183 //might change after this call. | |
184 destructor(ptr, get_map()); | |
185 ptr = 0; | |
186 | |
187 //Memory barrier to make sure pointer is nulled. | |
188 //Mark this singleton as destroyed. | |
189 atomic_write32(&this_module_singleton_initialized, Destroyed); | |
190 | |
191 //If this is the last singleton of this module | |
192 //apply map destruction. | |
193 //Note: singletons are destroyed when the module is unloaded | |
194 //so no threads should be executing or holding references | |
195 //to this module | |
196 if(1 == atomic_dec32(&this_module_singleton_count)){ | |
197 destroy_global_map_handle(); | |
198 } | |
199 } | |
200 } | |
201 | |
202 private: | |
203 static ThreadSafeGlobalMap &get_map() | |
204 { | |
205 return *static_cast<ThreadSafeGlobalMap *>(static_cast<void *>(&mem_holder.map_mem[0])); | |
206 } | |
207 | |
208 static void initialize_global_map_handle() | |
209 { | |
210 //Obtain unique map name and size | |
211 spin_wait swait; | |
212 while(1){ | |
213 //Try to pass map state to initializing | |
214 ::boost::uint32_t tmp = atomic_cas32(&this_module_map_initialized, Initializing, Uninitialized); | |
215 if(tmp == Initialized || tmp == Broken){ | |
216 break; | |
217 } | |
218 else if(tmp == Destroyed){ | |
219 tmp = atomic_cas32(&this_module_map_initialized, Uninitialized, Destroyed); | |
220 continue; | |
221 } | |
222 //If some other thread is doing the work wait | |
223 else if(tmp == Initializing){ | |
224 swait.yield(); | |
225 } | |
226 else{ //(tmp == Uninitialized) | |
227 //If not initialized try it again? | |
228 try{ | |
229 //Remove old global map from the system | |
230 intermodule_singleton_helpers::thread_safe_global_map_dependant<ThreadSafeGlobalMap>::remove_old_gmem(); | |
231 //in-place construction of the global map class | |
232 intermodule_singleton_helpers::thread_safe_global_map_dependant | |
233 <ThreadSafeGlobalMap>::construct_map(static_cast<void*>(&get_map())); | |
234 //Use global map's internal lock to initialize the lock file | |
235 //that will mark this gmem as "in use". | |
236 typename intermodule_singleton_helpers::thread_safe_global_map_dependant<ThreadSafeGlobalMap>:: | |
237 lock_file_logic f(get_map()); | |
238 //If function failed (maybe a competing process has erased the shared | |
239 //memory between creation and file locking), retry with a new instance. | |
240 if(f.retry()){ | |
241 get_map().~ThreadSafeGlobalMap(); | |
242 atomic_write32(&this_module_map_initialized, Destroyed); | |
243 } | |
244 else{ | |
245 //Locking succeeded, so this global map module-instance is ready | |
246 atomic_write32(&this_module_map_initialized, Initialized); | |
247 break; | |
248 } | |
249 } | |
250 catch(...){ | |
251 // | |
252 throw; | |
253 } | |
254 } | |
255 } | |
256 } | |
257 | |
258 static void destroy_global_map_handle() | |
259 { | |
260 if(!atomic_read32(&this_module_singleton_count)){ | |
261 //This module is being unloaded, so destroy | |
262 //the global map object of this module | |
263 //and unlink the global map if it's the last | |
264 typename intermodule_singleton_helpers::thread_safe_global_map_dependant<ThreadSafeGlobalMap>:: | |
265 unlink_map_logic f(get_map()); | |
266 (get_map()).~ThreadSafeGlobalMap(); | |
267 atomic_write32(&this_module_map_initialized, Destroyed); | |
268 //Do some cleanup for other processes old gmem instances | |
269 intermodule_singleton_helpers::thread_safe_global_map_dependant<ThreadSafeGlobalMap>::remove_old_gmem(); | |
270 } | |
271 } | |
272 | |
273 //Static data, zero-initalized without any dependencies | |
274 //this_module_singleton_count is the number of singletons used by this module | |
275 static volatile boost::uint32_t this_module_singleton_count; | |
276 | |
277 //this_module_map_initialized is the state of this module's map class object. | |
278 //Values: Uninitialized, Initializing, Initialized, Broken | |
279 static volatile boost::uint32_t this_module_map_initialized; | |
280 | |
281 //Raw memory to construct the global map manager | |
282 static struct mem_holder_t | |
283 { | |
284 ::boost::detail::max_align aligner; | |
285 char map_mem [sizeof(ThreadSafeGlobalMap)]; | |
286 } mem_holder; | |
287 }; | |
288 | |
289 template<class ThreadSafeGlobalMap> | |
290 volatile boost::uint32_t intermodule_singleton_common<ThreadSafeGlobalMap>::this_module_singleton_count; | |
291 | |
292 template<class ThreadSafeGlobalMap> | |
293 volatile boost::uint32_t intermodule_singleton_common<ThreadSafeGlobalMap>::this_module_map_initialized; | |
294 | |
295 template<class ThreadSafeGlobalMap> | |
296 typename intermodule_singleton_common<ThreadSafeGlobalMap>::mem_holder_t | |
297 intermodule_singleton_common<ThreadSafeGlobalMap>::mem_holder; | |
298 | |
299 //A reference count to be stored in global map holding the number | |
300 //of singletons (one per module) attached to the instance pointed by | |
301 //the internal ptr. | |
302 struct ref_count_ptr | |
303 { | |
304 ref_count_ptr(void *p, boost::uint32_t count) | |
305 : ptr(p), singleton_ref_count(count) | |
306 {} | |
307 void *ptr; | |
308 //This reference count serves to count the number of attached | |
309 //modules to this singleton | |
310 volatile boost::uint32_t singleton_ref_count; | |
311 }; | |
312 | |
313 | |
314 //Now this class is a singleton, initializing the singleton in | |
315 //the first get() function call if LazyInit is true. If false | |
316 //then the singleton will be initialized when loading the module. | |
317 template<typename C, bool LazyInit, bool Phoenix, class ThreadSafeGlobalMap> | |
318 class intermodule_singleton_impl | |
319 { | |
320 public: | |
321 | |
322 static C& get() //Let's make inlining easy | |
323 { | |
324 if(!this_module_singleton_ptr){ | |
325 if(lifetime.dummy_function()){ //This forces lifetime instantiation, for reference counted destruction | |
326 atentry_work(); | |
327 } | |
328 } | |
329 return *static_cast<C*>(this_module_singleton_ptr); | |
330 } | |
331 | |
332 private: | |
333 | |
334 static void atentry_work() | |
335 { | |
336 intermodule_singleton_common<ThreadSafeGlobalMap>::initialize_singleton_logic | |
337 (this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor, Phoenix); | |
338 } | |
339 | |
340 static void atexit_work() | |
341 { | |
342 intermodule_singleton_common<ThreadSafeGlobalMap>::finalize_singleton_logic | |
343 (this_module_singleton_ptr, this_module_singleton_initialized, singleton_destructor); | |
344 } | |
345 | |
346 //These statics will be zero-initialized without any constructor call dependency | |
347 //this_module_singleton_ptr will be a module-local pointer to the singleton | |
348 static void* this_module_singleton_ptr; | |
349 | |
350 //this_module_singleton_count will be used to synchronize threads of the same module | |
351 //for access to a singleton instance, and to flag the state of the | |
352 //singleton. | |
353 static volatile boost::uint32_t this_module_singleton_initialized; | |
354 | |
355 //This class destructor will trigger singleton destruction | |
356 struct lifetime_type_lazy | |
357 { | |
358 bool dummy_function() | |
359 { return m_dummy == 0; } | |
360 | |
361 ~lifetime_type_lazy() | |
362 { | |
363 if(!Phoenix){ | |
364 atexit_work(); | |
365 } | |
366 } | |
367 | |
368 //Dummy volatile so that the compiler can't resolve its value at compile-time | |
369 //and can't avoid lifetime_type instantiation if dummy_function() is called. | |
370 static volatile int m_dummy; | |
371 }; | |
372 | |
373 struct lifetime_type_static | |
374 : public lifetime_type_lazy | |
375 { | |
376 lifetime_type_static() | |
377 { atentry_work(); } | |
378 }; | |
379 | |
380 typedef typename if_c | |
381 <LazyInit, lifetime_type_lazy, lifetime_type_static>::type lifetime_type; | |
382 | |
383 static lifetime_type lifetime; | |
384 | |
385 //A functor to be executed inside global map lock that just | |
386 //searches for the singleton in map and if not present creates a new one. | |
387 //If singleton constructor throws, the exception is propagated | |
388 struct init_atomic_func | |
389 { | |
390 init_atomic_func(ThreadSafeGlobalMap &m) | |
391 : m_map(m) | |
392 {} | |
393 | |
394 void operator()() | |
395 { | |
396 ref_count_ptr *rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant | |
397 <ThreadSafeGlobalMap>::find(m_map, typeid(C).name()); | |
398 if(!rcount){ | |
399 C *p = new C; | |
400 try{ | |
401 ref_count_ptr val(p, 0u); | |
402 rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant | |
403 <ThreadSafeGlobalMap>::insert(m_map, typeid(C).name(), val); | |
404 } | |
405 catch(...){ | |
406 intermodule_singleton_helpers::thread_safe_global_map_dependant | |
407 <ThreadSafeGlobalMap>::erase(m_map, typeid(C).name()); | |
408 delete p; | |
409 throw; | |
410 } | |
411 } | |
412 if(Phoenix){ | |
413 std::atexit(&atexit_work); | |
414 } | |
415 atomic_inc32(&rcount->singleton_ref_count); | |
416 ret_ptr = rcount->ptr; | |
417 } | |
418 void *data() const | |
419 { return ret_ptr; } | |
420 | |
421 private: | |
422 ThreadSafeGlobalMap &m_map; | |
423 void *ret_ptr; | |
424 }; | |
425 | |
426 //A functor to be executed inside global map lock that just | |
427 //deletes the singleton in map if the attached count reaches to zero | |
428 struct fini_atomic_func | |
429 { | |
430 fini_atomic_func(ThreadSafeGlobalMap &m) | |
431 : m_map(m) | |
432 {} | |
433 | |
434 void operator()() | |
435 { | |
436 ref_count_ptr *rcount = intermodule_singleton_helpers::thread_safe_global_map_dependant | |
437 <ThreadSafeGlobalMap>::find(m_map, typeid(C).name()); | |
438 //The object must exist | |
439 BOOST_ASSERT(rcount); | |
440 BOOST_ASSERT(rcount->singleton_ref_count > 0); | |
441 //Check if last reference | |
442 if(atomic_dec32(&rcount->singleton_ref_count) == 1){ | |
443 //If last, destroy the object | |
444 BOOST_ASSERT(rcount->ptr != 0); | |
445 C *pc = static_cast<C*>(rcount->ptr); | |
446 //Now destroy map entry | |
447 bool destroyed = intermodule_singleton_helpers::thread_safe_global_map_dependant | |
448 <ThreadSafeGlobalMap>::erase(m_map, typeid(C).name()); | |
449 (void)destroyed; BOOST_ASSERT(destroyed == true); | |
450 delete pc; | |
451 } | |
452 } | |
453 void *data() const | |
454 { return ret_ptr; } | |
455 | |
456 private: | |
457 ThreadSafeGlobalMap &m_map; | |
458 void *ret_ptr; | |
459 }; | |
460 | |
461 //A wrapper to execute init_atomic_func | |
462 static void *singleton_constructor(ThreadSafeGlobalMap &map) | |
463 { | |
464 init_atomic_func f(map); | |
465 intermodule_singleton_helpers::thread_safe_global_map_dependant | |
466 <ThreadSafeGlobalMap>::atomic_func(map, f); | |
467 return f.data(); | |
468 } | |
469 | |
470 //A wrapper to execute fini_atomic_func | |
471 static void singleton_destructor(void *p, ThreadSafeGlobalMap &map) | |
472 { (void)p; | |
473 fini_atomic_func f(map); | |
474 intermodule_singleton_helpers::thread_safe_global_map_dependant | |
475 <ThreadSafeGlobalMap>::atomic_func(map, f); | |
476 } | |
477 }; | |
478 | |
479 template <typename C, bool L, bool P, class ThreadSafeGlobalMap> | |
480 volatile int intermodule_singleton_impl<C, L, P, ThreadSafeGlobalMap>::lifetime_type_lazy::m_dummy = 0; | |
481 | |
482 //These will be zero-initialized by the loader | |
483 template <typename C, bool L, bool P, class ThreadSafeGlobalMap> | |
484 void *intermodule_singleton_impl<C, L, P, ThreadSafeGlobalMap>::this_module_singleton_ptr = 0; | |
485 | |
486 template <typename C, bool L, bool P, class ThreadSafeGlobalMap> | |
487 volatile boost::uint32_t intermodule_singleton_impl<C, L, P, ThreadSafeGlobalMap>::this_module_singleton_initialized = 0; | |
488 | |
489 template <typename C, bool L, bool P, class ThreadSafeGlobalMap> | |
490 typename intermodule_singleton_impl<C, L, P, ThreadSafeGlobalMap>::lifetime_type | |
491 intermodule_singleton_impl<C, L, P, ThreadSafeGlobalMap>::lifetime; | |
492 | |
493 } //namespace ipcdetail{ | |
494 } //namespace interprocess{ | |
495 } //namespace boost{ | |
496 | |
497 #include <boost/interprocess/detail/config_end.hpp> | |
498 | |
499 #endif //#ifndef BOOST_INTERPROCESS_INTERMODULE_SINGLETON_COMMON_HPP |