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