502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| //////////////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| // (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
 | 
