boost/interprocess/segment_manager.hpp
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-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_SEGMENT_MANAGER_HPP
#define BOOST_INTERPROCESS_SEGMENT_MANAGER_HPP
#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/workaround.hpp>
#include <boost/interprocess/detail/type_traits.hpp>
#include <boost/interprocess/detail/transform_iterator.hpp>
#include <boost/interprocess/detail/mpl.hpp>
#include <boost/interprocess/detail/nothrow.hpp>
#include <boost/interprocess/detail/segment_manager_helper.hpp>
#include <boost/interprocess/detail/named_proxy.hpp>
#include <boost/interprocess/detail/utilities.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/indexes/iset_index.hpp>
#include <boost/interprocess/exceptions.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/smart_ptr/deleter.hpp>
#include <boost/move/utility_core.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
// container/detail
#include <boost/container/detail/minimal_char_traits_header.hpp>
#include <boost/container/detail/placement_new.hpp>
// std
#include <cstddef> //std::size_t
#include <boost/intrusive/detail/minimal_pair_header.hpp>
#include <boost/assert.hpp>
#ifndef BOOST_NO_EXCEPTIONS
#include <exception>
#endif
#include <typeinfo>
//!\file
//!Describes the object placed in a memory segment that provides
//!named object allocation capabilities for single-segment and
//!multi-segment allocations.
namespace boost{
namespace interprocess{
//!This object is the public base class of segment manager.
//!This class only depends on the memory allocation algorithm
//!and implements all the allocation features not related
//!to named or unique objects.
//!
//!Storing a reference to segment_manager forces
//!the holder class to be dependent on index types and character types.
//!When such dependence is not desirable and only anonymous and raw
//!allocations are needed, segment_manager_base is the correct answer.
template<class MemoryAlgorithm>
class segment_manager_base
: private MemoryAlgorithm
{
public:
typedef segment_manager_base<MemoryAlgorithm> segment_manager_base_type;
typedef typename MemoryAlgorithm::void_pointer void_pointer;
typedef typename MemoryAlgorithm::mutex_family mutex_family;
static const std::size_t MemAlignment = MemoryAlgorithm::Alignment;
typedef MemoryAlgorithm memory_algorithm;
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
//Experimental. Don't use
typedef typename MemoryAlgorithm::multiallocation_chain multiallocation_chain;
typedef typename MemoryAlgorithm::difference_type difference_type;
typedef typename MemoryAlgorithm::size_type size_type;
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
//!This constant indicates the payload size
//!associated with each allocation of the memory algorithm
static const size_type PayloadPerAllocation = MemoryAlgorithm::PayloadPerAllocation;
//!Constructor of the segment_manager_base
//!
//!"size" is the size of the memory segment where
//!the basic segment manager is being constructed.
//!
//!"reserved_bytes" is the number of bytes
//!after the end of the memory algorithm object itself
//!that the memory algorithm will exclude from
//!dynamic allocation
//!
//!Can throw
segment_manager_base(size_type sz, size_type reserved_bytes)
: MemoryAlgorithm(sz, reserved_bytes)
{
BOOST_ASSERT((sizeof(segment_manager_base<MemoryAlgorithm>) == sizeof(MemoryAlgorithm)));
}
//!Returns the size of the memory
//!segment
size_type get_size() const
{ return MemoryAlgorithm::get_size(); }
//!Returns the number of free bytes of the memory
//!segment
size_type get_free_memory() const
{ return MemoryAlgorithm::get_free_memory(); }
//!Obtains the minimum size needed by
//!the segment manager
static size_type get_min_size (size_type size)
{ return MemoryAlgorithm::get_min_size(size); }
//!Allocates nbytes bytes. This function is only used in
//!single-segment management. Never throws
void * allocate (size_type nbytes, const std::nothrow_t &)
{ return MemoryAlgorithm::allocate(nbytes); }
//!Returns a reference to the internal memory algorithm.
//!This function is useful for custom memory algorithms that
//!need additional configuration options after construction. Never throws.
//!This function should be only used by advanced users.
MemoryAlgorithm &get_memory_algorithm()
{ return static_cast<MemoryAlgorithm&>(*this); }
//!Returns a const reference to the internal memory algorithm.
//!This function is useful for custom memory algorithms that
//!need additional configuration options after construction. Never throws.
//!This function should be only used by advanced users.
const MemoryAlgorithm &get_memory_algorithm() const
{ return static_cast<const MemoryAlgorithm&>(*this); }
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
//Experimental. Dont' use.
//!Allocates n_elements of elem_bytes bytes.
//!Throws bad_alloc on failure. chain.size() is not increased on failure.
void allocate_many(size_type elem_bytes, size_type n_elements, multiallocation_chain &chain)
{
size_type prev_size = chain.size();
MemoryAlgorithm::allocate_many(elem_bytes, n_elements, chain);
if(!elem_bytes || chain.size() == prev_size){
throw bad_alloc();
}
}
//!Allocates n_elements, each one of element_lengths[i]*sizeof_element bytes.
//!Throws bad_alloc on failure. chain.size() is not increased on failure.
void allocate_many(const size_type *element_lengths, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain)
{
size_type prev_size = chain.size();
MemoryAlgorithm::allocate_many(element_lengths, n_elements, sizeof_element, chain);
if(!sizeof_element || chain.size() == prev_size){
throw bad_alloc();
}
}
//!Allocates n_elements of elem_bytes bytes.
//!Non-throwing version. chain.size() is not increased on failure.
void allocate_many(const std::nothrow_t &, size_type elem_bytes, size_type n_elements, multiallocation_chain &chain)
{ MemoryAlgorithm::allocate_many(elem_bytes, n_elements, chain); }
//!Allocates n_elements, each one of
//!element_lengths[i]*sizeof_element bytes.
//!Non-throwing version. chain.size() is not increased on failure.
void allocate_many(const std::nothrow_t &, const size_type *elem_sizes, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain)
{ MemoryAlgorithm::allocate_many(elem_sizes, n_elements, sizeof_element, chain); }
//!Deallocates all elements contained in chain.
//!Never throws.
void deallocate_many(multiallocation_chain &chain)
{ MemoryAlgorithm::deallocate_many(chain); }
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
//!Allocates nbytes bytes. Throws boost::interprocess::bad_alloc
//!on failure
void * allocate(size_type nbytes)
{
void * ret = MemoryAlgorithm::allocate(nbytes);
if(!ret)
throw bad_alloc();
return ret;
}
//!Allocates nbytes bytes. This function is only used in
//!single-segment management. Never throws
void * allocate_aligned (size_type nbytes, size_type alignment, const std::nothrow_t &)
{ return MemoryAlgorithm::allocate_aligned(nbytes, alignment); }
//!Allocates nbytes bytes. This function is only used in
//!single-segment management. Throws bad_alloc when fails
void * allocate_aligned(size_type nbytes, size_type alignment)
{
void * ret = MemoryAlgorithm::allocate_aligned(nbytes, alignment);
if(!ret)
throw bad_alloc();
return ret;
}
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
template<class T>
T *allocation_command (boost::interprocess::allocation_type command, size_type limit_size,
size_type &prefer_in_recvd_out_size, T *&reuse)
{
T *ret = MemoryAlgorithm::allocation_command
(command | boost::interprocess::nothrow_allocation, limit_size, prefer_in_recvd_out_size, reuse);
if(!(command & boost::interprocess::nothrow_allocation) && !ret)
throw bad_alloc();
return ret;
}
void *raw_allocation_command (boost::interprocess::allocation_type command, size_type limit_objects,
size_type &prefer_in_recvd_out_size, void *&reuse, size_type sizeof_object = 1)
{
void *ret = MemoryAlgorithm::raw_allocation_command
( command | boost::interprocess::nothrow_allocation, limit_objects,
prefer_in_recvd_out_size, reuse, sizeof_object);
if(!(command & boost::interprocess::nothrow_allocation) && !ret)
throw bad_alloc();
return ret;
}
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
//!Deallocates the bytes allocated with allocate/allocate_many()
//!pointed by addr
void deallocate (void *addr)
{ MemoryAlgorithm::deallocate(addr); }
//!Increases managed memory in extra_size bytes more. This only works
//!with single-segment management.
void grow(size_type extra_size)
{
//Growing managed segments that use raw pointers is UB, so disallow it.
BOOST_INTERPROCESS_STATIC_ASSERT(!(ipcdetail::is_same<void*, void_pointer>::value));
MemoryAlgorithm::grow(extra_size);
}
//!Decreases managed memory to the minimum. This only works
//!with single-segment management.
void shrink_to_fit()
{ MemoryAlgorithm::shrink_to_fit(); }
//!Returns the result of "all_memory_deallocated()" function
//!of the used memory algorithm
bool all_memory_deallocated()
{ return MemoryAlgorithm::all_memory_deallocated(); }
//!Returns the result of "check_sanity()" function
//!of the used memory algorithm
bool check_sanity()
{ return MemoryAlgorithm::check_sanity(); }
//!Writes to zero free memory (memory not yet allocated)
//!of the memory algorithm
void zero_free_memory()
{ MemoryAlgorithm::zero_free_memory(); }
//!Returns the size of the buffer previously allocated pointed by ptr
size_type size(const void *ptr) const
{ return MemoryAlgorithm::size(ptr); }
};
//!This object is placed in the beginning of memory segment and
//!implements the allocation (named or anonymous) of portions
//!of the segment. This object contains two indexes that
//!maintain an association between a name and a portion of the segment.
//!
//!The first index contains the mappings for normal named objects using the
//!char type specified in the template parameter.
//!
//!The second index contains the association for unique instances. The key will
//!be the const char * returned from type_info.name() function for the unique
//!type to be constructed.
//!
//!segment_manager<CharType, MemoryAlgorithm, IndexType> inherits publicly
//!from segment_manager_base<MemoryAlgorithm> and inherits from it
//!many public functions related to anonymous object and raw memory allocation.
//!See segment_manager_base reference to know about those functions.
template<class CharType
,class MemoryAlgorithm
,template<class IndexConfig> class IndexType>
class segment_manager
: public segment_manager_base<MemoryAlgorithm>
{
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
//Non-copyable
segment_manager();
segment_manager(const segment_manager &);
segment_manager &operator=(const segment_manager &);
typedef segment_manager_base<MemoryAlgorithm> segment_manager_base_t;
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
public:
typedef MemoryAlgorithm memory_algorithm;
typedef typename segment_manager_base_t::void_pointer void_pointer;
typedef typename segment_manager_base_t::size_type size_type;
typedef typename segment_manager_base_t::difference_type difference_type;
typedef CharType char_type;
typedef segment_manager_base<MemoryAlgorithm> segment_manager_base_type;
static const size_type PayloadPerAllocation = segment_manager_base_t::PayloadPerAllocation;
static const size_type MemAlignment = segment_manager_base_t::MemAlignment;
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
private:
typedef ipcdetail::block_header<size_type> block_header_t;
typedef ipcdetail::index_config<CharType, MemoryAlgorithm> index_config_named;
typedef ipcdetail::index_config<char, MemoryAlgorithm> index_config_unique;
typedef IndexType<index_config_named> index_type;
typedef ipcdetail::bool_<is_intrusive_index<index_type>::value > is_intrusive_t;
typedef ipcdetail::bool_<is_node_index<index_type>::value> is_node_index_t;
public:
typedef IndexType<index_config_named> named_index_t;
typedef IndexType<index_config_unique> unique_index_t;
typedef ipcdetail::char_ptr_holder<CharType> char_ptr_holder_t;
typedef ipcdetail::segment_manager_iterator_transform
<typename named_index_t::const_iterator
,is_intrusive_index<index_type>::value> named_transform;
typedef ipcdetail::segment_manager_iterator_transform
<typename unique_index_t::const_iterator
,is_intrusive_index<index_type>::value> unique_transform;
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
typedef typename segment_manager_base_t::mutex_family mutex_family;
typedef transform_iterator
<typename named_index_t::const_iterator, named_transform> const_named_iterator;
typedef transform_iterator
<typename unique_index_t::const_iterator, unique_transform> const_unique_iterator;
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
//!Constructor proxy object definition helper class
template<class T>
struct construct_proxy
{
typedef ipcdetail::named_proxy<segment_manager, T, false> type;
};
//!Constructor proxy object definition helper class
template<class T>
struct construct_iter_proxy
{
typedef ipcdetail::named_proxy<segment_manager, T, true> type;
};
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
//!Constructor of the segment manager
//!"size" is the size of the memory segment where
//!the segment manager is being constructed.
//!Can throw
explicit segment_manager(size_type segment_size)
: segment_manager_base_t(segment_size, priv_get_reserved_bytes())
, m_header(static_cast<segment_manager_base_t*>(get_this_pointer()))
{
(void) anonymous_instance; (void) unique_instance;
//Check EBO is applied, it's required
const void * const this_addr = this;
const void *const segm_addr = static_cast<segment_manager_base_t*>(this);
(void)this_addr; (void)segm_addr;
BOOST_ASSERT( this_addr == segm_addr);
const std::size_t void_ptr_alignment = boost::move_detail::alignment_of<void_pointer>::value; (void)void_ptr_alignment;
BOOST_ASSERT((0 == (std::size_t)this_addr % boost::move_detail::alignment_of<segment_manager>::value));
}
//!Tries to find a previous named/unique allocation. Returns the address
//!and the object count. On failure the first member of the
//!returned pair is 0.
template <class T>
std::pair<T*, size_type> find (char_ptr_holder_t name)
{ return this->priv_find_impl<T>(name, true); }
//!Tries to find a previous named/unique allocation. Returns the address
//!and the object count. On failure the first member of the
//!returned pair is 0. This search is not mutex-protected!
//!Use it only inside atomic_func() calls, where the internal mutex
//!is guaranteed to be locked.
template <class T>
std::pair<T*, size_type> find_no_lock (char_ptr_holder_t name)
{ return this->priv_find_impl<T>(name, false); }
//!Returns throwing "construct" proxy
//!object
template <class T>
typename construct_proxy<T>::type
construct(char_ptr_holder_t name)
{ return typename construct_proxy<T>::type (this, name, false, true); }
//!Returns throwing "search or construct" proxy
//!object
template <class T>
typename construct_proxy<T>::type find_or_construct(char_ptr_holder_t name)
{ return typename construct_proxy<T>::type (this, name, true, true); }
//!Returns no throwing "construct" proxy
//!object
template <class T>
typename construct_proxy<T>::type
construct(char_ptr_holder_t name, const std::nothrow_t &)
{ return typename construct_proxy<T>::type (this, name, false, false); }
//!Returns no throwing "search or construct"
//!proxy object
template <class T>
typename construct_proxy<T>::type
find_or_construct(char_ptr_holder_t name, const std::nothrow_t &)
{ return typename construct_proxy<T>::type (this, name, true, false); }
//!Returns throwing "construct from iterators" proxy object
template <class T>
typename construct_iter_proxy<T>::type
construct_it(char_ptr_holder_t name)
{ return typename construct_iter_proxy<T>::type (this, name, false, true); }
//!Returns throwing "search or construct from iterators"
//!proxy object
template <class T>
typename construct_iter_proxy<T>::type
find_or_construct_it(char_ptr_holder_t name)
{ return typename construct_iter_proxy<T>::type (this, name, true, true); }
//!Returns no throwing "construct from iterators"
//!proxy object
template <class T>
typename construct_iter_proxy<T>::type
construct_it(char_ptr_holder_t name, const std::nothrow_t &)
{ return typename construct_iter_proxy<T>::type (this, name, false, false); }
//!Returns no throwing "search or construct from iterators"
//!proxy object
template <class T>
typename construct_iter_proxy<T>::type
find_or_construct_it(char_ptr_holder_t name, const std::nothrow_t &)
{ return typename construct_iter_proxy<T>::type (this, name, true, false); }
//!Calls object function blocking recursive interprocess_mutex and guarantees that
//!no new named_alloc or destroy will be executed by any process while
//!executing the object function call
template <class Func>
void atomic_func(Func &f)
{ scoped_lock<rmutex> guard(m_header); f(); }
//!Tries to calls a functor guaranteeing that no new construction, search or
//!destruction will be executed by any process while executing the object
//!function call. If the atomic function can't be immediatelly executed
//!because the internal mutex is already locked, returns false.
//!If the functor throws, this function throws.
template <class Func>
bool try_atomic_func(Func &f)
{
scoped_lock<rmutex> guard(m_header, try_to_lock);
if(guard){
f();
return true;
}
else{
return false;
}
}
//!Destroys a previously created named/unique instance.
//!Returns false if the object was not present.
template <class T>
bool destroy(char_ptr_holder_t name)
{
BOOST_ASSERT(!name.is_anonymous());
if(name.is_unique()){
return this->priv_generic_named_destroy<T, char>(typeid(T).name(), m_header.m_unique_index);
}
else{
return this->priv_generic_named_destroy<T, CharType>(name.get(), m_header.m_named_index);
}
}
//!Destroys an anonymous, unique or named object
//!using its address
template <class T>
void destroy_ptr(const T *p)
{
priv_destroy_ptr(p);
}
//!Returns the name of an object created with construct/find_or_construct
//!functions. Does not throw
template<class T>
static const CharType *get_instance_name(const T *ptr)
{ return priv_get_instance_name(block_header_t::block_header_from_value(ptr)); }
//!Returns the length of an object created with construct/find_or_construct
//!functions. Does not throw.
template<class T>
static size_type get_instance_length(const T *ptr)
{ return priv_get_instance_length(block_header_t::block_header_from_value(ptr), sizeof(T)); }
//!Returns is the the name of an object created with construct/find_or_construct
//!functions. Does not throw
template<class T>
static instance_type get_instance_type(const T *ptr)
{ return priv_get_instance_type(block_header_t::block_header_from_value(ptr)); }
//!Preallocates needed index resources to optimize the
//!creation of "num" named objects in the managed memory segment.
//!Can throw boost::interprocess::bad_alloc if there is no enough memory.
void reserve_named_objects(size_type num)
{
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
m_header.m_named_index.reserve(num);
}
//!Preallocates needed index resources to optimize the
//!creation of "num" unique objects in the managed memory segment.
//!Can throw boost::interprocess::bad_alloc if there is no enough memory.
void reserve_unique_objects(size_type num)
{
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
m_header.m_unique_index.reserve(num);
}
//!Calls shrink_to_fit in both named and unique object indexes
//!to try to free unused memory from those indexes.
void shrink_to_fit_indexes()
{
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
m_header.m_named_index.shrink_to_fit();
m_header.m_unique_index.shrink_to_fit();
}
//!Returns the number of named objects stored in
//!the segment.
size_type get_num_named_objects()
{
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
return m_header.m_named_index.size();
}
//!Returns the number of unique objects stored in
//!the segment.
size_type get_num_unique_objects()
{
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
return m_header.m_unique_index.size();
}
//!Obtains the minimum size needed by the
//!segment manager
static size_type get_min_size()
{ return segment_manager_base_t::get_min_size(priv_get_reserved_bytes()); }
//!Returns a constant iterator to the beginning of the information about
//!the named allocations performed in this segment manager
const_named_iterator named_begin() const
{
return (make_transform_iterator)
(m_header.m_named_index.begin(), named_transform());
}
//!Returns a constant iterator to the end of the information about
//!the named allocations performed in this segment manager
const_named_iterator named_end() const
{
return (make_transform_iterator)
(m_header.m_named_index.end(), named_transform());
}
//!Returns a constant iterator to the beginning of the information about
//!the unique allocations performed in this segment manager
const_unique_iterator unique_begin() const
{
return (make_transform_iterator)
(m_header.m_unique_index.begin(), unique_transform());
}
//!Returns a constant iterator to the end of the information about
//!the unique allocations performed in this segment manager
const_unique_iterator unique_end() const
{
return (make_transform_iterator)
(m_header.m_unique_index.end(), unique_transform());
}
//!This is the default allocator to allocate types T
//!from this managed segment
template<class T>
struct allocator
{
typedef boost::interprocess::allocator<T, segment_manager> type;
};
//!Returns an instance of the default allocator for type T
//!initialized that allocates memory from this segment manager.
template<class T>
typename allocator<T>::type
get_allocator()
{ return typename allocator<T>::type(this); }
//!This is the default deleter to delete types T
//!from this managed segment.
template<class T>
struct deleter
{
typedef boost::interprocess::deleter<T, segment_manager> type;
};
//!Returns an instance of the default deleter for type T
//!that will delete an object constructed in this segment manager.
template<class T>
typename deleter<T>::type
get_deleter()
{ return typename deleter<T>::type(this); }
#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
//!Generic named/anonymous new function. Offers all the possibilities,
//!such as throwing, search before creating, and the constructor is
//!encapsulated in an object function.
template<class Proxy>
typename Proxy::object_type * generic_construct
(Proxy& pr, const CharType *name, size_type num, bool try2find, bool dothrow)
{
typedef typename Proxy::object_type object_type;
//Security overflow check
if(num > ((size_type)-1)/sizeof(object_type)){
return ipcdetail::null_or_bad_alloc<object_type>(dothrow);
}
if(name == 0){
return this->priv_anonymous_construct(pr, num, dothrow);
}
else if(name == reinterpret_cast<const CharType*>(-1)){
return this->priv_generic_named_construct
(pr, unique_type, typeid(object_type).name(), num, try2find, dothrow, m_header.m_unique_index);
}
else{
return this->priv_generic_named_construct
(pr, named_type, name, num, try2find, dothrow, m_header.m_named_index);
}
}
private:
//!Tries to find a previous named allocation. Returns the address
//!and the object count. On failure the first member of the
//!returned pair is 0.
template <class T>
std::pair<T*, size_type> priv_find_impl (const CharType* name, bool lock)
{
//The name can't be null, no anonymous object can be found by name
BOOST_ASSERT(name != 0);
size_type sz;
void *ret;
if(name == reinterpret_cast<const CharType*>(-1)){
ret = priv_generic_find<T>(typeid(T).name(), m_header.m_unique_index, sz, lock);
}
else{
ret = priv_generic_find<T>(name, m_header.m_named_index, sz, lock);
}
return std::pair<T*, size_type>(static_cast<T*>(ret), sz);
}
//!Tries to find a previous unique allocation. Returns the address
//!and the object count. On failure the first member of the
//!returned pair is 0.
template <class T>
std::pair<T*, size_type> priv_find_impl (const ipcdetail::unique_instance_t*, bool lock)
{
size_type size;
void *ret = priv_generic_find<T>(typeid(T).name(), m_header.m_unique_index, size, lock);
return std::pair<T*, size_type>(static_cast<T*>(ret), size);
}
template<class Proxy>
typename Proxy::object_type * priv_anonymous_construct(Proxy pr, size_type num, bool dothrow)
{
typedef typename Proxy::object_type object_type;
BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = boost::move_detail::alignment_of<object_type>::value;
block_header_t block_info ( size_type(sizeof(object_type)*num)
, size_type(t_alignment)
, anonymous_type
, 1
, 0);
//Check if there is enough memory
const std::size_t total_size = block_info.template total_anonymous_size<t_alignment>();
#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2)
void *ptr_struct = this->allocate(total_size, nothrow<>::get());
#else
void* ptr_struct = this->allocate_aligned(total_size, t_alignment, nothrow<>::get());
#endif
if(!ptr_struct){
return ipcdetail::null_or_bad_alloc<object_type>(dothrow);
}
//Build scoped ptr to avoid leaks with constructor exception
ipcdetail::mem_algo_deallocator<segment_manager_base_type> mem
(ptr_struct, *static_cast<segment_manager_base_type*>(this));
//Now construct the header
const std::size_t front_space = block_header_t::template front_space_without_header<t_alignment>();
block_header_t * const hdr = ::new((char*)ptr_struct + front_space, boost_container_new_t()) block_header_t(block_info);
BOOST_ASSERT(is_ptr_aligned(hdr));
void *ptr = 0; //avoid gcc warning
ptr = hdr->value();
//Now call constructors
pr.construct_n(ptr, num);
//All constructors successful, disable rollback
mem.release();
object_type* const pret = static_cast<object_type*>(ptr);
BOOST_ASSERT(is_ptr_aligned(pret));
return pret;
}
//!Calls the destructor and makes an anonymous deallocate
template <class T>
void priv_anonymous_destroy(const T *object)
{
BOOST_ASSERT(is_ptr_aligned(object));
//Get control data from associated with this object
block_header_t *ctrl_data = block_header_t::block_header_from_value(object);
//-------------------------------
//scoped_lock<rmutex> guard(m_header);
//-------------------------------
//This is not an anonymous object, the pointer is wrong!
BOOST_ASSERT(ctrl_data->alloc_type() == anonymous_type);
//Call destructors and free memory
//Build scoped ptr to avoid leaks with destructor exception
priv_destroy_n(object, ctrl_data->value_bytes()/sizeof(T));
BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment =
boost::move_detail::alignment_of<T>::value;
const std::size_t front_space = block_header_t::template front_space_without_header<t_alignment>();
this->deallocate((char*)ctrl_data - front_space);
}
template<class T>
void priv_destroy_ptr(const T *ptr)
{
BOOST_ASSERT(is_ptr_aligned(ptr));
block_header_t *ctrl_data = block_header_t::block_header_from_value(ptr);
switch(ctrl_data->alloc_type()){
case anonymous_type:
this->priv_anonymous_destroy(ptr);
break;
case named_type:
this->priv_generic_named_destroy<T, CharType>
(ctrl_data, m_header.m_named_index, is_node_index_t());
break;
case unique_type:
this->priv_generic_named_destroy<T, char>
(ctrl_data, m_header.m_unique_index, is_node_index_t());
break;
default:
//This type is unknown, bad pointer passed to this function!
BOOST_ASSERT(0);
break;
}
}
template<class T>
static void priv_destroy_n(T* memory, std::size_t num)
{
for (std::size_t destroyed = 0; destroyed < num; ++destroyed)
(memory++)->~T();
}
//!Returns the name of an object created with construct/find_or_construct
//!functions. Does not throw
static const CharType *priv_get_instance_name(block_header_t *ctrl_data)
{
boost::interprocess::allocation_type type = ctrl_data->alloc_type();
if(type == anonymous_type){
BOOST_ASSERT(ctrl_data->name_length() == 0);
return 0;
}
BOOST_ASSERT(ctrl_data->name_length() != 0);
CharType *name = static_cast<CharType*>(ctrl_data->template name<CharType>());
//Sanity checks
BOOST_ASSERT(ctrl_data->name_length() == std::char_traits<CharType>::length(name));
return name;
}
static size_type priv_get_instance_length(block_header_t *ctrl_data, size_type sizeofvalue)
{
//Get header
BOOST_ASSERT((ctrl_data->value_bytes() %sizeofvalue) == 0);
return ctrl_data->value_bytes()/sizeofvalue;
}
//!Returns is the the name of an object created with construct/find_or_construct
//!functions. Does not throw
static instance_type priv_get_instance_type(block_header_t *ctrl_data)
{
//Get header
BOOST_ASSERT((instance_type)ctrl_data->alloc_type() < max_allocation_type);
return (instance_type)ctrl_data->alloc_type();
}
static size_type priv_get_reserved_bytes()
{
//Get the number of bytes until the end of (*this)
//beginning in the end of the segment_manager_base_t base.
return sizeof(segment_manager) - sizeof(segment_manager_base_t);
}
template <class T, class CharT>
T *priv_generic_find
(const CharT* name,
IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index,
size_type &length, bool use_lock)
{
typedef IndexType<ipcdetail::index_config
<CharT, MemoryAlgorithm> > index_t;
typedef typename index_t::iterator index_it;
typedef typename index_t::compare_key_type compare_key_t;
//-------------------------------
scoped_lock<rmutex> guard(priv_get_lock(use_lock));
//-------------------------------
//Find name in index
compare_key_t key (name, std::char_traits<CharT>::length(name));
index_it it = index.find(key);
//Initialize return values
void *ret_ptr = 0;
length = 0;
//If found, assign values
if(it != index.end()){
//Get header
block_header_t *ctrl_data = priv_block_header_from_it(it, is_intrusive_t());
//Sanity check
BOOST_ASSERT((ctrl_data->value_bytes() % sizeof(T)) == 0);
ret_ptr = ctrl_data->value();
length = ctrl_data->value_bytes()/ sizeof(T);
}
return static_cast<T*>(ret_ptr);
}
template <class T, class CharT>
bool priv_generic_named_destroy
(block_header_t *block_header
,IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index
,ipcdetail::true_ is_node_index)
{
(void)is_node_index;
typedef IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > index_t;
typedef typename index_t::index_data_t index_data_t;
index_data_t* si = block_header_t::template to_first_header<index_data_t>(block_header);
return this->priv_generic_named_destroy_impl<T, CharT>(*si, index);
}
template <class T, class CharT>
bool priv_generic_named_destroy
(block_header_t *block_header
,IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index
,ipcdetail::false_ is_node_index)
{
(void)is_node_index;
CharT *name = static_cast<CharT*>(block_header->template name<CharT>());
return this->priv_generic_named_destroy<T, CharT>(name, index);
}
template <class T, class CharT>
bool priv_generic_named_destroy(const CharT *name,
IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index)
{
typedef IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > index_t;
typedef typename index_t::iterator index_it;
typedef typename index_t::compare_key_type compare_key_t;
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
//Find name in index
compare_key_t key(name, std::char_traits<CharT>::length(name));
index_it it = index.find(key);
//If not found, return false
if(it == index.end()){
//This name is not present in the index, wrong pointer or name!
//BOOST_ASSERT(0);
return false;
}
return this->priv_generic_named_destroy_impl<T, CharT>(it, index);
}
template <class T, class CharT>
bool priv_generic_named_destroy_impl
(typename IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> >::iterator it,
IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index)
{
typedef IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > index_t;
typedef typename index_t::index_data_t index_data_t;
//Get allocation parameters
BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment =
boost::move_detail::alignment_of<T>::value;
block_header_t *ctrl_data = priv_block_header_from_it(it, is_intrusive_t());
//Sanity checks
BOOST_ASSERT((ctrl_data->value_bytes() % sizeof(T)) == 0);
//Erase node from index
index.erase(it);
void *memory;
BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value){
index_data_t*ihdr = block_header_t::template to_first_header<index_data_t>(ctrl_data);
const std::size_t front_space = block_header_t::template front_space_with_header<t_alignment, index_data_t>();
memory = (char*)ihdr - front_space;
ihdr->~index_data_t();
}
else{
const std::size_t front_space = block_header_t::template front_space_without_header<t_alignment>();
memory = (char*)ctrl_data - front_space;
}
//Call destructors and free memory
priv_destroy_n(static_cast<T*>(ctrl_data->value()), ctrl_data->value_bytes()/sizeof(T));
//Destroy the headers
ctrl_data->~block_header_t();
this->deallocate(memory);
return true;
}
template<class IndexIt>
static block_header_t* priv_block_header_from_it(IndexIt it, ipcdetail::true_) //is_intrusive
{ return block_header_t::from_first_header(&*it); }
template<class IndexIt>
static block_header_t* priv_block_header_from_it(IndexIt it, ipcdetail::false_ ) //!is_intrusive
{
return static_cast<block_header_t*>(ipcdetail::to_raw_pointer(it->second.m_ptr));
}
//!Generic named new function for
//!named functions
template<class Proxy, class CharT>
typename Proxy::object_type * priv_generic_named_construct
(Proxy pr, unsigned char type, const CharT *name, size_type num, bool try2find, bool dothrow,
IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > &index)
{
typedef typename Proxy::object_type object_type;
std::size_t namelen = std::char_traits<CharT>::length(name);
BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = boost::move_detail::alignment_of<object_type>::value;
block_header_t block_info ( size_type(sizeof(object_type)*num)
, size_type(t_alignment)
, type
, sizeof(CharT)
, namelen);
typedef IndexType<ipcdetail::index_config<CharT, MemoryAlgorithm> > index_t;
typedef typename index_t::iterator index_it;
typedef typename index_t::compare_key_type compare_key_t;
typedef typename index_t::insert_commit_data commit_data_t;
typedef typename index_t::index_data_t index_data_t;
//-------------------------------
scoped_lock<rmutex> guard(m_header);
//-------------------------------
//First, we want to know if the key is already present before
//we allocate any memory, and if the key is not present, we
//want to allocate all memory in a single buffer that will
//contain the name and the user buffer.
index_it existing_it;
bool found = false;
commit_data_t commit_data;
BOOST_INTERPROCESS_TRY{
typedef std::pair<index_it, bool> index_ib;
index_ib insert_ret = index.insert_check(compare_key_t(name, namelen), commit_data);
existing_it = insert_ret.first;
found = !insert_ret.second;
}
//Ignore exceptions
BOOST_INTERPROCESS_CATCH(...){
if(dothrow)
BOOST_INTERPROCESS_RETHROW
return 0;
}
BOOST_INTERPROCESS_CATCH_END
//If found and this is find or construct, return data, error otherwise
if(found){
if(try2find){
return static_cast<object_type*>(priv_block_header_from_it(existing_it, is_intrusive_t())->value());
}
return ipcdetail::null_or_already_exists<object_type>(dothrow);
}
//Allocates buffer for name + data, this can throw (it hurts)
void *buffer_ptr;
block_header_t * hdr;
std::size_t front_space;
//Allocate and construct the headers
BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value){
const size_type total_size = block_info.template total_named_size_with_header<t_alignment, CharT, index_data_t>(namelen);
#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2)
buffer_ptr = this->allocate(total_size, nothrow<>::get());
#else
buffer_ptr = this->allocate_aligned(total_size, t_alignment, nothrow<>::get());
#endif
if(!buffer_ptr)
return ipcdetail::null_or_bad_alloc<object_type>(dothrow);
front_space = block_header_t::template front_space_with_header<t_alignment, index_data_t>();
hdr = block_header_t::template from_first_header(reinterpret_cast<index_data_t*>((void*)((char*)buffer_ptr + front_space)));
}
else{
const size_type total_size = block_info.template total_named_size<t_alignment, CharT>(namelen);
#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2)
buffer_ptr = this->allocate(total_size, nothrow<>::get());
#else
buffer_ptr = this->allocate_aligned(total_size, t_alignment, nothrow<>::get());
#endif
front_space = block_header_t::template front_space_without_header<t_alignment>();
//Check if there is enough memory
if (!buffer_ptr)
return ipcdetail::null_or_bad_alloc<object_type>(dothrow);
hdr = reinterpret_cast<block_header_t*>((void*)((char*)buffer_ptr + front_space));
}
BOOST_ASSERT(is_ptr_aligned(hdr));
hdr = ::new(hdr, boost_container_new_t()) block_header_t(block_info);
//Build scoped ptr to avoid leaks with constructor exception
ipcdetail::mem_algo_deallocator<segment_manager_base_type> mem
(buffer_ptr, *static_cast<segment_manager_base_type*>(this));
void *ptr = hdr->value();
//Copy name to memory segment and insert data
hdr->store_name_length(static_cast<typename block_header_t::name_len_t>(namelen));
CharT *name_ptr = static_cast<CharT *>(hdr->template name<CharT>());
std::char_traits<CharT>::copy(name_ptr, name, namelen+1);
index_it it;
BOOST_INTERPROCESS_TRY{
BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value) {
index_data_t* index_data = ::new((char*)buffer_ptr + front_space, boost_container_new_t()) index_data_t();
BOOST_ASSERT(is_ptr_aligned(index_data));
it = index.insert_commit(compare_key_t(name_ptr, namelen), hdr, *index_data, commit_data);
}
else{
index_data_t id;
it = index.insert_commit(compare_key_t(name_ptr, namelen), hdr, id, commit_data);
}
}
//Ignore exceptions
BOOST_INTERPROCESS_CATCH(...){
if(dothrow)
BOOST_INTERPROCESS_RETHROW
return 0;
}
BOOST_INTERPROCESS_CATCH_END
//Initialize the node value_eraser to erase inserted node
//if something goes wrong
value_eraser<index_t> v_eraser(index, it);
//Construct array, this can throw
pr.construct_n(ptr, num);
//Release rollbacks since construction was successful
v_eraser.release();
mem.release();
object_type* const pret = static_cast<object_type*>(ptr);
BOOST_ASSERT(is_ptr_aligned(pret));
return pret;
}
private:
//!Returns the this pointer
segment_manager *get_this_pointer()
{ return this; }
typedef typename MemoryAlgorithm::mutex_family::recursive_mutex_type rmutex;
scoped_lock<rmutex> priv_get_lock(bool use_lock)
{
scoped_lock<rmutex> local(m_header, defer_lock);
if(use_lock){
local.lock();
}
return scoped_lock<rmutex>(boost::move(local));
}
//!This struct includes needed data and derives from
//!rmutex to allow EBO when using null interprocess_mutex
struct header_t
: public rmutex
{
named_index_t m_named_index;
unique_index_t m_unique_index;
header_t(segment_manager_base_t *segment_mngr_base)
: m_named_index (segment_mngr_base)
, m_unique_index(segment_mngr_base)
{}
} m_header;
#endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
};
}} //namespace boost { namespace interprocess
#include <boost/interprocess/detail/config_end.hpp>
#endif //#ifndef BOOST_INTERPROCESS_SEGMENT_MANAGER_HPP