summaryrefslogtreecommitdiff
path: root/boost/interprocess/detail/intermodule_singleton.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/interprocess/detail/intermodule_singleton.hpp')
-rw-r--r--boost/interprocess/detail/intermodule_singleton.hpp1184
1 files changed, 1184 insertions, 0 deletions
diff --git a/boost/interprocess/detail/intermodule_singleton.hpp b/boost/interprocess/detail/intermodule_singleton.hpp
new file mode 100644
index 0000000000..4bffbe9d4e
--- /dev/null
+++ b/boost/interprocess/detail/intermodule_singleton.hpp
@@ -0,0 +1,1184 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2009-2011. 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>
+
+#if defined(BOOST_INTERPROCESS_WINDOWS)
+#include <boost/interprocess/windows_shared_memory.hpp>
+#endif
+
+#include <boost/interprocess/shared_memory_object.hpp>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/interprocess/sync/spin/mutex.hpp>
+#include <boost/interprocess/sync/spin/recursive_mutex.hpp>
+#include <boost/interprocess/detail/managed_memory_impl.hpp>
+#include <boost/interprocess/detail/managed_open_or_create_impl.hpp>
+#include <boost/interprocess/mem_algo/rbtree_best_fit.hpp>
+#include <boost/interprocess/indexes/iset_index.hpp>
+#include <boost/interprocess/creation_tags.hpp>
+#include <boost/interprocess/permissions.hpp>
+
+
+#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{
+
+struct intermodule_singleton_mutex_family
+{
+ typedef boost::interprocess::ipcdetail::spin_mutex mutex_type;
+ typedef boost::interprocess::ipcdetail::spin_recursive_mutex recursive_mutex_type;
+};
+
+struct intermodule_types
+{
+ //We must use offset_ptr since a loaded DLL can map the singleton holder shared memory
+ //at a different address than other DLLs/main executables
+ typedef rbtree_best_fit<intermodule_singleton_mutex_family, offset_ptr<void> > mem_algo;
+ template<class Device, bool FileBased>
+ struct open_or_create
+ {
+ typedef managed_open_or_create_impl
+ <Device, mem_algo::Alignment, FileBased> type;
+ };
+};
+
+template<class Device, bool FileBased>
+class basic_managed_global_memory
+ : public basic_managed_memory_impl
+ < char
+ , intermodule_types::mem_algo
+ , iset_index
+ , intermodule_types::open_or_create<Device, FileBased>::type::ManagedOpenOrCreateUserOffset
+ >
+ , private intermodule_types::open_or_create<Device, FileBased>::type
+{
+ /// @cond
+ typedef typename intermodule_types::template open_or_create<Device, FileBased>::type base2_t;
+
+ typedef basic_managed_memory_impl
+ < char
+ , intermodule_types::mem_algo
+ , iset_index
+ , base2_t::ManagedOpenOrCreateUserOffset
+ > base_t;
+
+ typedef create_open_func<base_t> create_open_func_t;
+
+ basic_managed_global_memory *get_this_pointer()
+ { return this; }
+
+ public:
+ typedef typename base_t::size_type size_type;
+
+ private:
+ typedef typename base_t::char_ptr_holder_t char_ptr_holder_t;
+ BOOST_MOVABLE_BUT_NOT_COPYABLE(basic_managed_global_memory)
+ /// @endcond
+
+ public: //functions
+/*
+ basic_managed_global_memory()
+ {}
+
+ basic_managed_global_memory(create_only_t create_only, const char *name,
+ size_type size, const void *addr = 0, const permissions& perm = permissions())
+ : base_t()
+ , base2_t(create_only, name, size, read_write, addr,
+ create_open_func_t(get_this_pointer(), DoCreate), perm)
+ {}
+*/
+ basic_managed_global_memory (open_or_create_t open_or_create,
+ const char *name, size_type size,
+ const void *addr = 0, const permissions& perm = permissions())
+ : base_t()
+ , base2_t(open_or_create, name, size, read_write, addr,
+ create_open_func_t(get_this_pointer(),
+ DoOpenOrCreate), perm)
+ {}
+
+ basic_managed_global_memory (open_only_t open_only, const char* name,
+ const void *addr = 0)
+ : base_t()
+ , base2_t(open_only, name, read_write, addr,
+ create_open_func_t(get_this_pointer(),
+ DoOpen))
+ {}
+
+/*
+ basic_managed_global_memory (open_copy_on_write_t, const char* name,
+ const void *addr = 0)
+ : base_t()
+ , base2_t(open_only, name, copy_on_write, addr,
+ create_open_func_t(get_this_pointer(),
+ DoOpen))
+ {}
+
+ //!Connects to a created shared memory and its segment manager.
+ //!in read-only mode.
+ //!This can throw.
+ basic_managed_global_memory (open_read_only_t, const char* name,
+ const void *addr = 0)
+ : base_t()
+ , base2_t(open_only, name, read_only, addr,
+ create_open_func_t(get_this_pointer(),
+ DoOpen))
+ {}
+
+ //!Moves the ownership of "moved"'s managed memory to *this.
+ //!Does not throw
+ basic_managed_global_memory(BOOST_RV_REF(basic_managed_global_memory) moved)
+ {
+ basic_managed_global_memory tmp;
+ this->swap(moved);
+ tmp.swap(moved);
+ }
+
+ //!Moves the ownership of "moved"'s managed memory to *this.
+ //!Does not throw
+ basic_managed_global_memory &operator=(BOOST_RV_REF(basic_managed_global_memory) moved)
+ {
+ basic_managed_global_memory tmp(boost::move(moved));
+ this->swap(tmp);
+ return *this;
+ }*/
+};
+
+#if defined(BOOST_INTERPROCESS_WINDOWS)
+typedef basic_managed_global_memory<windows_shared_memory, false> windows_managed_global_memory;
+#endif
+
+typedef basic_managed_global_memory<shared_memory_object, true> managed_global_memory;
+
+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<windows_managed_global_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<windows_managed_global_memory>
+{
+ lock_file_logic(windows_managed_global_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 *>(&mem_holder.shm_mem));
+ }
+
+ 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 struct mem_holder_t
+ {
+ ::boost::detail::max_align aligner;
+ char shm_mem [sizeof(ManagedShMem)];
+ } mem_holder;
+};
+
+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>
+typename intermodule_singleton_common<ManagedShMem>::mem_holder_t
+ intermodule_singleton_common<ManagedShMem>::mem_holder;
+
+template<class ManagedShMem>
+void intermodule_singleton_common<ManagedShMem>::initialize_shm()
+{
+ //Obtain unique shm name and size
+ std::string s;
+ 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();
+ //
+ if(s.empty()){
+ intermodule_singleton_helpers::get_shm_name(s);
+ }
+ const char *ShmName = s.c_str();
+ const std::size_t ShmSize = intermodule_singleton_helpers::get_shm_size();;
+
+ //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<windows_managed_global_memory>
+{
+ unlink_shmlogic(windows_managed_global_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){
+ 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 = 0;
+
+//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 = 0;
+
+template <typename C, bool L, class ManagedShMem>
+volatile boost::uint32_t intermodule_singleton_impl<C, L, ManagedShMem>::this_module_singleton_initialized = 0;
+
+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_global_memory>
+{};
+
+#if defined(BOOST_INTERPROCESS_WINDOWS)
+
+template<typename C, bool LazyInit = false>
+class windows_intermodule_singleton
+ : public intermodule_singleton_impl
+ < C
+ , LazyInit
+ , windows_managed_global_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>
+ #else
+ : public portable_intermodule_singleton<C, LazyInit>
+ #endif
+{};
+
+
+} //namespace ipcdetail{
+} //namespace interprocess{
+} //namespace boost{
+
+#include <boost/interprocess/detail/config_end.hpp>
+
+#endif