| ////////////////////////////////////////////////////////////////////////////// |
| // |
| // (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. |
| // |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| #ifndef BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL |
| #define BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL |
| |
| #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/os_thread_functions.hpp> |
| #include <boost/interprocess/detail/os_file_functions.hpp> |
| #include <boost/interprocess/creation_tags.hpp> |
| #include <boost/interprocess/mapped_region.hpp> |
| #include <boost/interprocess/detail/utilities.hpp> |
| #include <boost/interprocess/detail/type_traits.hpp> |
| #include <boost/interprocess/detail/atomic.hpp> |
| #include <boost/interprocess/detail/interprocess_tester.hpp> |
| #include <boost/interprocess/creation_tags.hpp> |
| #include <boost/interprocess/detail/mpl.hpp> |
| #include <boost/interprocess/permissions.hpp> |
| #include <boost/container/detail/type_traits.hpp> //alignment_of, aligned_storage |
| #include <boost/interprocess/sync/spin/wait.hpp> |
| #include <boost/move/move.hpp> |
| #include <boost/cstdint.hpp> |
| |
| namespace boost { |
| namespace interprocess { |
| |
| #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) |
| namespace ipcdetail{ class interprocess_tester; } |
| |
| |
| template<class DeviceAbstraction> |
| struct managed_open_or_create_impl_device_id_t |
| { |
| typedef const char *type; |
| }; |
| |
| #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS |
| |
| class xsi_shared_memory_file_wrapper; |
| class xsi_key; |
| |
| template<> |
| struct managed_open_or_create_impl_device_id_t<xsi_shared_memory_file_wrapper> |
| { |
| typedef xsi_key type; |
| }; |
| |
| #endif //BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS |
| |
| #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED |
| |
| namespace ipcdetail { |
| |
| |
| template <bool StoreDevice, class DeviceAbstraction> |
| class managed_open_or_create_impl_device_holder |
| { |
| public: |
| DeviceAbstraction &get_device() |
| { static DeviceAbstraction dev; return dev; } |
| |
| const DeviceAbstraction &get_device() const |
| { static DeviceAbstraction dev; return dev; } |
| }; |
| |
| template <class DeviceAbstraction> |
| class managed_open_or_create_impl_device_holder<true, DeviceAbstraction> |
| { |
| public: |
| DeviceAbstraction &get_device() |
| { return dev; } |
| |
| const DeviceAbstraction &get_device() const |
| { return dev; } |
| |
| private: |
| DeviceAbstraction dev; |
| }; |
| |
| template<class DeviceAbstraction, std::size_t MemAlignment, bool FileBased, bool StoreDevice> |
| class managed_open_or_create_impl |
| : public managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction> |
| { |
| //Non-copyable |
| BOOST_MOVABLE_BUT_NOT_COPYABLE(managed_open_or_create_impl) |
| |
| typedef typename managed_open_or_create_impl_device_id_t<DeviceAbstraction>::type device_id_t; |
| typedef managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction> DevHolder; |
| enum |
| { |
| UninitializedSegment, |
| InitializingSegment, |
| InitializedSegment, |
| CorruptedSegment |
| }; |
| |
| public: |
| static const std::size_t |
| ManagedOpenOrCreateUserOffset = |
| ct_rounded_size |
| < sizeof(boost::uint32_t) |
| , MemAlignment ? (MemAlignment) : |
| (::boost::container::container_detail::alignment_of |
| < ::boost::container::container_detail::max_align_t >::value) |
| >::value; |
| |
| managed_open_or_create_impl() |
| {} |
| |
| managed_open_or_create_impl(create_only_t, |
| const device_id_t & id, |
| std::size_t size, |
| mode_t mode, |
| const void *addr, |
| const permissions &perm) |
| { |
| priv_open_or_create |
| ( DoCreate |
| , id |
| , size |
| , mode |
| , addr |
| , perm |
| , null_mapped_region_function()); |
| } |
| |
| managed_open_or_create_impl(open_only_t, |
| const device_id_t & id, |
| mode_t mode, |
| const void *addr) |
| { |
| priv_open_or_create |
| ( DoOpen |
| , id |
| , 0 |
| , mode |
| , addr |
| , permissions() |
| , null_mapped_region_function()); |
| } |
| |
| |
| managed_open_or_create_impl(open_or_create_t, |
| const device_id_t & id, |
| std::size_t size, |
| mode_t mode, |
| const void *addr, |
| const permissions &perm) |
| { |
| priv_open_or_create |
| ( DoOpenOrCreate |
| , id |
| , size |
| , mode |
| , addr |
| , perm |
| , null_mapped_region_function()); |
| } |
| |
| template <class ConstructFunc> |
| managed_open_or_create_impl(create_only_t, |
| const device_id_t & id, |
| std::size_t size, |
| mode_t mode, |
| const void *addr, |
| const ConstructFunc &construct_func, |
| const permissions &perm) |
| { |
| priv_open_or_create |
| (DoCreate |
| , id |
| , size |
| , mode |
| , addr |
| , perm |
| , construct_func); |
| } |
| |
| template <class ConstructFunc> |
| managed_open_or_create_impl(open_only_t, |
| const device_id_t & id, |
| mode_t mode, |
| const void *addr, |
| const ConstructFunc &construct_func) |
| { |
| priv_open_or_create |
| ( DoOpen |
| , id |
| , 0 |
| , mode |
| , addr |
| , permissions() |
| , construct_func); |
| } |
| |
| template <class ConstructFunc> |
| managed_open_or_create_impl(open_or_create_t, |
| const device_id_t & id, |
| std::size_t size, |
| mode_t mode, |
| const void *addr, |
| const ConstructFunc &construct_func, |
| const permissions &perm) |
| { |
| priv_open_or_create |
| ( DoOpenOrCreate |
| , id |
| , size |
| , mode |
| , addr |
| , perm |
| , construct_func); |
| } |
| |
| managed_open_or_create_impl(BOOST_RV_REF(managed_open_or_create_impl) moved) |
| { this->swap(moved); } |
| |
| managed_open_or_create_impl &operator=(BOOST_RV_REF(managed_open_or_create_impl) moved) |
| { |
| managed_open_or_create_impl tmp(boost::move(moved)); |
| this->swap(tmp); |
| return *this; |
| } |
| |
| ~managed_open_or_create_impl() |
| {} |
| |
| std::size_t get_user_size() const |
| { return m_mapped_region.get_size() - ManagedOpenOrCreateUserOffset; } |
| |
| void *get_user_address() const |
| { return static_cast<char*>(m_mapped_region.get_address()) + ManagedOpenOrCreateUserOffset; } |
| |
| std::size_t get_real_size() const |
| { return m_mapped_region.get_size(); } |
| |
| void *get_real_address() const |
| { return m_mapped_region.get_address(); } |
| |
| void swap(managed_open_or_create_impl &other) |
| { |
| this->m_mapped_region.swap(other.m_mapped_region); |
| } |
| |
| bool flush() |
| { return m_mapped_region.flush(); } |
| |
| const mapped_region &get_mapped_region() const |
| { return m_mapped_region; } |
| |
| |
| DeviceAbstraction &get_device() |
| { return this->DevHolder::get_device(); } |
| |
| const DeviceAbstraction &get_device() const |
| { return this->DevHolder::get_device(); } |
| |
| private: |
| |
| //These are templatized to allow explicit instantiations |
| template<bool dummy> |
| static void truncate_device(DeviceAbstraction &, offset_t, false_) |
| {} //Empty |
| |
| template<bool dummy> |
| static void truncate_device(DeviceAbstraction &dev, offset_t size, true_) |
| { dev.truncate(size); } |
| |
| |
| template<bool dummy> |
| static bool check_offset_t_size(std::size_t , false_) |
| { return true; } //Empty |
| |
| template<bool dummy> |
| static bool check_offset_t_size(std::size_t size, true_) |
| { return size == std::size_t(offset_t(size)); } |
| |
| //These are templatized to allow explicit instantiations |
| template<bool dummy> |
| static void create_device(DeviceAbstraction &dev, const device_id_t & id, std::size_t size, const permissions &perm, false_ file_like) |
| { |
| (void)file_like; |
| DeviceAbstraction tmp(create_only, id, read_write, size, perm); |
| tmp.swap(dev); |
| } |
| |
| template<bool dummy> |
| static void create_device(DeviceAbstraction &dev, const device_id_t & id, std::size_t, const permissions &perm, true_ file_like) |
| { |
| (void)file_like; |
| DeviceAbstraction tmp(create_only, id, read_write, perm); |
| tmp.swap(dev); |
| } |
| |
| template <class ConstructFunc> inline |
| void priv_open_or_create |
| (create_enum_t type, |
| const device_id_t & id, |
| std::size_t size, |
| mode_t mode, const void *addr, |
| const permissions &perm, |
| ConstructFunc construct_func) |
| { |
| typedef bool_<FileBased> file_like_t; |
| (void)mode; |
| bool created = false; |
| bool ronly = false; |
| bool cow = false; |
| DeviceAbstraction dev; |
| |
| if(type != DoOpen){ |
| //Check if the requested size is enough to build the managed metadata |
| const std::size_t func_min_size = construct_func.get_min_size(); |
| if( (std::size_t(-1) - ManagedOpenOrCreateUserOffset) < func_min_size || |
| size < (func_min_size + ManagedOpenOrCreateUserOffset) ){ |
| throw interprocess_exception(error_info(size_error)); |
| } |
| } |
| //Check size can be represented by offset_t (used by truncate) |
| if(type != DoOpen && !check_offset_t_size<FileBased>(size, file_like_t())){ |
| throw interprocess_exception(error_info(size_error)); |
| } |
| if(type == DoOpen && mode == read_write){ |
| DeviceAbstraction tmp(open_only, id, read_write); |
| tmp.swap(dev); |
| created = false; |
| } |
| else if(type == DoOpen && mode == read_only){ |
| DeviceAbstraction tmp(open_only, id, read_only); |
| tmp.swap(dev); |
| created = false; |
| ronly = true; |
| } |
| else if(type == DoOpen && mode == copy_on_write){ |
| DeviceAbstraction tmp(open_only, id, read_only); |
| tmp.swap(dev); |
| created = false; |
| cow = true; |
| } |
| else if(type == DoCreate){ |
| create_device<FileBased>(dev, id, size, perm, file_like_t()); |
| created = true; |
| } |
| else if(type == DoOpenOrCreate){ |
| //This loop is very ugly, but brute force is sometimes better |
| //than diplomacy. If someone knows how to open or create a |
| //file and know if we have really created it or just open it |
| //drop me a e-mail! |
| bool completed = false; |
| spin_wait swait; |
| while(!completed){ |
| try{ |
| create_device<FileBased>(dev, id, size, perm, file_like_t()); |
| created = true; |
| completed = true; |
| } |
| catch(interprocess_exception &ex){ |
| if(ex.get_error_code() != already_exists_error){ |
| throw; |
| } |
| else{ |
| try{ |
| DeviceAbstraction tmp(open_only, id, read_write); |
| dev.swap(tmp); |
| created = false; |
| completed = true; |
| } |
| catch(interprocess_exception &e){ |
| if(e.get_error_code() != not_found_error){ |
| throw; |
| } |
| } |
| catch(...){ |
| throw; |
| } |
| } |
| } |
| catch(...){ |
| throw; |
| } |
| swait.yield(); |
| } |
| } |
| |
| if(created){ |
| try{ |
| //If this throws, we are lost |
| truncate_device<FileBased>(dev, size, file_like_t()); |
| |
| //If the following throws, we will truncate the file to 1 |
| mapped_region region(dev, read_write, 0, 0, addr); |
| boost::uint32_t *patomic_word = 0; //avoid gcc warning |
| patomic_word = static_cast<boost::uint32_t*>(region.get_address()); |
| boost::uint32_t previous = atomic_cas32(patomic_word, InitializingSegment, UninitializedSegment); |
| |
| if(previous == UninitializedSegment){ |
| try{ |
| construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset |
| , size - ManagedOpenOrCreateUserOffset, true); |
| //All ok, just move resources to the external mapped region |
| m_mapped_region.swap(region); |
| } |
| catch(...){ |
| atomic_write32(patomic_word, CorruptedSegment); |
| throw; |
| } |
| atomic_write32(patomic_word, InitializedSegment); |
| } |
| else if(previous == InitializingSegment || previous == InitializedSegment){ |
| throw interprocess_exception(error_info(already_exists_error)); |
| } |
| else{ |
| throw interprocess_exception(error_info(corrupted_error)); |
| } |
| } |
| catch(...){ |
| try{ |
| truncate_device<FileBased>(dev, 1u, file_like_t()); |
| } |
| catch(...){ |
| } |
| throw; |
| } |
| } |
| else{ |
| if(FileBased){ |
| offset_t filesize = 0; |
| spin_wait swait; |
| while(filesize == 0){ |
| if(!get_file_size(file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){ |
| error_info err = system_error_code(); |
| throw interprocess_exception(err); |
| } |
| swait.yield(); |
| } |
| if(filesize == 1){ |
| throw interprocess_exception(error_info(corrupted_error)); |
| } |
| } |
| |
| mapped_region region(dev, ronly ? read_only : (cow ? copy_on_write : read_write), 0, 0, addr); |
| |
| boost::uint32_t *patomic_word = static_cast<boost::uint32_t*>(region.get_address()); |
| boost::uint32_t value = atomic_read32(patomic_word); |
| |
| spin_wait swait; |
| while(value == InitializingSegment || value == UninitializedSegment){ |
| swait.yield(); |
| value = atomic_read32(patomic_word); |
| } |
| |
| if(value != InitializedSegment) |
| throw interprocess_exception(error_info(corrupted_error)); |
| |
| construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset |
| , region.get_size() - ManagedOpenOrCreateUserOffset |
| , false); |
| //All ok, just move resources to the external mapped region |
| m_mapped_region.swap(region); |
| } |
| if(StoreDevice){ |
| this->DevHolder::get_device() = boost::move(dev); |
| } |
| } |
| |
| friend void swap(managed_open_or_create_impl &left, managed_open_or_create_impl &right) |
| { |
| left.swap(right); |
| } |
| |
| private: |
| friend class interprocess_tester; |
| void dont_close_on_destruction() |
| { interprocess_tester::dont_close_on_destruction(m_mapped_region); } |
| |
| mapped_region m_mapped_region; |
| }; |
| |
| } //namespace ipcdetail { |
| |
| } //namespace interprocess { |
| } //namespace boost { |
| |
| #include <boost/interprocess/detail/config_end.hpp> |
| |
| #endif //#ifndef BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL |