Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r60513 - trunk/boost/interprocess/detail
From: igaztanaga_at_[hidden]
Date: 2010-03-12 08:16:39


Author: igaztanaga
Date: 2010-03-12 08:16:38 EST (Fri, 12 Mar 2010)
New Revision: 60513
URL: http://svn.boost.org/trac/boost/changeset/60513

Log:
Interprocess:
  [@https://svn.boost.org/trac/boost/ticket/3439 #3439],
  [@https://svn.boost.org/trac/boost/ticket/3846 #3846],
  [@https://svn.boost.org/trac/boost/ticket/3947 #3947],
  [@https://svn.boost.org/trac/boost/ticket/3985 #3985].
Intrusive:
  [@https://svn.boost.org/trac/boost/ticket/3668 #3668],
  [@https://svn.boost.org/trac/boost/ticket/3339 #3688],
  [@https://svn.boost.org/trac/boost/ticket/3698 #3698],
  [@https://svn.boost.org/trac/boost/ticket/3706 #3706],
  [@https://svn.boost.org/trac/boost/ticket/3721 #3721].
  [@https://svn.boost.org/trac/boost/ticket/3729 #3729],
  [@https://svn.boost.org/trac/boost/ticket/3746 #3746],
  [@https://svn.boost.org/trac/boost/ticket/3781 #3781],
  [@https://svn.boost.org/trac/boost/ticket/3829 #3829],
  [@https://svn.boost.org/trac/boost/ticket/3840 #3840],
  [@https://svn.boost.org/trac/boost/ticket/3339 #3339],
  [@https://svn.boost.org/trac/boost/ticket/3419 #3419],
  [@https://svn.boost.org/trac/boost/ticket/3431 #3431],
Text files modified:
   trunk/boost/interprocess/detail/intermodule_singleton.hpp | 1073 ++++++++++++++++++++++++++++++++++++---
   1 files changed, 976 insertions(+), 97 deletions(-)

Modified: trunk/boost/interprocess/detail/intermodule_singleton.hpp
==============================================================================
--- trunk/boost/interprocess/detail/intermodule_singleton.hpp (original)
+++ trunk/boost/interprocess/detail/intermodule_singleton.hpp 2010-03-12 08:16:38 EST (Fri, 12 Mar 2010)
@@ -1,10 +1,3 @@
-/* Copyright 2006-2009 Joaquin M Lopez Munoz.
- * 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/flyweight for library home page.
- */
 //////////////////////////////////////////////////////////////////////////////
 //
 // (C) Copyright Ion Gaztanaga 2009-2009. Distributed under the Boost
@@ -15,7 +8,6 @@
 //
 //////////////////////////////////////////////////////////////////////////////
 
-
 #ifndef BOOST_INTERPROCESS_INTERMODULE_SINGLETON_HPP
 #define BOOST_INTERPROCESS_INTERMODULE_SINGLETON_HPP
 
@@ -24,137 +16,884 @@
 #endif
 
 #include <boost/interprocess/detail/config_begin.hpp>
+#include <boost/interprocess/detail/workaround.hpp>
+
+#include <boost/interprocess/managed_shared_memory.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include <boost/interprocess/detail/os_thread_functions.hpp>
 #include <boost/interprocess/managed_shared_memory.hpp>
+#include <boost/interprocess/detail/tmp_dir_helpers.hpp>
+#include <boost/interprocess/detail/os_file_functions.hpp>
+#include <boost/interprocess/streams/bufferstream.hpp>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+
+#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>
+#endif
 
 namespace boost{
 namespace interprocess{
 namespace detail{
 
-const char *get_singleton_unique_name()
-{ return "unique_name";
+namespace intermodule_singleton_helpers {
 
-template<typename C>
-struct atomic_functor
+const int GMemMarkToBeRemoved = -1;
+const int GMemNotPresent = -2;
+
+typedef char pid_str_t[sizeof(OS_process_id_t)*3+1];
+
+inline void get_pid_str(pid_str_t &pid_str)
 {
- atomic_functor(managed_shared_memory &seg)
- : seg_(seg)
- {}
+ OS_process_id_t pid = get_current_process_id();
+ bufferstream bstream(pid_str, sizeof(pid_str));
+ bstream << pid << std::ends;
+}
+
+inline const char *get_lock_file_base_name()
+{ return "bip.gmem.lock."; }
+
+inline const char *get_lock_file_subdir_name()
+{ return "bip.gmem.locks"; }
+
+inline void create_and_get_lock_file_path(std::string &s)
+{
+ std::string filename(get_lock_file_base_name());
+ pid_str_t pid_str;
+ get_pid_str(pid_str);
+ filename += pid_str;
+ tmp_folder(s);
+ if(!open_or_create_directory(s.c_str())){
+ throw interprocess_exception(error_info(system_error_code()));
+ }
+ s += "/";
+ s += get_lock_file_subdir_name();
+ if(!open_or_create_directory(s.c_str())){
+ throw interprocess_exception(error_info(system_error_code()));
+ }
+ s += "/";
+ s += filename;
+}
+
+inline void get_lock_file_path(std::string &s)
+{
+ std::string filename(get_lock_file_base_name());
+ pid_str_t pid_str;
+ get_pid_str(pid_str);
+ filename += pid_str;
+
+ tmp_folder(s);
+ s += "/";
+ s += get_lock_file_subdir_name();
+ s += "/";
+ s += filename;
+}
+
+inline const char *get_shm_base_name()
+{ return "bip.gmem.shm."; }
 
- inline void operator()()
+inline void get_shm_name(std::string &shm_name)
+{
+ shm_name = get_shm_base_name();
+ pid_str_t pid_str;
+ get_pid_str(pid_str);
+ shm_name += pid_str;
+}
+
+inline std::size_t get_shm_size()
+{ return 65536; }
+
+inline bool is_other_process_gmem_lock_file(const char *filename, std::string &pid_string)
+{
+ const char * const base_str = get_lock_file_base_name();
+ const std::size_t base_str_len = std::strlen(base_str);
+ const std::size_t filename_length = std::strlen(filename);
+
+ //Get pid string
+ pid_str_t pid_str;
+ get_pid_str(pid_str);
+
+ //Check filename length
+ const std::size_t pid_str_len = std::strlen(pid_str);
+ if(base_str_len >= filename_length){
+ return false;
+ }
+ //Check base str
+ for(std::size_t i = 0, max = base_str_len; i != max; ++i){
+ if(base_str[i] != filename[i]){
+ return false;
+ }
+ }
+
+ pid_string = &filename[base_str_len];
+ if((base_str_len + pid_str_len) != filename_length){
+ return true;
+ }
+
+ //Check pid
+ for(std::size_t i = base_str_len, max = filename_length, pid_i = 0; i != max; ++i, ++pid_i){
+ if(filename[i] != pid_str[pid_i]){
+ return true;
+ }
+ }
+ return false;
+}
+
+inline void apply_gmem_erase_logic(const char *filepath, const char *filename);
+
+#if (defined BOOST_INTERPROCESS_WINDOWS)
+
+inline bool remove_old_gmem()
+{
+ void * hFile; // Handle to directory
+ winapi::win32_find_data_t FileInformation; // File information
+
+ //Get base directory
+ std::string str;
+ tmp_folder(str);
+ str += "/";
+ str += get_lock_file_subdir_name();
+ const std::size_t base_root_dir_len = str.size();
+
+ //Find all files and directories
+ str += "\\*.*";
+ hFile = winapi::find_first_file(str.c_str(), &FileInformation);
+ if(hFile != winapi::invalid_handle_value){
+ do{ //Now loop every file
+ str.erase(base_root_dir_len);
+ //If it's not "." or ".." skip it
+ if(FileInformation.cFileName[0] != '.'){
+ str += "\\"; str += FileInformation.cFileName;
+ //If it's a file, apply erase logic
+ if(!(FileInformation.dwFileAttributes & winapi::file_attribute_directory)){
+ apply_gmem_erase_logic(str.c_str(), FileInformation.cFileName);
+ }
+ }
+ //Go to the next file
+ } while(winapi::find_next_file(hFile, &FileInformation) == 1);
+
+ // Close handle and see if the loop has ended with an error
+ winapi::find_close(hFile);
+ if(winapi::get_last_error() != winapi::error_no_more_files){
+ return false;
+ }
+ }
+ return true;
+}
+
+
+struct locking_file_serial_id
+{
+ int fd;
+ unsigned long dwVolumeSerialNumber;
+ unsigned long nFileIndexHigh;
+ unsigned long nFileIndexLow;
+ volatile boost::uint32_t ref_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)
+{
+ while(1){
+ file_handle_t handle = create_or_open_file(name);
+ 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);
+ 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.ref_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
+
+inline bool remove_old_gmem()
+{
+ std::string refcstrRootDirectory;
+ tmp_folder(refcstrRootDirectory);
+ refcstrRootDirectory += "/";
+ refcstrRootDirectory += get_lock_file_subdir_name();
+
+ DIR *d = opendir(refcstrRootDirectory.c_str());
+ if(!d) {
+ return false;
+ }
+
+ struct dir_close
    {
- referenced_instance**pptr = seg.find_or_construct<referenced_instance*>(unique_instance)();
- detail::atomic_cas32(
+ DIR *d_;
+ dir_close(DIR *d) : d_(d) {}
+ ~dir_close() { ::closedir(d_); }
+ } dc(d); (void)dc;
+
+ struct ::dirent *de;
+ struct ::stat st;
+ std::string fn;
+
+ while((de=::readdir(d))) {
+ if( de->d_name[0] == '.' && ( de->d_name[1] == '\0'
+ || (de->d_name[1] == '.' && de->d_name[2] == '\0' )) ){
+ continue;
+ }
+ fn = refcstrRootDirectory;
+ fn += '/';
+ fn += de->d_name;
+
+ if(std::remove(fn.c_str())) {
+ if(::stat(fn.c_str(), & st)) {
+ return false;
+ }
+ //If it's a file, apply erase logic
+ if(!S_ISDIR(st.st_mode)) {
+ apply_gmem_erase_logic(fn.c_str(), de->d_name);
+ }
+ }
    }
+ return true;
+}
 
- managed_shared_memory &seg_;
+struct locking_file_serial_id
+{
+ int fd;
+ dev_t st_dev;
+ ino_t st_ino;
+ volatile boost::uint32_t ref_count;
 };
 
-template<class C>
-struct intermodule_singleton_instantiator
+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)
+{
+ while(1){
+ int fd = create_or_open_file(name);
+ 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);
+ 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.ref_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
+
+struct gmem_erase_func
 {
- intermodule_singleton_instantiator()
- : ppref(0), pc(0)
+ gmem_erase_func(const char *shm_name, const char *lock_file_path, managed_shared_memory & shm)
+ :shm_name_(shm_name), lock_file_path_(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(lock_file_path_);
+ shared_memory_object::remove(shm_name_);
+ }
    
- bool done = false;
+ const char * const shm_name_;
+ const char * const lock_file_path_;
+ managed_shared_memory & shm_;
+};
+
+inline void apply_gmem_erase_logic(const char *filepath, const char *filename)
+{
+ int fd = GMemMarkToBeRemoved;
+ try{
+ std::string str;
+ if(!is_other_process_gmem_lock_file(filename, str)){
+ return;
+ }
+ fd = try_open_and_lock_file(filepath);
+ if(fd < 0){
+ return;
+ }
+ str.insert(0, get_shm_base_name());
       try{
- managed_shared_memory seg(interprocess::create_only, get_singleton_unique_name(), 16384);
- //Register cleanup??? We can't because the address of a local function might be the
- //address of a dll, and that dll can be UNLOADED!!!
- }
- catch(interprocess_exception &ex){
- if(ex.get_error_code() != already_exists_error){
- managed_shared_memory seg(open_only, "unique_name", 16384);
- seg.find_or_construct<referenced_instance*>(unique_instance)();
- }
- else{
- throw;
+ managed_shared_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(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 {
 
+struct lock_file_logic
+{
+ lock_file_logic(managed_shared_memory &shm)
+ : mshm(shm)
+ {}
+
+ locking_file_serial_id * register_lock_file(int fd)
    {
- ppref=seg.find_or_construct<referenced_instance*>(unique_instance)();
- if(*ppref){
- /* As in some OSes Boost.Interprocess memory segments can outlive
- * their associated processes, there is a possibility that we
- * retrieve a dangling pointer (coming from a previous aborted run,
- * for instance). Try to protect against this by checking that
- * the contents of the pointed object are consistent.
- */
- if(std::strcmp(segment_name,(*ppref)->segment_name)!=0){
- *ppref=0; /* dangling pointer! */
- }
- else ++((*ppref)->ref);
- }
- }
- if(!*ppref){
- std::auto_ptr<referenced_instance> apc(
- new referenced_instance(segment_name));
- interprocess::scoped_lock<interprocess::named_mutex> lock(mutex);
- ppref=seg.find_or_construct<referenced_instance*>(
- typeid(C).name())((referenced_instance*)0);
- if(!*ppref)*ppref=apc.release();
- ++((*ppref)->ref);
+ locking_file_serial_id *pinfo = mshm.construct<locking_file_serial_id>("lock_file_fd")();
+ fill_file_serial_id(fd, *pinfo);
+ return pinfo;
    }
- pc=&(*ppref)->c;
+
+ void operator()(void)
+ {
+ retry_with_new_shm = false;
+ locking_file_serial_id *pserial_id = mshm.find<locking_file_serial_id>("lock_file_fd").first;
+
+ int fd;
+ if(!pserial_id){
+ fd = GMemNotPresent;
+ }
+ else{
+ fd = pserial_id->fd;
+ }
+ if(fd == GMemNotPresent){
+ std::string lck_str;
+ create_and_get_lock_file_path(lck_str);
+ int fd = intermodule_singleton_helpers::open_or_create_and_lock_file(lck_str.c_str());
+ 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;
+ }
+ else{
+ this->register_lock_file(fd);
+ }
+ }
+ else if (fd == GMemMarkToBeRemoved){
+ retry_with_new_shm = true;
+ }
+ 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{
+ //Valid lock file, just increment ref count
+ atomic_inc32(&pserial_id->ref_count);
+ }
    }
-
- ~intermodule_singleton_instantiator()
+
+ managed_shared_memory &mshm;
+ bool retry_with_new_shm;
+};
+
+} //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<int Dummy>
+class intermodule_singleton_common
+{
+ public:
+ typedef void*(init_func_t)(managed_shared_memory &);
+ typedef void (fini_func_t)(void *, managed_shared_memory &);
+
+ static void destroy_pc(void *ptr, fini_func_t fini_func)
    {
- /* As in construction time, actual deletion is performed outside the
- * lock to avoid leaving the lock dangling in case of crash.
- */
-
- referenced_instance* pref=0;
+ if(ptr)
+ fini_func(ptr, get_shm());
+ if(1 == atomic_dec32(&shm_ref_count)){
+ destroy_shm();
+ }
+ }
+
+ static void initialize_pc(void *&ptr, volatile boost::uint32_t &barrier, init_func_t ini_func);
+
+ private:
+ static managed_shared_memory &get_shm()
    {
- interprocess::scoped_lock<interprocess::named_mutex> lock(mutex);
- if(--((*ppref)->ref)==0){
- pref=*ppref;
- *ppref=0;
+ return *static_cast<managed_shared_memory *>(static_cast<void *>(&shm_mem));
+ }
+
+ static const std::size_t MemSize = ((sizeof(managed_shared_memory)-1)/sizeof(max_align))+1u;
+ static void initialize_shm();
+ static void destroy_shm();
+ //Static data, zero-initalized without any dependencies
+ static volatile boost::uint32_t shm_ref_count;
+ static volatile boost::uint32_t shm_initialized;
+ static max_align shm_mem[MemSize];
+};
+
+template<int Dummy>
+volatile boost::uint32_t intermodule_singleton_common<Dummy>::shm_ref_count;
+
+template<int Dummy>
+const std::size_t intermodule_singleton_common<Dummy>::MemSize;
+
+template<int Dummy>
+volatile boost::uint32_t intermodule_singleton_common<Dummy>::shm_initialized;
+
+template<int Dummy>
+max_align intermodule_singleton_common<Dummy>::shm_mem[intermodule_singleton_common<Dummy>::MemSize];
+
+template<int Dummy>
+void intermodule_singleton_common<Dummy>::initialize_shm()
+{
+ std::string s;
+ intermodule_singleton_helpers::get_shm_name(s);
+ const char *ShmName = s.c_str();
+ const std::size_t ShmSize = intermodule_singleton_helpers::get_shm_size();;
+ while(1){
+ ::boost::uint32_t tmp = atomic_cas32(&shm_initialized, 1, 0);
+ if(tmp >= 2u){
+ break;
+ }
+ else if(tmp == 1u){
+ thread_yield();
+ }
+ else{ //(tmp == 0u)
+ try{
+ intermodule_singleton_helpers::remove_old_gmem();
+ ::new (&get_shm())managed_shared_memory(open_or_create, ShmName, ShmSize);
+ intermodule_singleton_helpers::lock_file_logic f(get_shm());
+ get_shm().atomic_func(f);
+ if(f.retry_with_new_shm){
+ get_shm().~managed_shared_memory();
+ atomic_write32(&shm_initialized, 0);
+ }
+ else{
+ atomic_write32(&shm_initialized, 2);
+ break;
+ }
+ }
+ catch(...){
+ //
+ throw;
+ }
       }
    }
- if(pref)delete pref;
+}
+
+struct unlink_shmlogic
+{
+ unlink_shmlogic(managed_shared_memory &mshm)
+ : mshm_(mshm)
+ {}
+ void operator()()
+ {
+ intermodule_singleton_helpers::locking_file_serial_id *pserial_id =
+ mshm_.find<intermodule_singleton_helpers::locking_file_serial_id>
+ ("lock_file_fd").first;
+ assert(0 != pserial_id);
+ if(1 == atomic_dec32(&pserial_id->ref_count)){
+ int fd = pserial_id->fd;
+ if(fd > 0){
+ pserial_id->fd = intermodule_singleton_helpers::GMemMarkToBeRemoved;
+ std::string s;
+ intermodule_singleton_helpers::get_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());
+ }
+ }
    }
+ managed_shared_memory &mshm_;
 };
 
+template<int Dummy>
+void intermodule_singleton_common<Dummy>::destroy_shm()
+{
+ if(!atomic_read32(&shm_ref_count)){
+ unlink_shmlogic f(get_shm());
+ get_shm().atomic_func(f);
+ (get_shm()).~managed_shared_memory();
+ atomic_write32(&shm_initialized, 0u);
+ intermodule_singleton_helpers::remove_old_gmem();
+ }
+}
+
+template<int Dummy>
+void intermodule_singleton_common<Dummy>::initialize_pc(void *&ptr, volatile boost::uint32_t &barrier, init_func_t init_func)
+{
+ // insert memory barrier
+ if(atomic_read32(&barrier) != 2u){
+ //Try to pass from 0 to 1, and insert memory barrier
+ ::boost::uint32_t tmp_barrier = atomic_cas32(&barrier, 1, 0);
+ if(tmp_barrier == 0u){
+ try{
+ initialize_shm();
+ atomic_inc32(&shm_ref_count);
+ //This can throw
+ void *tmp = init_func(get_shm());
+ //Barrier...
+ atomic_write32(&barrier, 1u);
+ //This won't throw
+ ptr = tmp;
+ //Memory barrier inserted, all previous operations should complete
+ //before this one
+ atomic_inc32(&barrier);
+ }
+ catch(...){
+ //Mark singleton failed to initialize
+ atomic_write32(&barrier, 3u);
+ throw;
+ }
+ }
+ else if(tmp_barrier == 1u){
+ //Another thread is initializing the singleton, just wait
+ while(1){
+ tmp_barrier = atomic_read32(&barrier);
+ if(tmp_barrier >= 2u){
+ //Already initialized, or exception thrown by initializer thread
+ break;
+ }
+ else if(tmp_barrier == 1u){
+ detail::thread_yield();
+ }
+ else{
+ //This can't be happening!
+ assert(0);
+ }
+ }
+ }
+ if(tmp_barrier > 2u){
+ //Exception thrown, singleton initialization failed
+ throw interprocess_exception();
+ }
+ }
+ assert(ptr != 0);
+}
+
+
+//Now this class is a classic singleton, initializing the singleton in
+//the first get() function call
+
 template<typename C>
-struct intermodule_singleton
+class intermodule_singleton
 {
- static C& get()
- {
- static intermodule_singleton_instantiator<C> instance;
- return instance.get();
- }
+ public:
+ static C& get() //Let's make inlining easy
+ {
+ if(!pc){
+ if(deleter.dummy_function()) //This forces deleter instantiation, for reference counted destruction
+ intermodule_singleton_common<0>::initialize_pc(pc, barrier, initialize);
+ }
+ return *static_cast<C*>(pc);
+ }
+
+ struct ref_count_ptr
+ {
+ ref_count_ptr(C *p, boost::uint32_t count)
+ : ptr(p), ref_count(count)
+ {}
+ C *ptr;
+ volatile boost::uint32_t ref_count;
+ };
+
+ private:
+ //These will be zero-initialized without any constructor call dependency
+ static void* pc;
+ static volatile boost::uint32_t barrier;
+ //This class destructor will trigger singleton destruction
+ static struct deleter_type
+ {
+ bool dummy_function()
+ { return m_dummy == 0; }
 
-private:
+ ~deleter_type()
+ {
+ intermodule_singleton_common<0>::destroy_pc(pc, finalize);
+ }
+ //Dummy volatile so that the compiler can't resolve its value at compile-time
+ //and can't avoid deleter_type instantiation if dummy_function() is called.
+ static volatile int m_dummy;
+ } deleter;
 
- C& get()const{return *pc;}
-
- private:
- interprocess::managed_shared_memory seg;
- struct referenced_instance
- {
- referenced_instance(const char* segment_name_):ref(0)
+
+ struct init_atomic_func
+ {
+ init_atomic_func(managed_shared_memory &m)
+ : mshm(m)
+ {}
+ void operator()()
       {
- std::strcpy(segment_name,segment_name_);
+ ref_count_ptr *rcount = mshm.find<ref_count_ptr>(unique_instance).first;
+ if(!rcount){
+ C *p = new C();
+ try{
+ rcount = mshm.construct<ref_count_ptr>(unique_instance)(p, 0u);
+ }
+ catch(...){
+ delete p;
+ throw;
+ }
+ }
+ atomic_inc32(&rcount->ref_count);
+ ret_ptr = rcount->ptr;
       }
+ managed_shared_memory &mshm;
+ void *ret_ptr;
+ };
 
- ~referenced_instance(){segment_name[0]='\0';}
+ struct fini_atomic_func
+ {
+ fini_atomic_func(managed_shared_memory &m)
+ : mshm(m)
+ {}
+ void operator()()
+ {
+ ref_count_ptr *rcount = mshm.find<ref_count_ptr>(unique_instance).first;
+ //The object must exist
+ assert(rcount);
+ //Check if last reference
+ if(atomic_dec32(&rcount->ref_count) == 1){
+ //If last, destroy the object
+ assert(rcount->ptr != 0);
+ delete rcount->ptr;
+ //Now destroy shm entry
+ bool destroyed = mshm.destroy<ref_count_ptr>(unique_instance);
+ (void)destroyed; assert(destroyed == true);
+ }
+ }
+ managed_shared_memory &mshm;
+ void *ret_ptr;
+ };
 
- char segment_name[128]; /* used to detect dangling pointers */
- mutable long ref;
- C c;
- }** ppref;
- C* pc;
- };
-};
 
-template<typename C>
-inline void atomic_functor<C>::operator()()
-{
- referenced_instance *pptr = seg.find_or_construct<referenced_instance>(unique_instance)();
+ static void *initialize(managed_shared_memory &mshm)
+ {
+ init_atomic_func f(mshm);
+ mshm.atomic_func(f);
+ return f.ret_ptr;
+ }
+
+ static void finalize(void *p, managed_shared_memory &mshm)
+ { (void)p;
+ fini_atomic_func f(mshm);
+ mshm.atomic_func(f);
+ }
 };
 
+template <typename C>
+volatile int intermodule_singleton<C>::deleter_type::m_dummy;
+
+//These will be zero-initialized by the loader
+template <typename C>
+void *intermodule_singleton<C>::pc;
+
+template <typename C>
+volatile boost::uint32_t intermodule_singleton<C>::barrier;
+
+template <typename C>
+typename intermodule_singleton<C>::deleter_type intermodule_singleton<C>::deleter;
+
 
 } //namespace detail{
 } //namespace interprocess{
@@ -163,3 +902,143 @@
 #include <boost/interprocess/detail/config_end.hpp>
 
 #endif
+
+
+/*
+
+
+
+
+
+
+ if try_lock_file
+ open_shm_1
+open_or_create_shm_1
+ atomic_1
+ mark_to_be_removed
+ remove_file
+ remove_shm
+ close
+
+atomic_1
+ if not present fd
+ if(!create_and_lock) //delete in course, try again later
+ mark_to_be_removed
+ remove_shm_1
+ try_again
+ register_fd
+ else if to_be_removed
+ try_again
+ else if not_valid_fd
+ mark_to_be_removed
+ remove_shm_1
+ try_again
+ else if(valid_fd)
+ continue;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hint: fcntl is a nightmare because:
+
+- to check if a file is locked you must open an new descriptor
+- if you close any descriptor to that file all locks on that file are gone
+- so each time you want to check something is locked without disburbing
+ anything, you must leak that descriptor.
+
+Hints to have only one open descriptor:
+
+- Mark the lock file with a magic string.
+- Store the file descriptor in shared memory.
+- Open or create the shared memory:
+ - If created, no problem,
+ create a file and lock it in a loop testing that the file exists
+ - If opened, take the the stored fd:
+ - Take a lock on shared memory, if time exceeds too much time, unlink it and start again
+ - IF it's a regular file (IFREG, using fstat to check) AND
+ its size is the expected (fstat) AND
+ contains the magic number.
+ LOCK IT
+ ELSE
+ the fd is invalid so unlink the shared memory and try to create it again
+
+
+REMOVE_OLD_GMEM()
+
+lock semaphore
+
+loop all files in /tmp/boost_interprocess/gmem
+ check pattern(name) #PID#.lock
+ if (!pattern)
+ continue;
+ pid = extract_pid(name)
+ if(pid == self_pid)
+ continue;
+ open(name);
+ if(::fnctl(fd, GET_LCK)
+ assert(flock.pid != self_pid)
+ continue;
+ shm_remove(bip_gmem_#PID#)
+ unlink(name);
+
+unlock_semaphore
+
+
+
+STATIC_SINGLETON_CONSTRUCTOR
+
+remove_old_gmem()
+
+ managed_shared_memory shm;
+ bool completed = false;
+ while(!completed){
+ exclusive_lock(semaphore);
+ try{
+ managed_shared_memory tmp(create_only, bip_gmem_#SELFPID#, SIZE);
+ shm = ::boost::move(tmp);
+ if(!::open(/tmp/boost_interprocess/gmem/#PID#.lock) ||
+ !::fnctl(fd, LOCK)
+ throw whatever;
+ leave file locked;
+ }
+ catch(interprocess_exception &e){
+ if(e.error_code() != already_exists_error)){
+ throw;
+ }
+ try{
+ managed_shared_memory tmp(open_only, bip_gmem_#SELFPID#)
+ shm = ::boost::move(tmp);
+ }
+ catch(...){
+ if(e.error_code() != not_found_error)){
+ throw;
+ }
+ }
+ }
+ completed = true;
+ }
+ ref_counted_type *rcount = shm.find_or_create<ref_counted_type>(unique_instance)(int(0));
+ atomic_inc(rcount->cnt);
+
+STATIC_SINGLETON_DESTRUCTOR
+
+remove_old_mem()
+
+exclusive_lock(semaphore)
+ ref_count_type *rcount = shm.find_or_create<ref_count_type>("ref_count")(int(0));
+ if(!atomic_dec(rcount->cnt))
+ shm.destroy<T>(unique_instance);
+
+*/


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk