315 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| //////////////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| // (C) Copyright Ion Gaztanaga 2009-2012. Distributed under the Boost
 | |
| // Software License, Version 1.0. (See accompanying file
 | |
| // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
| //
 | |
| // See http://www.boost.org/libs/interprocess for documentation.
 | |
| //
 | |
| //////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| #ifndef BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP
 | |
| #define BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP
 | |
| 
 | |
| #ifndef BOOST_CONFIG_HPP
 | |
| #  include <boost/config.hpp>
 | |
| #endif
 | |
| #
 | |
| #if defined(BOOST_HAS_PRAGMA_ONCE)
 | |
| #pragma once
 | |
| #endif
 | |
| 
 | |
| #include <boost/interprocess/detail/config_begin.hpp>
 | |
| #include <boost/interprocess/detail/workaround.hpp>
 | |
| #include <boost/container/string.hpp>
 | |
| 
 | |
| #if !defined(BOOST_INTERPROCESS_WINDOWS)
 | |
|    #error "This header can't be included from non-windows operating systems"
 | |
| #endif
 | |
| 
 | |
| #include <boost/assert.hpp>
 | |
| #include <boost/interprocess/detail/intermodule_singleton_common.hpp>
 | |
| #include <boost/interprocess/sync/windows/winapi_semaphore_wrapper.hpp>
 | |
| #include <boost/interprocess/sync/windows/winapi_mutex_wrapper.hpp>
 | |
| #include <boost/interprocess/sync/scoped_lock.hpp>
 | |
| #include <boost/cstdint.hpp>
 | |
| #include <string>
 | |
| #include <boost/container/map.hpp>
 | |
| 
 | |
| namespace boost{
 | |
| namespace interprocess{
 | |
| namespace ipcdetail{
 | |
| 
 | |
| namespace intermodule_singleton_helpers {
 | |
| 
 | |
| //This global map will be implemented using 3 sync primitives:
 | |
| //
 | |
| //1)  A named mutex that will implement global mutual exclusion between
 | |
| //    threads from different modules/dlls
 | |
| //
 | |
| //2)  A semaphore that will act as a global counter for modules attached to the global map
 | |
| //    so that the global map can be destroyed when the last module is detached.
 | |
| //
 | |
| //3)  A semaphore that will be hacked to hold the address of a heap-allocated map in the
 | |
| //    max and current semaphore count.
 | |
| class windows_semaphore_based_map
 | |
| {
 | |
|    typedef boost::container::map<boost::container::string, ref_count_ptr> map_type;
 | |
| 
 | |
|    public:
 | |
|    windows_semaphore_based_map()
 | |
|    {
 | |
|       map_type *m = new map_type;
 | |
|       boost::uint32_t initial_count = 0;
 | |
|       boost::uint32_t max_count = 0;
 | |
| 
 | |
|       //Windows user address space sizes:
 | |
|       //32 bit windows: [32 bit processes] 2GB or 3GB (31/32 bits)
 | |
|       //64 bit windows: [32 bit processes] 2GB or 4GB (31/32 bits)
 | |
|       //                [64 bit processes] 2GB or 8TB (31/43 bits)
 | |
|       //
 | |
|       //Windows semaphores use 'long' parameters (32 bits in LLP64 data model) and
 | |
|       //those values can't be negative, so we have 31 bits to store something
 | |
|       //in max_count and initial count parameters.
 | |
|       //Also, max count must be bigger than 0 and bigger or equal than initial count.
 | |
|       if(sizeof(void*) == sizeof(boost::uint32_t)){
 | |
|          //This means that for 32 bit processes, a semaphore count (31 usable bits) is
 | |
|          //enough to store 4 byte aligned memory (4GB -> 32 bits - 2 bits = 30 bits).
 | |
|          //The max count will hold the pointer value and current semaphore count
 | |
|          //will be zero.
 | |
|          //
 | |
|          //Relying in UB with a cast through union, but all known windows compilers
 | |
|          //accept this (C11 also accepts this).
 | |
|          union caster_union
 | |
|          {
 | |
|             void *addr;
 | |
|             boost::uint32_t addr_uint32;
 | |
|          } caster;
 | |
|          caster.addr = m;
 | |
|          //memory is at least 4 byte aligned in windows
 | |
|          BOOST_ASSERT((caster.addr_uint32 & boost::uint32_t(3)) == 0);
 | |
|          max_count = caster.addr_uint32 >> 2;
 | |
|       }
 | |
|       else if(sizeof(void*) == sizeof(boost::uint64_t)){
 | |
|          //Relying in UB with a cast through union, but all known windows compilers
 | |
|          //accept this (C11 accepts this).
 | |
|          union caster_union
 | |
|          {
 | |
|             void *addr;
 | |
|             boost::uint64_t addr_uint64;
 | |
|          } caster;
 | |
|          caster.addr = m;
 | |
|          //We'll encode the address using 30 bits in each 32 bit high and low parts.
 | |
|          //High part will be the sem max count, low part will be the sem initial count.
 | |
|          //(restrictions: max count > 0, initial count >= 0 and max count >= initial count):
 | |
|          //
 | |
|          // - Low part will be shifted two times (4 byte alignment) so that top
 | |
|          //   two bits are cleared (the top one for sign, the next one to
 | |
|          //   assure low part value is always less than the high part value.
 | |
|          // - The top bit of the high part will be cleared and the next bit will be 1
 | |
|          //   (so high part is always bigger than low part due to the quasi-top bit).
 | |
|          //
 | |
|          //   This means that the addresses we can store must be 4 byte aligned
 | |
|          //   and less than 1 ExbiBytes ( 2^60 bytes, ~1 ExaByte). User-level address space in Windows 64
 | |
|          //   is much less than this (8TB, 2^43 bytes): "1 EByte (or it was 640K?) ought to be enough for anybody" ;-).
 | |
|          caster.addr = m;
 | |
|          BOOST_ASSERT((caster.addr_uint64 & boost::uint64_t(3)) == 0);
 | |
|          max_count = boost::uint32_t(caster.addr_uint64 >> 32);
 | |
|          initial_count = boost::uint32_t(caster.addr_uint64);
 | |
|          initial_count = initial_count/4;
 | |
|          //Make sure top two bits are zero
 | |
|          BOOST_ASSERT((max_count & boost::uint32_t(0xC0000000)) == 0);
 | |
|          //Set quasi-top bit
 | |
|          max_count |= boost::uint32_t(0x40000000);
 | |
|       }
 | |
|       bool created = false;
 | |
|       const permissions & perm = permissions();
 | |
|       std::string pid_creation_time, name;
 | |
|       get_pid_creation_time_str(pid_creation_time);
 | |
|       name = "bipc_gmap_sem_lock_";
 | |
|       name += pid_creation_time;
 | |
|       bool success = m_mtx_lock.open_or_create(name.c_str(), perm);
 | |
|       name = "bipc_gmap_sem_count_";
 | |
|       name += pid_creation_time;
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       {
 | |
|          success = success && m_sem_count.open_or_create
 | |
|             ( name.c_str(), static_cast<long>(0), winapi_semaphore_wrapper::MaxCount, perm, created);
 | |
|          name = "bipc_gmap_sem_map_";
 | |
|          name += pid_creation_time;
 | |
|          success = success && m_sem_map.open_or_create
 | |
|             (name.c_str(), initial_count, max_count, perm, created);
 | |
|          if(!success){
 | |
|             delete m;
 | |
|             //winapi_xxx wrappers do the cleanup...
 | |
|             throw int(0);
 | |
|          }
 | |
|          if(!created){
 | |
|             delete m;
 | |
|          }
 | |
|          else{
 | |
|             BOOST_ASSERT(&get_map_unlocked() == m);
 | |
|          }
 | |
|          m_sem_count.post();
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    map_type &get_map_unlocked()
 | |
|    {
 | |
|       if(sizeof(void*) == sizeof(boost::uint32_t)){
 | |
|          union caster_union
 | |
|          {
 | |
|             void *addr;
 | |
|             boost::uint32_t addr_uint32;
 | |
|          } caster;
 | |
|          caster.addr = 0;
 | |
|          caster.addr_uint32 = m_sem_map.limit();
 | |
|          caster.addr_uint32 = caster.addr_uint32 << 2;
 | |
|          return *static_cast<map_type*>(caster.addr);
 | |
|       }
 | |
|       else{
 | |
|          union caster_union
 | |
|          {
 | |
|             void *addr;
 | |
|             boost::uint64_t addr_uint64;
 | |
|          } caster;
 | |
|          boost::uint32_t max_count(m_sem_map.limit()), initial_count(m_sem_map.value());
 | |
|          //Clear quasi-top bit
 | |
|          max_count &= boost::uint32_t(0xBFFFFFFF);
 | |
|          caster.addr_uint64 = max_count;
 | |
|          caster.addr_uint64 =  caster.addr_uint64 << 32;
 | |
|          caster.addr_uint64 |= boost::uint64_t(initial_count) << 2;
 | |
|          return *static_cast<map_type*>(caster.addr);
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    ref_count_ptr *find(const char *name)
 | |
|    {
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       map_type &map = this->get_map_unlocked();
 | |
|       map_type::iterator it = map.find(boost::container::string(name));
 | |
|       if(it != map.end()){
 | |
|          return &it->second;
 | |
|       }
 | |
|       else{
 | |
|          return 0;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    ref_count_ptr * insert(const char *name, const ref_count_ptr &ref)
 | |
|    {
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       map_type &map = this->get_map_unlocked();
 | |
|       map_type::iterator it = map.insert(map_type::value_type(boost::container::string(name), ref)).first;
 | |
|       return &it->second;
 | |
|    }
 | |
| 
 | |
|    bool erase(const char *name)
 | |
|    {
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       map_type &map = this->get_map_unlocked();
 | |
|       return map.erase(boost::container::string(name)) != 0;
 | |
|    }
 | |
| 
 | |
|    template<class F>
 | |
|    void atomic_func(F &f)
 | |
|    {
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       f();
 | |
|    }
 | |
| 
 | |
|    ~windows_semaphore_based_map()
 | |
|    {
 | |
|       scoped_lock<winapi_mutex_wrapper> lck(m_mtx_lock);
 | |
|       m_sem_count.wait();
 | |
|       if(0 == m_sem_count.value()){
 | |
|          map_type &map = this->get_map_unlocked();
 | |
|          BOOST_ASSERT(map.empty());
 | |
|          delete ↦
 | |
|       }
 | |
|       //First close sems to protect this with the external mutex
 | |
|       m_sem_map.close();
 | |
|       m_sem_count.close();
 | |
|       //Once scoped_lock unlocks the mutex, the destructor will close the handle...
 | |
|    }
 | |
| 
 | |
|    private:
 | |
|    winapi_mutex_wrapper     m_mtx_lock;
 | |
|    winapi_semaphore_wrapper m_sem_map;
 | |
|    winapi_semaphore_wrapper m_sem_count;
 | |
| };
 | |
| 
 | |
| template<>
 | |
| struct thread_safe_global_map_dependant<windows_semaphore_based_map>
 | |
| {
 | |
|    static void apply_gmem_erase_logic(const char *, const char *){}
 | |
| 
 | |
|    static bool remove_old_gmem()
 | |
|    { return true; }
 | |
| 
 | |
|    struct lock_file_logic
 | |
|    {
 | |
|       lock_file_logic(windows_semaphore_based_map &)
 | |
|          : retry_with_new_map(false)
 | |
|       {}
 | |
| 
 | |
|       void operator()(void){}
 | |
|       bool retry() const { return retry_with_new_map; }
 | |
|       private:
 | |
|       const bool retry_with_new_map;
 | |
|    };
 | |
| 
 | |
|    static void construct_map(void *addr)
 | |
|    {
 | |
|       ::new (addr)windows_semaphore_based_map;
 | |
|    }
 | |
| 
 | |
|    struct unlink_map_logic
 | |
|    {
 | |
|       unlink_map_logic(windows_semaphore_based_map &)
 | |
|       {}
 | |
|       void operator()(){}
 | |
|    };
 | |
| 
 | |
|    static ref_count_ptr *find(windows_semaphore_based_map &map, const char *name)
 | |
|    {
 | |
|       return map.find(name);
 | |
|    }
 | |
| 
 | |
|    static ref_count_ptr * insert(windows_semaphore_based_map &map, const char *name, const ref_count_ptr &ref)
 | |
|    {
 | |
|       return map.insert(name, ref);
 | |
|    }
 | |
| 
 | |
|    static bool erase(windows_semaphore_based_map &map, const char *name)
 | |
|    {
 | |
|       return map.erase(name);
 | |
|    }
 | |
| 
 | |
|    template<class F>
 | |
|    static void atomic_func(windows_semaphore_based_map &map, F &f)
 | |
|    {
 | |
|       map.atomic_func(f);
 | |
|    }
 | |
| };
 | |
| 
 | |
| }  //namespace intermodule_singleton_helpers {
 | |
| 
 | |
| template<typename C, bool LazyInit = true, bool Phoenix = false>
 | |
| class windows_intermodule_singleton
 | |
|    : public intermodule_singleton_impl
 | |
|       < C
 | |
|       , LazyInit
 | |
|       , Phoenix
 | |
|       , intermodule_singleton_helpers::windows_semaphore_based_map
 | |
|       >
 | |
| {};
 | |
| 
 | |
| }  //namespace ipcdetail{
 | |
| }  //namespace interprocess{
 | |
| }  //namespace boost{
 | |
| 
 | |
| #include <boost/interprocess/detail/config_end.hpp>
 | |
| 
 | |
| #endif   //#ifndef BOOST_INTERPROCESS_WINDOWS_INTERMODULE_SINGLETON_HPP
 | 
