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