123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- //////////////////////////////////////////////////////////////////////////////
- //
- // (C) Copyright Ion Gaztanaga 2009-2012. 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
- #ifndef BOOST_CONFIG_HPP
- # include <boost/config.hpp>
- #endif
- #
- #if defined(BOOST_HAS_PRAGMA_ONCE)
- #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/shared_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_shared_dir_and_clean_old(s);
- s += "/";
- s += subdir_name;
- if(!open_or_create_shared_directory(s.c_str())){
- error_info err = system_error_code();
- throw interprocess_exception(err);
- }
- 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;
- BOOST_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());
- BOOST_TRY{
- managed_global_memory shm(open_only, str.c_str());
- gmem_erase_func func(str.c_str(), filepath, shm);
- shm.try_atomic_func(func);
- }
- BOOST_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);
- }
- } BOOST_CATCH_END
- }
- BOOST_CATCH(...){
- } BOOST_CATCH_END
- if(fd >= 0){
- close_lock_file(fd);
- }
- }
- public:
- static bool remove_old_gmem()
- {
- std::string refcstrRootDirectory;
- get_shared_dir(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_lockfile = 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_lockfile < 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_lockfile);
- }
- }
- //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 = false>
- 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
|