123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- //////////////////////////////////////////////////////////////////////////////
- //
- // (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/anonymous_shared_memory.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/interprocess/timed_utils.hpp>
- #include <boost/move/move.hpp>
- #include <boost/cstdint.hpp>
- namespace boost {
- namespace interprocess {
- 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 bool_<FileBased> file_like_t;
- static const unsigned MaxCreateOrOpenTries = BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_INITIALIZE_MAX_TRIES;
- static const unsigned MaxInitializeTimeSec = BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_INITIALIZE_TIMEOUT_SEC;
- typedef managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction> DevHolder;
- enum
- {
- UninitializedSegment,
- InitializingSegment,
- InitializedSegment,
- CorruptedSegment
- };
- static const std::size_t RequiredAlignment =
- MemAlignment ? MemAlignment
- : boost::container::dtl::alignment_of< boost::container::dtl::max_align_t >::value
- ;
- public:
- static const std::size_t ManagedOpenOrCreateUserOffset =
- ct_rounded_size<sizeof(boost::uint32_t), RequiredAlignment>::value;
- managed_open_or_create_impl()
- {}
- template <class DeviceId>
- managed_open_or_create_impl(create_only_t,
- const DeviceId & 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());
- }
- template <class DeviceId>
- managed_open_or_create_impl(open_only_t,
- const DeviceId & id,
- mode_t mode,
- const void *addr)
- {
- priv_open_or_create
- ( DoOpen
- , id
- , 0
- , mode
- , addr
- , permissions()
- , null_mapped_region_function());
- }
- template <class DeviceId>
- managed_open_or_create_impl(open_or_create_t,
- const DeviceId & 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 DeviceId, class ConstructFunc>
- managed_open_or_create_impl(create_only_t,
- const DeviceId & 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 DeviceId, class ConstructFunc>
- managed_open_or_create_impl(open_only_t,
- const DeviceId & 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 DeviceId, class ConstructFunc>
- managed_open_or_create_impl(open_or_create_t,
- const DeviceId & 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);
- }
- template <class ConstructFunc>
- managed_open_or_create_impl(std::size_t size, void *addr, const ConstructFunc &construct_func)
- {
- priv_map_anonymous
- ( size
- , addr
- , 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, class DeviceId>
- static void create_device(DeviceAbstraction &dev, const DeviceId & id, std::size_t size, const permissions &perm, false_ /*file_like*/)
- {
- DeviceAbstraction tmp(create_only, id, read_write, size, perm);
- tmp.swap(dev);
- }
- template<bool dummy, class DeviceId>
- static void create_device(DeviceAbstraction &dev, const DeviceId & id, std::size_t, const permissions &perm, true_ /*file_like*/)
- {
- DeviceAbstraction tmp(create_only, id, read_write, perm);
- tmp.swap(dev);
- }
- template <class DeviceId>
- static bool do_create_else_open(DeviceAbstraction &dev, const DeviceId & id, std::size_t size, const permissions &perm)
- {
- //This loop is very ugly, but brute force is sometimes better
- //than diplomacy. In POSIX file-based resources we can' know if we
- //effectively created the file or not (there is no ERROR_ALREADY_EXISTS equivalent),
- //so we try to create exclusively and fallback to open if already exists, with
- //some retries if opening also fails because the file does not exist
- //(there is a race, the creator just removed the file after creating it).
- //
- //We'll put a maximum retry limit just to avoid possible deadlocks, we don't
- //want to support pathological use cases.
- spin_wait swait;
- unsigned tries = 0;
- while(1){
- BOOST_TRY{
- create_device<FileBased>(dev, id, size, perm, file_like_t());
- return true;
- }
- BOOST_CATCH(interprocess_exception &ex){
- #ifndef BOOST_NO_EXCEPTIONS
- if(ex.get_error_code() != already_exists_error){
- BOOST_RETHROW
- }
- else if (++tries == MaxCreateOrOpenTries) {
- //File existing when trying to create, but non-existing when
- //trying to open, and tried MaxCreateOrOpenTries times. Something fishy
- //is happening here and we can't solve it
- throw interprocess_exception(error_info(corrupted_error));
- }
- else{
- BOOST_TRY{
- DeviceAbstraction tmp(open_only, id, read_write);
- dev.swap(tmp);
- return false;
- }
- BOOST_CATCH(interprocess_exception &e){
- if(e.get_error_code() != not_found_error){
- BOOST_RETHROW
- }
- }
- BOOST_CATCH(...){
- BOOST_RETHROW
- } BOOST_CATCH_END
- }
- #endif //#ifndef BOOST_NO_EXCEPTIONS
- }
- BOOST_CATCH(...){
- BOOST_RETHROW
- } BOOST_CATCH_END
- swait.yield();
- }
- return false;
- }
- template <class ConstructFunc>
- static void do_map_after_create
- (DeviceAbstraction &dev, mapped_region &final_region,
- std::size_t size, const void *addr, ConstructFunc construct_func)
- {
- BOOST_TRY{
- //If this throws, we are lost
- truncate_device<FileBased>(dev, static_cast<offset_t>(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){
- BOOST_TRY{
- construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset
- , size - ManagedOpenOrCreateUserOffset, true);
- //All ok, just move resources to the external mapped region
- final_region.swap(region);
- }
- BOOST_CATCH(...){
- atomic_write32(patomic_word, CorruptedSegment);
- BOOST_RETHROW
- } BOOST_CATCH_END
- atomic_write32(patomic_word, InitializedSegment);
- }
- else{
- atomic_write32(patomic_word, CorruptedSegment);
- throw interprocess_exception(error_info(corrupted_error));
- }
- }
- BOOST_CATCH(...){
- BOOST_TRY{
- truncate_device<FileBased>(dev, 1u, file_like_t());
- }
- BOOST_CATCH(...){
- }
- BOOST_CATCH_END
- BOOST_RETHROW
- }
- BOOST_CATCH_END
- }
- template <class ConstructFunc>
- static void do_map_after_open
- ( DeviceAbstraction &dev, mapped_region &final_region
- , const void *addr, ConstructFunc construct_func
- , bool ronly, bool cow)
- {
- const usduration TimeoutSec(usduration_from_seconds(MaxInitializeTimeSec));
- if(FileBased){
- offset_t filesize = 0;
- spin_wait swait;
- //If a file device was used, the creator might be truncating the device, so wait
- //until the file size is enough to map the initial word
- ustime ustime_start = microsec_clock<ustime>::universal_time();
- while(1){
- if(!get_file_size(file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){
- error_info err = system_error_code();
- throw interprocess_exception(err);
- }
- if (filesize != 0)
- break;
- else {
- //More than MaxZeroTruncateTimeSec seconds waiting to the creator
- //to minimally increase the size of the file: something bad has happened
- const usduration elapsed(microsec_clock<ustime>::universal_time() - ustime_start);
- if (elapsed > TimeoutSec){
- throw interprocess_exception(error_info(corrupted_error));
- }
- swait.yield();
- }
- }
- //The creator detected an error creating the file and signalled it with size 1
- 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);
- if (value != InitializedSegment){
- ustime ustime_start = microsec_clock<ustime>::universal_time();
- spin_wait swait;
- while ((value = atomic_read32(patomic_word)) != InitializedSegment){
- if(value == CorruptedSegment){
- throw interprocess_exception(error_info(corrupted_error));
- }
- //More than MaxZeroTruncateTimeSec seconds waiting to the creator
- //to minimally increase the size of the file: something bad has happened
- const usduration elapsed(microsec_clock<ustime>::universal_time() - ustime_start);
- if (elapsed > TimeoutSec){
- throw interprocess_exception(error_info(corrupted_error));
- }
- swait.yield();
- }
- //The size of the file might have grown while Uninitialized -> Initializing, so remap
- {
- mapped_region null_map;
- region.swap(null_map);
- }
- mapped_region final_size_map(dev, ronly ? read_only : (cow ? copy_on_write : read_write), 0, 0, addr);
- final_size_map.swap(region);
- }
- construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset
- , region.get_size() - ManagedOpenOrCreateUserOffset
- , false);
- //All ok, just move resources to the external mapped region
- final_region.swap(region);
- }
- template <class DeviceId, class ConstructFunc> inline
- void priv_open_or_create
- (create_enum_t type,
- const DeviceId & id,
- std::size_t size,
- mode_t mode, const void *addr,
- const permissions &perm,
- ConstructFunc construct_func)
- {
- 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 (!check_offset_t_size<FileBased>(size, file_like_t())){
- throw interprocess_exception(error_info(size_error));
- }
- }
- //Now create the device (file, shm file, etc.)
- DeviceAbstraction dev;
- (void)mode;
- bool created = false;
- bool ronly = false;
- bool cow = false;
- if(type == DoOpen){
- DeviceAbstraction tmp(open_only, id, mode == read_write ? read_write : read_only);
- tmp.swap(dev);
- ronly = mode == read_only;
- cow = mode == copy_on_write;
- }
- else if(type == DoCreate){
- create_device<FileBased>(dev, id, size, perm, file_like_t());
- created = true;
- }
- else { //DoOpenOrCreate
- created = this->do_create_else_open(dev, id, size, perm);
- }
- if(created){
- this->do_map_after_create(dev, m_mapped_region, size, addr, construct_func);
- }
- else{
- this->do_map_after_open(dev, m_mapped_region, addr, construct_func, ronly, cow);
- }
- if(StoreDevice){
- this->DevHolder::get_device() = boost::move(dev);
- }
- }
- template <class ConstructFunc> inline
- void priv_map_anonymous
- (std::size_t size,
- void *addr,
- ConstructFunc construct_func)
- {
- mapped_region region = anonymous_shared_memory(size, 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){
- BOOST_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);
- }
- BOOST_CATCH(...){
- atomic_write32(patomic_word, CorruptedSegment);
- BOOST_RETHROW
- } BOOST_CATCH_END
- atomic_write32(patomic_word, InitializedSegment);
- }
- else{
- atomic_write32(patomic_word, CorruptedSegment);
- throw interprocess_exception(error_info(corrupted_error));
- }
- }
- 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
|