diff options
Diffstat (limited to 'boost/interprocess/detail/intermodule_singleton.hpp')
-rw-r--r-- | boost/interprocess/detail/intermodule_singleton.hpp | 1148 |
1 files changed, 6 insertions, 1142 deletions
diff --git a/boost/interprocess/detail/intermodule_singleton.hpp b/boost/interprocess/detail/intermodule_singleton.hpp index 4bffbe9d4e..054322699d 100644 --- a/boost/interprocess/detail/intermodule_singleton.hpp +++ b/boost/interprocess/detail/intermodule_singleton.hpp @@ -18,1163 +18,27 @@ #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> +#ifdef BOOST_INTERPROCESS_WINDOWS + #include <boost/interprocess/detail/windows_intermodule_singleton.hpp> #endif +#include <boost/interprocess/detail/portable_intermodule_singleton.hpp> 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> +template<typename C, bool LazyInit = true, bool Phoenix = true> class intermodule_singleton #ifdef BOOST_INTERPROCESS_WINDOWS - : public windows_intermodule_singleton<C, LazyInit> + : public windows_intermodule_singleton<C, LazyInit, Phoenix> #else - : public portable_intermodule_singleton<C, LazyInit> + : public portable_intermodule_singleton<C, LazyInit, Phoenix> #endif {}; - } //namespace ipcdetail{ } //namespace interprocess{ } //namespace boost{ |