blob: 8ef76a64fc1834aed6083b9e3d5755d17ebe7f04 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2009-2009. 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_INTERMODULE_SINGLETON_HPP
#define BOOST_INTERPROCESS_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>
#include <boost/interprocess/managed_shared_memory.hpp>
#ifdef BOOST_INTERPROCESS_WINDOWS
#include <boost/interprocess/managed_windows_shared_memory.hpp>
#endif
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/interprocess/detail/os_thread_functions.hpp>
#include <boost/interprocess/detail/tmp_dir_helpers.hpp>
#include <boost/interprocess/detail/os_file_functions.hpp>
#include <boost/interprocess/detail/mpl.hpp>
#include <boost/assert.hpp>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#if defined (BOOST_INTERPROCESS_WINDOWS)
#include <fcntl.h>
#include <io.h>
#include <sys/locking.h>
#else
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
namespace boost{
namespace interprocess{
namespace detail{
namespace file_locking_helpers {
inline void get_pid_creation_time_str(std::string &s)
{
std::stringstream stream;
stream << get_current_process_id() << '_';
stream.precision(6);
stream << std::fixed << get_current_process_creation_time();
s = stream.str();
}
inline void create_tmp_subdir_and_get_pid_based_filepath(const char *subdir_name, const char *file_prefix, OS_process_id_t pid, std::string &s, bool creation_time = false)
{
//Let's create a lock file for each process gmem that will mark if
//the process is alive or not
create_tmp_and_clean_old(s);
s += "/";
s += subdir_name;
if(!open_or_create_directory(s.c_str())){
throw interprocess_exception(error_info(system_error_code()));
}
s += "/";
s += file_prefix;
if(creation_time){
std::string sstamp;
get_pid_creation_time_str(sstamp);
s += sstamp;
}
else{
pid_str_t pid_str;
get_pid_str(pid_str, pid);
s += pid_str;
}
}
inline bool check_if_filename_complies_with_pid
(const char *filename, const char *prefix, OS_process_id_t pid, std::string &file_suffix, bool creation_time = false)
{
//Check if filename complies with lock file name pattern
std::string fname(filename);
std::string fprefix(prefix);
if(fname.size() <= fprefix.size()){
return false;
}
fname.resize(fprefix.size());
if(fname != fprefix){
return false;
}
//If not our lock file, delete it if we can lock it
fname = filename;
fname.erase(0, fprefix.size());
pid_str_t pid_str;
get_pid_str(pid_str, pid);
file_suffix = pid_str;
if(creation_time){
std::size_t p = fname.find('_');
if (p == std::string::npos){
return false;
}
std::string save_suffix(fname);
fname.erase(p);
fname.swap(file_suffix);
bool ret = (file_suffix == fname);
file_suffix.swap(save_suffix);
return ret;
}
else{
fname.swap(file_suffix);
return (file_suffix == fname);
}
}
} //file_locking_helpers
namespace intermodule_singleton_helpers {
const int GMemMarkToBeRemoved = -1;
const int GMemNotPresent = -2;
inline const char *get_lock_file_subdir_name()
{ return "gmem"; }
inline const char *get_lock_file_base_name()
{ return "lck"; }
inline void create_and_get_singleton_lock_file_path(std::string &s)
{
file_locking_helpers::create_tmp_subdir_and_get_pid_based_filepath
(get_lock_file_subdir_name(), get_lock_file_base_name(), get_current_process_id(), s, true);
}
inline const char *get_shm_base_name()
{ return "bip.gmem.shm."; }
inline void get_shm_name(std::string &shm_name)
{
file_locking_helpers::get_pid_creation_time_str(shm_name);
shm_name.insert(0, get_shm_base_name());
}
inline std::size_t get_shm_size()
{ return 65536; }
template<class ManagedShMem>
struct managed_sh_dependant
{
static void apply_gmem_erase_logic(const char *filepath, const char *filename);
static bool remove_old_gmem()
{
std::string refcstrRootDirectory;
tmp_folder(refcstrRootDirectory);
refcstrRootDirectory += "/";
refcstrRootDirectory += get_lock_file_subdir_name();
return for_each_file_in_dir(refcstrRootDirectory.c_str(), apply_gmem_erase_logic);
}
};
#if (defined BOOST_INTERPROCESS_WINDOWS)
template<>
struct managed_sh_dependant<managed_windows_shared_memory>
{
static void apply_gmem_erase_logic(const char *, const char *){}
static bool remove_old_gmem()
{ return true; }
};
struct locking_file_serial_id
{
int fd;
unsigned long dwVolumeSerialNumber;
unsigned long nFileIndexHigh;
unsigned long nFileIndexLow;
//This reference count counts the number of modules attached
//to the shared memory and lock file. This serves to unlink
//the locking file and shared memory when all modules are
//done with the global memory (shared memory)
volatile boost::uint32_t modules_attached_to_gmem_count;
};
inline bool lock_locking_file(int fd)
{
int ret = 0;
while(ret != 0 && errno == EDEADLK){
ret = _locking(fd, _LK_LOCK, 1/*lock_file_contents_length()*/);
}
return 0 == ret;
}
inline bool try_lock_locking_file(int fd)
{
return 0 == _locking(fd, _LK_NBLCK , 1);
}
inline int open_or_create_and_lock_file(const char *name)
{
permissions p;
p.set_unrestricted();
while(1){
file_handle_t handle = create_or_open_file(name, read_write, p);
int fd = _open_osfhandle((intptr_t)handle, _O_TEXT);
if(fd < 0){
close_file(handle);
return fd;
}
if(!try_lock_locking_file(fd)){
_close(fd);
return -1;
}
struct _stat s;
if(0 == _stat(name, &s)){
return fd;
}
else{
_close(fd);
}
}
}
inline int try_open_and_lock_file(const char *name)
{
file_handle_t handle = open_existing_file(name, read_write);
int fd = _open_osfhandle((intptr_t)handle, _O_TEXT);
if(fd < 0){
close_file(handle);
return fd;
}
if(!try_lock_locking_file(fd)){
_close(fd);
return -1;
}
return fd;
}
inline void close_lock_file(int fd)
{ _close(fd); }
inline bool is_valid_fd(int fd)
{
struct _stat s;
return EBADF != _fstat(fd, &s);
}
inline bool is_normal_file(int fd)
{
if(_isatty(fd))
return false;
struct _stat s;
if(0 != _fstat(fd, &s))
return false;
return 0 != (s.st_mode & _S_IFREG);
}
inline std::size_t get_size(int fd)
{
struct _stat s;
if(0 != _fstat(fd, &s))
return 0u;
return (std::size_t)s.st_size;
}
inline bool fill_file_serial_id(int fd, locking_file_serial_id &id)
{
winapi::interprocess_by_handle_file_information info;
if(!winapi::get_file_information_by_handle((void*)_get_osfhandle(fd), &info))
return false;
id.fd = fd;
id.dwVolumeSerialNumber = info.dwVolumeSerialNumber;
id.nFileIndexHigh = info.nFileIndexHigh;
id.nFileIndexLow = info.nFileIndexLow;
id.modules_attached_to_gmem_count = 1; //Initialize attached count
return true;
}
inline bool compare_file_serial(int fd, const locking_file_serial_id &id)
{
winapi::interprocess_by_handle_file_information info;
if(!winapi::get_file_information_by_handle((void*)_get_osfhandle(fd), &info))
return false;
return id.dwVolumeSerialNumber == info.dwVolumeSerialNumber &&
id.nFileIndexHigh == info.nFileIndexHigh &&
id.nFileIndexLow == info.nFileIndexLow;
}
#else //UNIX
struct locking_file_serial_id
{
int fd;
dev_t st_dev;
ino_t st_ino;
//This reference count counts the number of modules attached
//to the shared memory and lock file. This serves to unlink
//the locking file and shared memory when all modules are
//done with the global memory (shared memory)
volatile boost::uint32_t modules_attached_to_gmem_count;
};
inline bool lock_locking_file(int fd)
{
int ret = 0;
while(ret != 0 && errno != EINTR){
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 1;
ret = fcntl (fd, F_SETLKW, &lock);
}
return 0 == ret;
}
inline bool try_lock_locking_file(int fd)
{
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 1;
return 0 == fcntl (fd, F_SETLK, &lock);
}
inline int open_or_create_and_lock_file(const char *name)
{
permissions p;
p.set_unrestricted();
while(1){
int fd = create_or_open_file(name, read_write, p);
if(fd < 0){
return fd;
}
if(!try_lock_locking_file(fd)){
close(fd);
return -1;
}
struct stat s;
if(0 == stat(name, &s)){
return fd;
}
else{
close(fd);
}
}
}
inline int try_open_and_lock_file(const char *name)
{
int fd = open_existing_file(name, read_write);
if(fd < 0){
return fd;
}
if(!try_lock_locking_file(fd)){
close(fd);
return -1;
}
return fd;
}
inline void close_lock_file(int fd)
{ close(fd); }
inline bool is_valid_fd(int fd)
{
struct stat s;
return EBADF != fstat(fd, &s);
}
inline bool is_normal_file(int fd)
{
struct stat s;
if(0 != fstat(fd, &s))
return false;
return 0 != (s.st_mode & S_IFREG);
}
inline std::size_t get_size(int fd)
{
struct stat s;
if(0 != fstat(fd, &s))
return 0u;
return (std::size_t)s.st_size;
}
inline bool fill_file_serial_id(int fd, locking_file_serial_id &id)
{
struct stat s;
if(0 != fstat(fd, &s))
return false;
id.fd = fd;
id.st_dev = s.st_dev;
id.st_ino = s.st_ino;
id.modules_attached_to_gmem_count = 1; //Initialize attached count
return true;
}
inline bool compare_file_serial(int fd, const locking_file_serial_id &id)
{
struct stat info;
if(0 != fstat(fd, &info))
return false;
return id.st_dev == info.st_dev &&
id.st_ino == info.st_ino;
}
#endif
template<class ManagedShMem>
struct gmem_erase_func
{
gmem_erase_func(const char *shm_name, const char *singleton_lock_file_path, ManagedShMem & shm)
:shm_name_(shm_name), singleton_lock_file_path_(singleton_lock_file_path), shm_(shm)
{}
void operator()()
{
locking_file_serial_id *pserial_id = shm_.template find<locking_file_serial_id>("lock_file_fd").first;
if(pserial_id){
pserial_id->fd = GMemMarkToBeRemoved;
}
delete_file(singleton_lock_file_path_);
shared_memory_object::remove(shm_name_);
}
const char * const shm_name_;
const char * const singleton_lock_file_path_;
ManagedShMem & shm_;
};
//This function applies shared memory erasure logic based on the passed lock file.
template<class ManagedShMem>
void managed_sh_dependant<ManagedShMem>::
apply_gmem_erase_logic(const char *filepath, const char *filename)
{
int fd = GMemMarkToBeRemoved;
try{
std::string str;
//If the filename is current process lock file, then avoid it
if(file_locking_helpers::check_if_filename_complies_with_pid
(filename, get_lock_file_base_name(), get_current_process_id(), str, true)){
return;
}
//Open and lock the other process' lock file
fd = try_open_and_lock_file(filepath);
if(fd < 0){
return;
}
//If done, then the process is dead so take global shared memory name
//(the name is based on the lock file name) and try to apply erasure logic
str.insert(0, get_shm_base_name());
try{
ManagedShMem shm(open_only, str.c_str());
gmem_erase_func<ManagedShMem> func(str.c_str(), filepath, shm);
shm.try_atomic_func(func);
}
catch(interprocess_exception &e){
//If shared memory is not found erase the lock file
if(e.get_error_code() == not_found_error){
delete_file(filepath);
}
}
}
catch(...){
}
if(fd >= 0){
close_lock_file(fd);
}
}
} //namespace intermodule_singleton_helpers {
namespace intermodule_singleton_helpers {
//The lock file logic creates uses a unique instance to a file
template <class ManagedShMem>
struct lock_file_logic
{
lock_file_logic(ManagedShMem &shm)
: mshm(shm)
{ shm.atomic_func(*this); }
void operator()(void)
{
retry_with_new_shm = false;
//First find the file locking descriptor id
locking_file_serial_id *pserial_id =
mshm.template find<locking_file_serial_id>("lock_file_fd").first;
int fd;
//If not found schedule a creation
if(!pserial_id){
fd = GMemNotPresent;
}
//Else get it
else{
fd = pserial_id->fd;
}
//If we need to create a new one, do it
if(fd == GMemNotPresent){
std::string lck_str;
//Create a unique current pid based lock file path
create_and_get_singleton_lock_file_path(lck_str);
//Open or create and lock file
int fd = intermodule_singleton_helpers::open_or_create_and_lock_file(lck_str.c_str());
//If failed, write a bad file descriptor to notify other modules that
//something was wrong and unlink shared memory. Mark the function object
//to tell caller to retry with another shared memory
if(fd < 0){
this->register_lock_file(GMemMarkToBeRemoved);
std::string s;
get_shm_name(s);
shared_memory_object::remove(s.c_str());
retry_with_new_shm = true;
}
//If successful, register the file descriptor
else{
this->register_lock_file(fd);
}
}
//If the fd was invalid (maybe a previous try failed) notify caller that
//should retry creation logic, since this shm might have been already
//unlinked since the shm was removed
else if (fd == GMemMarkToBeRemoved){
retry_with_new_shm = true;
}
//If the stored fd is not valid (a open fd, a normal file with the
//expected size, or does not have the same file id number,
//then it's an old shm from an old process with the same pid.
//If that's the case, mark it as invalid
else if(!is_valid_fd(fd) ||
!is_normal_file(fd) ||
0 != get_size(fd) ||
!compare_file_serial(fd, *pserial_id)){
pserial_id->fd = GMemMarkToBeRemoved;
std::string s;
get_shm_name(s);
shared_memory_object::remove(s.c_str());
retry_with_new_shm = true;
}
else{
//If the lock file is ok, increment reference count of
//attached modules to shared memory
atomic_inc32(&pserial_id->modules_attached_to_gmem_count);
}
}
private:
locking_file_serial_id * register_lock_file(int fd)
{
locking_file_serial_id *pinfo = mshm.template construct<locking_file_serial_id>("lock_file_fd")();
fill_file_serial_id(fd, *pinfo);
return pinfo;
}
public:
ManagedShMem &mshm;
bool retry_with_new_shm;
};
#if defined (BOOST_INTERPROCESS_WINDOWS)
template<>
struct lock_file_logic<managed_windows_shared_memory>
{
lock_file_logic(managed_windows_shared_memory &)
: retry_with_new_shm(false)
{}
void operator()(void){}
const bool retry_with_new_shm;
};
#endif
} //namespace intermodule_singleton_helpers {
//This class contains common code for all singleton types, so that we instantiate this
//code just once per module. This class also holds a reference counted shared memory
//to be used by all instances
template<class ManagedShMem>
class intermodule_singleton_common
{
public:
typedef void*(singleton_constructor_t)(ManagedShMem &);
typedef void (singleton_destructor_t)(void *, ManagedShMem &);
static const ::boost::uint32_t Uninitialized = 0u;
static const ::boost::uint32_t Initializing = 1u;
static const ::boost::uint32_t Initialized = 2u;
static const ::boost::uint32_t Broken = 3u;
static void finalize_singleton_logic(void *ptr, singleton_destructor_t destructor)
{
if(ptr)
destructor(ptr, get_shm());
//If this is the last singleton of this module
//apply shm destruction.
//Note: singletons are destroyed when the module is unloaded
//so no threads should be executing or holding references
//to this module
if(1 == atomic_dec32(&this_module_singleton_count)){
destroy_shm();
}
}
static void initialize_singleton_logic
(void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t ini_func);
private:
static ManagedShMem &get_shm()
{
return *static_cast<ManagedShMem *>(static_cast<void *>(&shm_mem));
}
enum { MemSize = ((sizeof(ManagedShMem)-1)/sizeof(max_align))+1u };
static void initialize_shm();
static void destroy_shm();
//Static data, zero-initalized without any dependencies
//this_module_singleton_count is the number of singletons used by this module
static volatile boost::uint32_t this_module_singleton_count;
//this_module_shm_initialized is the state of this module's shm class object
static volatile boost::uint32_t this_module_shm_initialized;
static max_align shm_mem[MemSize];
};
template<class ManagedShMem>
volatile boost::uint32_t intermodule_singleton_common<ManagedShMem>::this_module_singleton_count;
template<class ManagedShMem>
volatile boost::uint32_t intermodule_singleton_common<ManagedShMem>::this_module_shm_initialized;
template<class ManagedShMem>
max_align intermodule_singleton_common<ManagedShMem>::shm_mem[intermodule_singleton_common<ManagedShMem>::MemSize];
template<class ManagedShMem>
void intermodule_singleton_common<ManagedShMem>::initialize_shm()
{
//Obtain unique shm name and size
std::string s;
intermodule_singleton_helpers::get_shm_name(s);
const char *ShmName = s.c_str();
const std::size_t ShmSize = intermodule_singleton_helpers::get_shm_size();;
while(1){
//Try to pass shm state to initializing
::boost::uint32_t tmp = atomic_cas32(&this_module_shm_initialized, Initializing, Uninitialized);
if(tmp >= Initialized){
break;
}
//If some other thread is doing the work wait
else if(tmp == Initializing){
thread_yield();
}
else{ //(tmp == Uninitialized)
//If not initialized try it again?
try{
//Remove old shared memory from the system
intermodule_singleton_helpers::managed_sh_dependant<ManagedShMem>::remove_old_gmem();
//in-place construction of the shared memory class
::new (&get_shm())ManagedShMem(open_or_create, ShmName, ShmSize);
//Use shared memory internal lock to initialize the lock file
//that will mark this gmem as "in use".
intermodule_singleton_helpers::lock_file_logic<ManagedShMem> f(get_shm());
//If function failed (maybe a competing process has erased the shared
//memory between creation and file locking), retry with a new instance.
if(f.retry_with_new_shm){
get_shm().~ManagedShMem();
atomic_write32(&this_module_shm_initialized, Uninitialized);
}
else{
//Locking succeeded, so this shared memory module-instance is ready
atomic_write32(&this_module_shm_initialized, Initialized);
break;
}
}
catch(...){
//
throw;
}
}
}
}
template<class ManagedShMem>
struct unlink_shmlogic
{
unlink_shmlogic(ManagedShMem &mshm)
: mshm_(mshm)
{ mshm.atomic_func(*this); }
void operator()()
{
intermodule_singleton_helpers::locking_file_serial_id *pserial_id =
mshm_.template find<intermodule_singleton_helpers::locking_file_serial_id>
("lock_file_fd").first;
BOOST_ASSERT(0 != pserial_id);
if(1 == atomic_dec32(&pserial_id->modules_attached_to_gmem_count)){
int fd = pserial_id->fd;
if(fd > 0){
pserial_id->fd = intermodule_singleton_helpers::GMemMarkToBeRemoved;
std::string s;
intermodule_singleton_helpers::create_and_get_singleton_lock_file_path(s);
delete_file(s.c_str());
intermodule_singleton_helpers::close_lock_file(fd);
intermodule_singleton_helpers::get_shm_name(s);
shared_memory_object::remove(s.c_str());
}
}
}
ManagedShMem &mshm_;
};
#if defined (BOOST_INTERPROCESS_WINDOWS)
template<>
struct unlink_shmlogic<managed_windows_shared_memory>
{
unlink_shmlogic(managed_windows_shared_memory &)
{}
void operator()(){}
};
#endif
template<class ManagedShMem>
void intermodule_singleton_common<ManagedShMem>::destroy_shm()
{
if(!atomic_read32(&this_module_singleton_count)){
//This module is being unloaded, so destroy
//the shared memory object of this module
//and unlink the shared memory if it's the last
unlink_shmlogic<ManagedShMem> f(get_shm());
(get_shm()).~ManagedShMem();
atomic_write32(&this_module_shm_initialized, Uninitialized);
//Do some cleanup for other processes old gmem instances
intermodule_singleton_helpers::managed_sh_dependant<ManagedShMem>::remove_old_gmem();
}
}
//Initialize this_module_singleton_ptr, creates the shared memory if needed and also creates an unique
//opaque type in shared memory through a singleton_constructor_t function call,
//initializing the passed pointer to that unique instance.
//
//We have two concurrency types here. a)the shared memory/singleton creation must
//be safe between threads of this process but in different modules/dlls. b)
//the pointer to the singleton is per-module, so we have to protect this
//initization between threads of the same module.
//
//All static variables declared here are shared between inside a module
//so atomic operations will synchronize only threads of the same module.
template<class ManagedShMem>
void intermodule_singleton_common<ManagedShMem>::initialize_singleton_logic
(void *&ptr, volatile boost::uint32_t &this_module_singleton_initialized, singleton_constructor_t constructor)
{
//If current module is not initialized enter to lock free logic
if(atomic_read32(&this_module_singleton_initialized) != Initialized){
//Now a single thread of the module will succeed in this CAS.
//trying to pass from Uninitialized to Initializing
::boost::uint32_t previous_module_singleton_initialized = atomic_cas32
(&this_module_singleton_initialized, Initializing, Uninitialized);
//If the thread succeeded the CAS (winner) it will compete with other
//winner threads from other modules to create the shared memory
if(previous_module_singleton_initialized == Uninitialized){
try{
//Now initialize shm, this function solves concurrency issues
//between threads of several modules
initialize_shm();
//Increment the module reference count that reflects how many
//singletons this module holds, so that we can safely destroy
//module shared memory object when no singleton is left
atomic_inc32(&this_module_singleton_count);
//Now try to create the singleton in shared memory.
//This function solves concurrency issues
//between threads of several modules
void *tmp = constructor(get_shm());
//Insert a barrier before assigning the pointer to
//make sure this assignment comes after the initialization
atomic_write32(&this_module_singleton_initialized, Initializing);
//Assign the singleton address to the module-local pointer
ptr = tmp;
//Memory barrier inserted, all previous operations should complete
//before this one. Now marked as initialized
atomic_inc32(&this_module_singleton_initialized);
}
catch(...){
//Mark singleton failed to initialize
atomic_write32(&this_module_singleton_initialized, Broken);
throw;
}
}
//If previous state was initializing, this means that another winner thread is
//trying to initialize the singleton. Just wait until completes its work.
else if(previous_module_singleton_initialized == Initializing){
while(1){
previous_module_singleton_initialized = atomic_read32(&this_module_singleton_initialized);
if(previous_module_singleton_initialized >= Initialized){
//Already initialized, or exception thrown by initializer thread
break;
}
else if(previous_module_singleton_initialized == Initializing){
detail::thread_yield();
}
else{
//This can't be happening!
BOOST_ASSERT(0);
}
}
}
else if(previous_module_singleton_initialized == Initialized){
//Nothing to do here, the singleton is ready
}
//If previous state was greater than initialized, then memory is broken
//trying to initialize the singleton.
else{//(previous_module_singleton_initialized > Initialized)
throw interprocess_exception("boost::interprocess::intermodule_singleton initialization failed");
}
}
BOOST_ASSERT(ptr != 0);
}
//Now this class is a singleton, initializing the singleton in
//the first get() function call if LazyInit is false. If true
//then the singleton will be initialized when loading the module.
template<typename C, bool LazyInit, class ManagedShMem>
class intermodule_singleton_impl
{
public:
static C& get() //Let's make inlining easy
{
if(!this_module_singleton_ptr){
if(lifetime.dummy_function()) //This forces lifetime instantiation, for reference counted destruction
intermodule_singleton_common<ManagedShMem>::initialize_singleton_logic
(this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor);
}
return *static_cast<C*>(this_module_singleton_ptr);
}
private:
struct ref_count_ptr
{
ref_count_ptr(C *p, boost::uint32_t count)
: ptr(p), singleton_ref_count(count)
{}
C *ptr;
//This reference count serves to count the number of attached
//modules to this singleton
volatile boost::uint32_t singleton_ref_count;
};
//These statics will be zero-initialized without any constructor call dependency
//this_module_singleton_ptr will be a module-local pointer to the singleton
static void* this_module_singleton_ptr;
//this_module_singleton_count will be used to synchronize threads of the same module
//for access to a singleton instance, and to flag the state of the
//singleton.
static volatile boost::uint32_t this_module_singleton_initialized;
//This class destructor will trigger singleton destruction
struct lifetime_type_lazy
{
bool dummy_function()
{ return m_dummy == 0; }
~lifetime_type_lazy()
{
intermodule_singleton_common<ManagedShMem>::finalize_singleton_logic
(this_module_singleton_ptr, singleton_destructor);
}
//Dummy volatile so that the compiler can't resolve its value at compile-time
//and can't avoid lifetime_type instantiation if dummy_function() is called.
static volatile int m_dummy;
};
struct lifetime_type_static
: public lifetime_type_lazy
{
lifetime_type_static()
{
intermodule_singleton_common<ManagedShMem>::initialize_singleton_logic
(this_module_singleton_ptr, this_module_singleton_initialized, singleton_constructor);
}
};
typedef typename if_c
<LazyInit, lifetime_type_lazy, lifetime_type_static>::type lifetime_type;
static lifetime_type lifetime;
//A functor to be executed inside shared memory lock that just
//searches for the singleton in shm and if not present creates a new one.
//If singleton constructor throws, the exception is propagated
struct init_atomic_func
{
init_atomic_func(ManagedShMem &m)
: mshm(m)
{}
void operator()()
{
ref_count_ptr *rcount = mshm.template find<ref_count_ptr>(unique_instance).first;
if(!rcount){
C *p = new C();
try{
rcount = mshm.template construct<ref_count_ptr>(unique_instance)(p, 0u);
}
catch(...){
delete p;
throw;
}
}
atomic_inc32(&rcount->singleton_ref_count);
ret_ptr = rcount->ptr;
}
ManagedShMem &mshm;
void *ret_ptr;
};
//A functor to be executed inside shared memory lock that just
//deletes the singleton in shm if the attached count reaches to zero
struct fini_atomic_func
{
fini_atomic_func(ManagedShMem &m)
: mshm(m)
{}
void operator()()
{
ref_count_ptr *rcount = mshm.template find<ref_count_ptr>(unique_instance).first;
//The object must exist
BOOST_ASSERT(rcount);
//Check if last reference
if(atomic_dec32(&rcount->singleton_ref_count) == 1){
//If last, destroy the object
BOOST_ASSERT(rcount->ptr != 0);
delete rcount->ptr;
//Now destroy shm entry
bool destroyed = mshm.template destroy<ref_count_ptr>(unique_instance);
(void)destroyed; BOOST_ASSERT(destroyed == true);
}
}
ManagedShMem &mshm;
void *ret_ptr;
};
//A wrapper to execute init_atomic_func
static void *singleton_constructor(ManagedShMem &mshm)
{
init_atomic_func f(mshm);
mshm.atomic_func(f);
return f.ret_ptr;
}
//A wrapper to execute fini_atomic_func
static void singleton_destructor(void *p, ManagedShMem &mshm)
{ (void)p;
fini_atomic_func f(mshm);
mshm.atomic_func(f);
}
};
template <typename C, bool L, class ManagedShMem>
volatile int intermodule_singleton_impl<C, L, ManagedShMem>::lifetime_type_lazy::m_dummy;
//These will be zero-initialized by the loader
template <typename C, bool L, class ManagedShMem>
void *intermodule_singleton_impl<C, L, ManagedShMem>::this_module_singleton_ptr;
template <typename C, bool L, class ManagedShMem>
volatile boost::uint32_t intermodule_singleton_impl<C, L, ManagedShMem>::this_module_singleton_initialized;
template <typename C, bool L, class ManagedShMem>
typename intermodule_singleton_impl<C, L, ManagedShMem>::lifetime_type
intermodule_singleton_impl<C, L, ManagedShMem>::lifetime;
template<typename C, bool LazyInit = false>
class portable_intermodule_singleton
: public intermodule_singleton_impl<C, LazyInit, managed_shared_memory>
{};
#ifdef BOOST_INTERPROCESS_WINDOWS
template<typename C, bool LazyInit = false>
class windows_intermodule_singleton
: public intermodule_singleton_impl<C, LazyInit, managed_windows_shared_memory>
{};
#endif
//Now this class is a singleton, initializing the singleton in
//the first get() function call if LazyInit is false. If true
//then the singleton will be initialized when loading the module.
template<typename C, bool LazyInit = false>
class intermodule_singleton
#ifdef BOOST_INTERPROCESS_WINDOWS
: public windows_intermodule_singleton<C, LazyInit>
// : public portable_intermodule_singleton<C, LazyInit>
#else
: public portable_intermodule_singleton<C, LazyInit>
#endif
{};
} //namespace detail{
} //namespace interprocess{
} //namespace boost{
#include <boost/interprocess/detail/config_end.hpp>
#endif