summaryrefslogtreecommitdiff
path: root/boost/interprocess/detail/portable_intermodule_singleton.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/interprocess/detail/portable_intermodule_singleton.hpp')
-rw-r--r--boost/interprocess/detail/portable_intermodule_singleton.hpp356
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