Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: // Chris@16: // (C) Copyright Ion Gaztanaga 2009-2012. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // See http://www.boost.org/libs/interprocess for documentation. Chris@16: // Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: #ifndef BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_HPP Chris@16: #define BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_HPP Chris@16: Chris@101: #ifndef BOOST_CONFIG_HPP Chris@101: # include Chris@101: #endif Chris@101: # Chris@101: #if defined(BOOST_HAS_PRAGMA_ONCE) Chris@16: #pragma once Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost{ Chris@16: namespace interprocess{ Chris@16: namespace ipcdetail{ Chris@16: Chris@16: typedef basic_managed_global_memory managed_global_memory; Chris@16: Chris@16: namespace intermodule_singleton_helpers { Chris@16: Chris@16: static void create_tmp_subdir_and_get_pid_based_filepath Chris@16: (const char *subdir_name, const char *file_prefix, OS_process_id_t pid, std::string &s, bool creation_time = false) Chris@16: { Chris@16: //Let's create a lock file for each process gmem that will mark if Chris@16: //the process is alive or not Chris@101: create_shared_dir_and_clean_old(s); Chris@16: s += "/"; Chris@16: s += subdir_name; Chris@16: if(!open_or_create_directory(s.c_str())){ Chris@101: error_info err = system_error_code(); Chris@101: throw interprocess_exception(err); Chris@16: } Chris@16: s += "/"; Chris@16: s += file_prefix; Chris@16: if(creation_time){ Chris@16: std::string sstamp; Chris@16: get_pid_creation_time_str(sstamp); Chris@16: s += sstamp; Chris@16: } Chris@16: else{ Chris@16: pid_str_t pid_str; Chris@16: get_pid_str(pid_str, pid); Chris@16: s += pid_str; Chris@16: } Chris@16: } Chris@16: Chris@16: static bool check_if_filename_complies_with_pid Chris@16: (const char *filename, const char *prefix, OS_process_id_t pid, std::string &file_suffix, bool creation_time = false) Chris@16: { Chris@16: //Check if filename complies with lock file name pattern Chris@16: std::string fname(filename); Chris@16: std::string fprefix(prefix); Chris@16: if(fname.size() <= fprefix.size()){ Chris@16: return false; Chris@16: } Chris@16: fname.resize(fprefix.size()); Chris@16: if(fname != fprefix){ Chris@16: return false; Chris@16: } Chris@16: Chris@16: //If not our lock file, delete it if we can lock it Chris@16: fname = filename; Chris@16: fname.erase(0, fprefix.size()); Chris@16: pid_str_t pid_str; Chris@16: get_pid_str(pid_str, pid); Chris@16: file_suffix = pid_str; Chris@16: if(creation_time){ Chris@16: std::size_t p = fname.find('_'); Chris@16: if (p == std::string::npos){ Chris@16: return false; Chris@16: } Chris@16: std::string save_suffix(fname); Chris@16: fname.erase(p); Chris@16: fname.swap(file_suffix); Chris@16: bool ret = (file_suffix == fname); Chris@16: file_suffix.swap(save_suffix); Chris@16: return ret; Chris@16: } Chris@16: else{ Chris@16: fname.swap(file_suffix); Chris@16: return (file_suffix == fname); Chris@16: } Chris@16: } Chris@16: Chris@16: template<> Chris@16: struct thread_safe_global_map_dependant Chris@16: { Chris@16: private: Chris@16: static const int GMemMarkToBeRemoved = -1; Chris@16: static const int GMemNotPresent = -2; Chris@16: Chris@16: static const char *get_lock_file_subdir_name() Chris@16: { return "gmem"; } Chris@16: Chris@16: static const char *get_lock_file_base_name() Chris@16: { return "lck"; } Chris@16: Chris@16: static void create_and_get_singleton_lock_file_path(std::string &s) Chris@16: { Chris@16: create_tmp_subdir_and_get_pid_based_filepath Chris@16: (get_lock_file_subdir_name(), get_lock_file_base_name(), get_current_process_id(), s, true); Chris@16: } Chris@16: Chris@16: struct gmem_erase_func Chris@16: { Chris@16: gmem_erase_func(const char *shm_name, const char *singleton_lock_file_path, managed_global_memory & shm) Chris@16: :shm_name_(shm_name), singleton_lock_file_path_(singleton_lock_file_path), shm_(shm) Chris@16: {} Chris@16: Chris@16: void operator()() Chris@16: { Chris@16: locking_file_serial_id *pserial_id = shm_.find("lock_file_fd").first; Chris@16: if(pserial_id){ Chris@16: pserial_id->fd = GMemMarkToBeRemoved; Chris@16: } Chris@16: delete_file(singleton_lock_file_path_); Chris@16: shared_memory_object::remove(shm_name_); Chris@16: } Chris@16: Chris@16: const char * const shm_name_; Chris@16: const char * const singleton_lock_file_path_; Chris@16: managed_global_memory & shm_; Chris@16: }; Chris@16: Chris@16: //This function applies shared memory erasure logic based on the passed lock file. Chris@16: static void apply_gmem_erase_logic(const char *filepath, const char *filename) Chris@16: { Chris@16: int fd = GMemMarkToBeRemoved; Chris@16: try{ Chris@16: std::string str; Chris@16: //If the filename is current process lock file, then avoid it Chris@16: if(check_if_filename_complies_with_pid Chris@16: (filename, get_lock_file_base_name(), get_current_process_id(), str, true)){ Chris@16: return; Chris@16: } Chris@16: //Open and lock the other process' lock file Chris@16: fd = try_open_and_lock_file(filepath); Chris@16: if(fd < 0){ Chris@16: return; Chris@16: } Chris@16: //If done, then the process is dead so take global shared memory name Chris@16: //(the name is based on the lock file name) and try to apply erasure logic Chris@16: str.insert(0, get_map_base_name()); Chris@16: try{ Chris@16: managed_global_memory shm(open_only, str.c_str()); Chris@16: gmem_erase_func func(str.c_str(), filepath, shm); Chris@16: shm.try_atomic_func(func); Chris@16: } Chris@16: catch(interprocess_exception &e){ Chris@16: //If shared memory is not found erase the lock file Chris@16: if(e.get_error_code() == not_found_error){ Chris@16: delete_file(filepath); Chris@16: } Chris@16: } Chris@16: } Chris@16: catch(...){ Chris@16: Chris@16: } Chris@16: if(fd >= 0){ Chris@16: close_lock_file(fd); Chris@16: } Chris@16: } Chris@16: Chris@16: public: Chris@16: Chris@16: static bool remove_old_gmem() Chris@16: { Chris@16: std::string refcstrRootDirectory; Chris@101: get_shared_dir(refcstrRootDirectory); Chris@16: refcstrRootDirectory += "/"; Chris@16: refcstrRootDirectory += get_lock_file_subdir_name(); Chris@16: return for_each_file_in_dir(refcstrRootDirectory.c_str(), apply_gmem_erase_logic); Chris@16: } Chris@16: Chris@16: struct lock_file_logic Chris@16: { Chris@16: lock_file_logic(managed_global_memory &shm) Chris@16: : mshm(shm) Chris@16: { shm.atomic_func(*this); } Chris@16: Chris@16: void operator()(void) Chris@16: { Chris@16: retry_with_new_map = false; Chris@16: Chris@16: //First find the file locking descriptor id Chris@16: locking_file_serial_id *pserial_id = Chris@16: mshm.find("lock_file_fd").first; Chris@16: Chris@16: int fd; Chris@16: //If not found schedule a creation Chris@16: if(!pserial_id){ Chris@16: fd = GMemNotPresent; Chris@16: } Chris@16: //Else get it Chris@16: else{ Chris@16: fd = pserial_id->fd; Chris@16: } Chris@16: //If we need to create a new one, do it Chris@16: if(fd == GMemNotPresent){ Chris@16: std::string lck_str; Chris@16: //Create a unique current pid based lock file path Chris@16: create_and_get_singleton_lock_file_path(lck_str); Chris@16: //Open or create and lock file Chris@16: int fd_lockfile = open_or_create_and_lock_file(lck_str.c_str()); Chris@16: //If failed, write a bad file descriptor to notify other modules that Chris@16: //something was wrong and unlink shared memory. Mark the function object Chris@16: //to tell caller to retry with another shared memory Chris@16: if(fd_lockfile < 0){ Chris@16: this->register_lock_file(GMemMarkToBeRemoved); Chris@16: std::string s; Chris@16: get_map_name(s); Chris@16: shared_memory_object::remove(s.c_str()); Chris@16: retry_with_new_map = true; Chris@16: } Chris@16: //If successful, register the file descriptor Chris@16: else{ Chris@16: this->register_lock_file(fd_lockfile); Chris@16: } Chris@16: } Chris@16: //If the fd was invalid (maybe a previous try failed) notify caller that Chris@16: //should retry creation logic, since this shm might have been already Chris@16: //unlinked since the shm was removed Chris@16: else if (fd == GMemMarkToBeRemoved){ Chris@16: retry_with_new_map = true; Chris@16: } Chris@16: //If the stored fd is not valid (a open fd, a normal file with the Chris@16: //expected size, or does not have the same file id number, Chris@16: //then it's an old shm from an old process with the same pid. Chris@16: //If that's the case, mark it as invalid Chris@16: else if(!is_valid_fd(fd) || Chris@16: !is_normal_file(fd) || Chris@16: 0 != get_size(fd) || Chris@16: !compare_file_serial(fd, *pserial_id)){ Chris@16: pserial_id->fd = GMemMarkToBeRemoved; Chris@16: std::string s; Chris@16: get_map_name(s); Chris@16: shared_memory_object::remove(s.c_str()); Chris@16: retry_with_new_map = true; Chris@16: } Chris@16: else{ Chris@16: //If the lock file is ok, increment reference count of Chris@16: //attached modules to shared memory Chris@16: atomic_inc32(&pserial_id->modules_attached_to_gmem_count); Chris@16: } Chris@16: } Chris@16: Chris@16: bool retry() const { return retry_with_new_map; } Chris@16: Chris@16: private: Chris@16: locking_file_serial_id * register_lock_file(int fd) Chris@16: { Chris@16: locking_file_serial_id *pinfo = mshm.construct("lock_file_fd")(); Chris@16: fill_file_serial_id(fd, *pinfo); Chris@16: return pinfo; Chris@16: } Chris@16: Chris@16: managed_global_memory &mshm; Chris@16: bool retry_with_new_map; Chris@16: }; Chris@16: Chris@16: static void construct_map(void *addr) Chris@16: { Chris@16: std::string s; Chris@16: intermodule_singleton_helpers::get_map_name(s); Chris@16: const char *MapName = s.c_str(); Chris@16: const std::size_t MapSize = intermodule_singleton_helpers::get_map_size();; Chris@16: ::new (addr)managed_global_memory(open_or_create, MapName, MapSize); Chris@16: } Chris@16: Chris@16: struct unlink_map_logic Chris@16: { Chris@16: unlink_map_logic(managed_global_memory &mshm) Chris@16: : mshm_(mshm) Chris@16: { mshm.atomic_func(*this); } Chris@16: Chris@16: void operator()() Chris@16: { Chris@16: locking_file_serial_id *pserial_id = Chris@16: mshm_.find Chris@16: ("lock_file_fd").first; Chris@16: BOOST_ASSERT(0 != pserial_id); Chris@16: if(1 == atomic_dec32(&pserial_id->modules_attached_to_gmem_count)){ Chris@16: int fd = pserial_id->fd; Chris@16: if(fd > 0){ Chris@16: pserial_id->fd = GMemMarkToBeRemoved; Chris@16: std::string s; Chris@16: create_and_get_singleton_lock_file_path(s); Chris@16: delete_file(s.c_str()); Chris@16: close_lock_file(fd); Chris@16: intermodule_singleton_helpers::get_map_name(s); Chris@16: shared_memory_object::remove(s.c_str()); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@16: managed_global_memory &mshm_; Chris@16: }; Chris@16: Chris@16: static ref_count_ptr *find(managed_global_memory &map, const char *name) Chris@16: { Chris@16: return map.find(name).first; Chris@16: } Chris@16: Chris@16: static ref_count_ptr *insert(managed_global_memory &map, const char *name, const ref_count_ptr &ref) Chris@16: { Chris@16: return map.construct(name)(ref); Chris@16: } Chris@16: Chris@16: static bool erase(managed_global_memory &map, const char *name) Chris@16: { Chris@16: return map.destroy(name); Chris@16: } Chris@16: Chris@16: template Chris@16: static void atomic_func(managed_global_memory &map, F &f) Chris@16: { Chris@16: map.atomic_func(f); Chris@16: } Chris@16: }; Chris@16: Chris@16: } //namespace intermodule_singleton_helpers { Chris@16: Chris@101: template Chris@16: class portable_intermodule_singleton Chris@16: : public intermodule_singleton_impl Chris@16: {}; Chris@16: Chris@16: } //namespace ipcdetail{ Chris@16: } //namespace interprocess{ Chris@16: } //namespace boost{ Chris@16: Chris@16: #include Chris@16: Chris@16: #endif //#ifndef BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_HPP