307 lines
10 KiB
C++
307 lines
10 KiB
C++
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// (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
|
|
|
|
#if defined(_MSC_VER)&&(_MSC_VER>=1200)
|
|
#pragma once
|
|
#endif
|
|
|
|
#include <boost/interprocess/detail/config_begin.hpp>
|
|
#include <boost/interprocess/detail/workaround.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 <map>
|
|
|
|
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 std::map<std::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){
|
|
//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(std::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(std::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(std::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()){
|
|
delete &this->get_map_unlocked();
|
|
}
|
|
//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 = true>
|
|
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
|