diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 22da3e089..5e0312890 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -15,6 +15,8 @@ # the run rule for each source, the result is a list of all # the run rules, which we can pass on to the test_suite rule: +import ../config/checks/config : requires ; + rule test_all { local all_rules = ; @@ -49,6 +51,22 @@ rule test_all ] ; } + for local fileb in [ glob cpp11_*.cpp ] + { + all_rules += [ run $(fileb) + : # additional args + : # test-files + : # requirements + [ requires cxx11_lambdas cxx11_rvalue_references cxx11_final cxx11_auto_declarations ] + acc:-lrt + acc-pa_risc:-lrt + gcc-mingw:"-lole32 -loleaut32 -lpsapi -ladvapi32" + hpux,gcc:"-Wl,+as,mpas" + windows,clang:"-lole32 -loleaut32 -lpsapi -ladvapi32" + linux:"-lrt" + ] ; + } + return $(all_rules) ; } diff --git a/example/cpp11_nonpersistent_shared_memory.cpp b/example/cpp11_nonpersistent_shared_memory.cpp new file mode 100644 index 000000000..898cc45ab --- /dev/null +++ b/example/cpp11_nonpersistent_shared_memory.cpp @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2006-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. +// +////////////////////////////////////////////////////////////////////////////// +#include +#include + +#if defined(BOOST_INTERPROCESS_BSD_DERIVATIVE) || defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + +//[doc_managed_nonpersistent_shared_memory +#include +#include +#include +#include +#include +#include +#include + +namespace { + struct semaphore final { + semaphore(char const* pszName) noexcept + : m_psem(sem_open(pszName, O_CREAT, /* accessible by current user only */ S_IRUSR|S_IWUSR, 1)) + {} + + ~semaphore() { + sem_close(m_psem); + } + + void wait() const& noexcept { + sem_wait(m_psem); + } + + void post() const& noexcept { + sem_post(m_psem); + } + private: + sem_t* m_psem; + }; + + struct SShared final { + char m_sz[12]; + }; +} + +int main(int argc, char *argv[]) +{ + using namespace boost::interprocess; + + bool bFirstProcess = false; + basic_managed_nonpersistent_shared_memory, iset_index> shm( + open_or_create, + "MySharedMemory", + 1000, + [&]() noexcept { + std::cout << "Shared memory segment created. Resetting semaphore.\n"; + bFirstProcess = true; + if(-1==sem_unlink("MySharedSemaphore")) { + if(EINVAL!=errno && ENOENT!=errno) { + std::cerr << "Deleting semaphore returned unexpected error.\n"; + std::exit(1); + } + } + } + ); + + semaphore sem("MySharedSemaphore"); + + sem.wait(); + + SShared* pshared = shm.find_or_construct("A")(); + char const str[] = { "Hello World" }; + + //Map the whole shared memory in this process + if(bFirstProcess) { + std::cout << "Acquired semaphore. Writing to shared memory.\n"; + std::copy(std::begin(str), std::end(str), pshared->m_sz); + + sem.post(); // release semaphore before starting process + + std::cout << "Start child process.\n"; + if(0 != std::system(argv[0])) { + std::cerr << "Child process failed.\n"; + return 1; + } + } else { + std::cout << "Acquired semaphore. Reading shared memory.\n"; + std::cout << pshared->m_sz << "\n"; + + bool const bEqual = std::equal(std::begin(str), std::end(str), pshared->m_sz); + sem.post(); + + if(!bEqual) { + std::cerr << "Error reading shared memory\n"; + return 1; + } + } + + return 0; +} +//] + +#else //BOOST_INTERPROCESS_BSD_DERIVATIVE + +int main() +{ + return 0; +} + +#endif //BOOST_INTERPROCESS_BSD_DERIVATIVE + +#include diff --git a/include/boost/interprocess/managed_nonpersistent_shared_memory.hpp b/include/boost/interprocess/managed_nonpersistent_shared_memory.hpp new file mode 100755 index 000000000..12dff3754 --- /dev/null +++ b/include/boost/interprocess/managed_nonpersistent_shared_memory.hpp @@ -0,0 +1,227 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-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_MANAGED_ROBUST_SHARED_MEMORY_HPP +#define BOOST_INTERPROCESS_MANAGED_ROBUST_SHARED_MEMORY_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif +# +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include + +#include +#include +#include +#include +#include +//These includes needed to fulfill default template parameters of +//predeclarations in interprocess_fwd.hpp +#include +#include + +namespace boost { +namespace interprocess { + +namespace ipcdetail { + +template +struct nonpersistentmem_open_or_create +{ + typedef ipcdetail::managed_open_or_create_impl + < nonpersistent_shared_memory_object, AllocationAlgorithm::Alignment, true, true> type; +}; + +} //namespace ipcdetail { + +//!A basic non-persistent shared memory named object creation class. +//!It emulates non-persistent shared memory with temporary files on +//!filesystems that support flock-style shared and exclusive locks. +//!When the shared memory is created, and the temporary file still exists +//!but is not accessed by any other process, its contents are cleared. +//! +//!Inherits all basic functionality from +//!basic_managed_memory_impl*/ +template + < + class CharType, + class AllocationAlgorithm, + template class IndexType + > +class basic_managed_nonpersistent_shared_memory + : public ipcdetail::basic_managed_memory_impl + ::type::ManagedOpenOrCreateUserOffset> + , private ipcdetail::nonpersistentmem_open_or_create::type +{ + #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + typedef ipcdetail::basic_managed_memory_impl + ::type::ManagedOpenOrCreateUserOffset> base_t; + typedef typename ipcdetail::nonpersistentmem_open_or_create::type base2_t; + + template + class create_open_func_callback : public ipcdetail::create_open_func { + using base_t = ipcdetail::create_open_func; + public: + + create_open_func_callback(BasicManagedMemoryImpl * const frontend, ipcdetail::create_enum_t type, Func&& fn) + : base_t(frontend, type), m_func(std::forward(fn)) {} + + bool operator()(void *addr, std::size_t size, bool created) const { + if(!base_t::operator()(addr, size, created)) return false; // return on failure + + if(created) { + m_func(); + } + return true; + } + private: + typename std::decay::type m_func; + }; + + basic_managed_nonpersistent_shared_memory *get_this_pointer() + { return this; } + + public: + typedef nonpersistent_shared_memory_object device_type; + typedef typename base_t::size_type size_type; + + private: + typedef typename base_t::char_ptr_holder_t char_ptr_holder_t; + BOOST_MOVABLE_BUT_NOT_COPYABLE(basic_managed_nonpersistent_shared_memory) + #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED + + public: //functions + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~basic_managed_nonpersistent_shared_memory() + {} + + //!Default constructor. Does nothing. + //!Useful in combination with move semantics + basic_managed_nonpersistent_shared_memory() + {} + + //!Creates shared memory and creates and places the segment manager if + //!segment was not created, or if no process is accessing the shared memory. + // When the segment was already created, it connects to the segment. + //!This can throw. + template + basic_managed_nonpersistent_shared_memory (open_or_create_t, + const char *name, + size_type size, + Func&& OnCreation, + const void *addr = 0, const permissions& perm = permissions()) + : base_t() + , base2_t(open_or_create, name, size, read_write, addr, + create_open_func_callback(get_this_pointer(), ipcdetail::DoOpenOrCreate, std::forward(OnCreation)), + perm) + {} + + //!Moves the ownership of "moved"'s managed memory to *this. + //!Does not throw + basic_managed_nonpersistent_shared_memory(BOOST_RV_REF(basic_managed_nonpersistent_shared_memory) moved) + { + basic_managed_nonpersistent_shared_memory tmp; + this->swap(moved); + tmp.swap(moved); + } + + //!Moves the ownership of "moved"'s managed memory to *this. + //!Does not throw + basic_managed_nonpersistent_shared_memory &operator=(BOOST_RV_REF(basic_managed_nonpersistent_shared_memory) moved) + { + basic_managed_nonpersistent_shared_memory tmp(boost::move(moved)); + this->swap(tmp); + return *this; + } + + //!Swaps the ownership of the managed shared memories managed by *this and other. + //!Never throws. + void swap(basic_managed_nonpersistent_shared_memory &other) + { + base_t::swap(other); + base2_t::swap(other); + } + + #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + + //!Tries to find a previous named allocation address. Returns a memory + //!buffer and the object count. If not found returned pointer is 0. + //!Never throws. + template + std::pair find (char_ptr_holder_t name) + { + if(base2_t::get_mapped_region().get_mode() == read_only){ + return base_t::template find_no_lock(name); + } + else{ + return base_t::template find(name); + } + } + + #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED +}; + +#ifdef BOOST_INTERPROCESS_DOXYGEN_INVOKED + +//!Typedef for a default basic_managed_shared_memory +//!of narrow characters +typedef basic_managed_nonpersistent_shared_memory + + ,iset_index> +managed_nonpersistent_shared_memory; + +//!Typedef for a default basic_managed_shared_memory +//!of wide characters +typedef basic_managed_nonpersistent_shared_memory + + ,iset_index> +wmanaged_shared_memory; + +//!Typedef for a default basic_managed_shared_memory +//!of narrow characters to be placed in a fixed address +typedef basic_managed_nonpersistent_shared_memory + + ,iset_index> +fixed_managed_shared_memory; + +//!Typedef for a default basic_managed_shared_memory +//!of narrow characters to be placed in a fixed address +typedef basic_managed_nonpersistent_shared_memory + + ,iset_index> +wfixed_managed_shared_memory; + + +#endif //#ifdef BOOST_INTERPROCESS_DOXYGEN_INVOKED + +} //namespace interprocess { +} //namespace boost { + +#include + +#endif //BOOST_INTERPROCESS_MANAGED_ROBUST_SHARED_MEMORY_HPP + diff --git a/include/boost/interprocess/nonpersistent_shared_memory_object.hpp b/include/boost/interprocess/nonpersistent_shared_memory_object.hpp new file mode 100755 index 000000000..0dcde6675 --- /dev/null +++ b/include/boost/interprocess/nonpersistent_shared_memory_object.hpp @@ -0,0 +1,254 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-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_ROBUST_SHARED_MEMORY_OBJECT_HPP +#define BOOST_INTERPROCESS_ROBUST_SHARED_MEMORY_OBJECT_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif +# +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS) +# include //O_CREAT, O_*... +# include //shm_xxx +# include //ftruncate, close +# include //mode_t, S_IRWXG, S_IRWXO, S_IRWXU, +# if defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) +# if defined(__FreeBSD__) +# include +# endif +# endif +#else +// +#endif + +//!\file +//!Describes a shared memory object management class. + +namespace boost { +namespace interprocess { + +//!A class that emulates non-persistent shared memory objects with a temporary file on +//!filesystems that support flock-style shared and exclusive locks. +//!When the shared memory object is created, and the backing file still exists +//!but is not accessed by any other process, its contents are cleared. +class nonpersistent_shared_memory_object +{ + #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + //Non-copyable and non-assignable + BOOST_MOVABLE_BUT_NOT_COPYABLE(nonpersistent_shared_memory_object) + #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED + + public: + //!Default constructor. Represents an empty nonpersistent_shared_memory_object. + nonpersistent_shared_memory_object(); + + //!Tries to create a shared memory object with name "name" and mode "mode", with the + //!access mode "mode". If the file previously exists, but is not accessed by another process + //!its contents are cleared. Otherwise throws an error. + nonpersistent_shared_memory_object(create_only_t, const char *name, mode_t mode, const permissions &perm = permissions()) + { this->priv_open_or_create(ipcdetail::DoCreate, name, mode, perm); } + + //!Tries to open a shared memory object with name "name", with the access mode "mode". + //!If the file does not previously exist, or it exists but is currently not opened by another + //!process, it throws an error. + nonpersistent_shared_memory_object(open_only_t, const char *name, mode_t mode) + { this->priv_open_or_create(ipcdetail::DoOpen, name, mode, permissions()); } + + //!Moves the ownership of "moved"'s shared memory object to *this. + //!After the call, "moved" does not represent any shared memory object. + //!Does not throw + nonpersistent_shared_memory_object(BOOST_RV_REF(nonpersistent_shared_memory_object) moved) + : m_handle(file_handle_t(ipcdetail::invalid_file())) + , m_mode(read_only) + { this->swap(moved); } + + //!Moves the ownership of "moved"'s shared memory to *this. + //!After the call, "moved" does not represent any shared memory. + //!Does not throw + nonpersistent_shared_memory_object &operator=(BOOST_RV_REF(nonpersistent_shared_memory_object) moved) + { + nonpersistent_shared_memory_object tmp(boost::move(moved)); + this->swap(tmp); + return *this; + } + + //!Swaps the nonpersistent_shared_memory_objects. Does not throw + void swap(nonpersistent_shared_memory_object &moved); + + //!Erases a shared memory object from the system. + //!Returns false on error. Never throws + static bool remove(const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. All mapped regions are still + //!valid after destruction. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~nonpersistent_shared_memory_object(); + + //!Returns the name of the shared memory object. + const char *get_name() const; + + //!Returns true if the size of the shared memory object + //!can be obtained and writes the size in the passed reference + bool get_size(offset_t &size) const; + + //!Returns access mode + mode_t get_mode() const; + + //!Returns mapping handle. Never throws. + mapping_handle_t get_mapping_handle() const; + + void truncate(offset_t length); + + #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + private: + + //!Closes a previously opened file mapping. Never throws. + void priv_close(); + + //!Opens or creates a shared memory object. + bool priv_open_or_create(ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm); + + file_handle_t m_handle; + mode_t m_mode; + std::string m_filename; + #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED +}; + +#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + +inline nonpersistent_shared_memory_object::nonpersistent_shared_memory_object() + : m_handle(file_handle_t(ipcdetail::invalid_file())) + , m_mode(read_only) +{} + +inline nonpersistent_shared_memory_object::~nonpersistent_shared_memory_object() +{ this->priv_close(); } + + +inline const char *nonpersistent_shared_memory_object::get_name() const +{ return m_filename.c_str(); } + +inline bool nonpersistent_shared_memory_object::get_size(offset_t &size) const +{ return ipcdetail::get_file_size((file_handle_t)m_handle, size); } + +inline void nonpersistent_shared_memory_object::swap(nonpersistent_shared_memory_object &other) +{ + boost::adl_move_swap(m_handle, other.m_handle); + boost::adl_move_swap(m_mode, other.m_mode); + m_filename.swap(other.m_filename); +} + +inline mapping_handle_t nonpersistent_shared_memory_object::get_mapping_handle() const +{ + return ipcdetail::mapping_handle_from_file_handle(m_handle); +} + +inline mode_t nonpersistent_shared_memory_object::get_mode() const +{ return m_mode; } + + +inline void nonpersistent_shared_memory_object::truncate(offset_t length) +{ + if(!ipcdetail::truncate_file(m_handle, length)){ + error_info err = system_error_code(); + throw interprocess_exception(err); + } +} + +inline bool nonpersistent_shared_memory_object::priv_open_or_create + (ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm) +{ + BOOST_ASSERT(type==ipcdetail::DoCreate || type==ipcdetail::DoOpen); + + m_filename = filename; + m_mode = mode; + + std::string shmfile; + ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, shmfile); + + // Try to create or open file with exclusive lock. If that succeeds we are the first to open that file. + m_handle = ::open(shmfile.c_str(), ((int)mode) | O_CREAT | O_EXLOCK | O_NONBLOCK, perm.get_permissions()); + if(ipcdetail::invalid_file()==m_handle) { + if(EWOULDBLOCK != system_error_code()) { + throw interprocess_exception(system_error_code()); + } + + if(ipcdetail::DoCreate==type) { + throw interprocess_exception(already_exists_error); + } + + // We are not the first to open backing file, block until we get shared lock instead + m_handle = ::open(shmfile.c_str(), ((int)mode) | O_SHLOCK); + if(ipcdetail::invalid_file()==m_handle) { + throw interprocess_exception(system_error_code()); + } + } else { + if(ipcdetail::DoOpen==type + // We are the first to open this file. Make sure it's truncated to 0 bytes. + || !ipcdetail::truncate_file(m_handle, 0) + // Degrade lock to shared lock when we have truncated file + // This operation is not atomic. The lock is released before the shared lock is acquired. + // However, this does not pose a problem: + // a) Another process might be waiting for a shared lock above and acquires + // it before we get the shared lock here. The order of shared locks does not matter. + // b) When the lock is released another process acquires the exclusive lock again + // but this will just truncate the file to size 0 again and then we proceed + // with a shared lock here. + || 0!=flock(m_handle, LOCK_SH)) + { + auto const err = ipcdetail::DoOpen==type ? error_info(not_found_error) : error_info(system_error_code()); + this->priv_close(); + throw interprocess_exception(err); + } + } + + return true; +} + +inline void nonpersistent_shared_memory_object::priv_close() +{ + if(m_handle != ipcdetail::invalid_file()){ + ipcdetail::close_file(m_handle); + m_handle = ipcdetail::invalid_file(); + } +} + +#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED + +} //namespace interprocess { +} //namespace boost { + +#include + +#endif //BOOST_INTERPROCESS_ROBUST_SHARED_MEMORY_OBJECT_HPP