diff options
Diffstat (limited to 'boost/interprocess/detail/portable_intermodule_singleton.hpp')
-rw-r--r-- | boost/interprocess/detail/portable_intermodule_singleton.hpp | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/boost/interprocess/detail/portable_intermodule_singleton.hpp b/boost/interprocess/detail/portable_intermodule_singleton.hpp new file mode 100644 index 0000000000..eb2a13e104 --- /dev/null +++ b/boost/interprocess/detail/portable_intermodule_singleton.hpp @@ -0,0 +1,356 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (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_PORTABLE_INTERMODULE_SINGLETON_HPP +#define BOOST_INTERPROCESS_PORTABLE_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/detail/managed_global_memory.hpp> +#include <boost/interprocess/detail/intermodule_singleton_common.hpp> +#include <boost/interprocess/shared_memory_object.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/file_locking_helpers.hpp> +#include <boost/assert.hpp> +#include <cstddef> +#include <cstdio> +#include <cstring> +#include <string> + +namespace boost{ +namespace interprocess{ +namespace ipcdetail{ + +typedef basic_managed_global_memory<shared_memory_object, true> managed_global_memory; + +namespace intermodule_singleton_helpers { + +static 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; + } +} + +static 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); + } +} + +template<> +struct thread_safe_global_map_dependant<managed_global_memory> +{ + private: + static const int GMemMarkToBeRemoved = -1; + static const int GMemNotPresent = -2; + + static const char *get_lock_file_subdir_name() + { return "gmem"; } + + static const char *get_lock_file_base_name() + { return "lck"; } + + static void create_and_get_singleton_lock_file_path(std::string &s) + { + create_tmp_subdir_and_get_pid_based_filepath + (get_lock_file_subdir_name(), get_lock_file_base_name(), get_current_process_id(), s, true); + } + + struct gmem_erase_func + { + gmem_erase_func(const char *shm_name, const char *singleton_lock_file_path, managed_global_memory & shm) + :shm_name_(shm_name), singleton_lock_file_path_(singleton_lock_file_path), shm_(shm) + {} + + void operator()() + { + locking_file_serial_id *pserial_id = shm_.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_; + managed_global_memory & shm_; + }; + + //This function applies shared memory erasure logic based on the passed lock file. + static void 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(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_map_base_name()); + try{ + managed_global_memory shm(open_only, str.c_str()); + gmem_erase_func 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); + } + } + + public: + + 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); + } + + struct lock_file_logic + { + lock_file_logic(managed_global_memory &shm) + : mshm(shm) + { shm.atomic_func(*this); } + + void operator()(void) + { + retry_with_new_map = false; + + //First find the file locking descriptor id + locking_file_serial_id *pserial_id = + mshm.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 = 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_map_name(s); + shared_memory_object::remove(s.c_str()); + retry_with_new_map = 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_map = 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_map_name(s); + shared_memory_object::remove(s.c_str()); + retry_with_new_map = 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); + } + } + + bool retry() const { return retry_with_new_map; } + + private: + locking_file_serial_id * register_lock_file(int fd) + { + locking_file_serial_id *pinfo = mshm.construct<locking_file_serial_id>("lock_file_fd")(); + fill_file_serial_id(fd, *pinfo); + return pinfo; + } + + managed_global_memory &mshm; + bool retry_with_new_map; + }; + + static void construct_map(void *addr) + { + std::string s; + intermodule_singleton_helpers::get_map_name(s); + const char *MapName = s.c_str(); + const std::size_t MapSize = intermodule_singleton_helpers::get_map_size();; + ::new (addr)managed_global_memory(open_or_create, MapName, MapSize); + } + + struct unlink_map_logic + { + unlink_map_logic(managed_global_memory &mshm) + : mshm_(mshm) + { mshm.atomic_func(*this); } + + void operator()() + { + locking_file_serial_id *pserial_id = + mshm_.find<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 = GMemMarkToBeRemoved; + std::string s; + create_and_get_singleton_lock_file_path(s); + delete_file(s.c_str()); + close_lock_file(fd); + intermodule_singleton_helpers::get_map_name(s); + shared_memory_object::remove(s.c_str()); + } + } + } + + private: + managed_global_memory &mshm_; + }; + + static ref_count_ptr *find(managed_global_memory &map, const char *name) + { + return map.find<ref_count_ptr>(name).first; + } + + static ref_count_ptr *insert(managed_global_memory &map, const char *name, const ref_count_ptr &ref) + { + return map.construct<ref_count_ptr>(name)(ref); + } + + static bool erase(managed_global_memory &map, const char *name) + { + return map.destroy<ref_count_ptr>(name); + } + + template<class F> + static void atomic_func(managed_global_memory &map, F &f) + { + map.atomic_func(f); + } +}; + +} //namespace intermodule_singleton_helpers { + +template<typename C, bool LazyInit = true, bool Phoenix = true> +class portable_intermodule_singleton + : public intermodule_singleton_impl<C, LazyInit, Phoenix, managed_global_memory> +{}; + +} //namespace ipcdetail{ +} //namespace interprocess{ +} //namespace boost{ + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_HPP |