blob: 712b308ba159990538afa1e67b310167aa7c3db2 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2006. 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
#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/detail/move.hpp>
#include <boost/interprocess/permissions.hpp>
#include <boost/cstdint.hpp>
namespace boost {
namespace interprocess {
/// @cond
namespace detail{ 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
/// @endcond
namespace detail {
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, bool FileBased = true, bool StoreDevice = true>
class managed_open_or_create_impl
: public managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction>
{
//Non-copyable
BOOST_INTERPROCESS_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 =
detail::ct_rounded_size
< sizeof(boost::uint32_t)
, detail::alignment_of<detail::max_align>::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
( detail::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
( detail::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
( detail::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
(detail::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
( detail::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
( detail::DoOpenOrCreate
, id
, size
, mode
, addr
, perm
, construct_func);
}
managed_open_or_create_impl(BOOST_INTERPROCESS_RV_REF(managed_open_or_create_impl) moved)
{ this->swap(moved); }
managed_open_or_create_impl &operator=(BOOST_INTERPROCESS_RV_REF(managed_open_or_create_impl) moved)
{
managed_open_or_create_impl tmp(boost::interprocess::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 &, std::size_t, detail::false_)
{} //Empty
template<bool dummy>
static void truncate_device(DeviceAbstraction &dev, std::size_t size, detail::true_)
{ dev.truncate(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, detail::false_)
{
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, detail::true_)
{
DeviceAbstraction tmp(create_only, id, read_write, perm);
tmp.swap(dev);
}
template <class ConstructFunc> inline
void priv_open_or_create
(detail::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 detail::bool_<FileBased> file_like_t;
(void)mode;
error_info err;
bool created = false;
bool ronly = false;
bool cow = false;
DeviceAbstraction dev;
if(type != detail::DoOpen && size < ManagedOpenOrCreateUserOffset){
throw interprocess_exception(error_info(size_error));
}
if(type == detail::DoOpen && mode == read_write){
DeviceAbstraction tmp(open_only, id, read_write);
tmp.swap(dev);
created = false;
}
else if(type == detail::DoOpen && mode == read_only){
DeviceAbstraction tmp(open_only, id, read_only);
tmp.swap(dev);
created = false;
ronly = true;
}
else if(type == detail::DoOpen && mode == copy_on_write){
DeviceAbstraction tmp(open_only, id, read_only);
tmp.swap(dev);
created = false;
cow = true;
}
else if(type == detail::DoCreate){
create_device<FileBased>(dev, id, size, perm, file_like_t());
created = true;
}
else if(type == detail::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;
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 &ex){
if(ex.get_error_code() != not_found_error){
throw;
}
}
}
}
detail::thread_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 = detail::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(...){
detail::atomic_write32(patomic_word, CorruptedSegment);
throw;
}
detail::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;
while(filesize == 0){
if(!detail::get_file_size(detail::file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){
throw interprocess_exception(error_info(system_error_code()));
}
detail::thread_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 = detail::atomic_read32(patomic_word);
while(value == InitializingSegment || value == UninitializedSegment){
detail::thread_yield();
value = detail::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::interprocess::move(dev);
}
}
private:
friend class detail::interprocess_tester;
void dont_close_on_destruction()
{ detail::interprocess_tester::dont_close_on_destruction(m_mapped_region); }
mapped_region m_mapped_region;
};
template<class DeviceAbstraction>
inline void swap(managed_open_or_create_impl<DeviceAbstraction> &x
,managed_open_or_create_impl<DeviceAbstraction> &y)
{ x.swap(y); }
} //namespace detail {
} //namespace interprocess {
} //namespace boost {
#endif //#ifndef BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL