You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
1035 lines
32 KiB
1035 lines
32 KiB
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// (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/type_traits/type_with_alignment.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 ipcdetail{ |
|
|
|
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(::boost::detail::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 ::boost::detail::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> |
|
::boost::detail::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){ |
|
ipcdetail::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 ipcdetail{ |
|
} //namespace interprocess{ |
|
} //namespace boost{ |
|
|
|
#include <boost/interprocess/detail/config_end.hpp> |
|
|
|
#endif
|
|
|