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
|