////////////////////////////////////////////////////////////////////////////// // // (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 #if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //std::size_t #include //char_traits #include //std::nothrow #include //std::pair #include #ifndef BOOST_NO_EXCEPTIONS #include #endif //!\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 segment_manager_base : private MemoryAlgorithm { public: typedef segment_manager_base segment_manager_base_type; typedef typename MemoryAlgorithm::void_pointer void_pointer; typedef typename MemoryAlgorithm::mutex_family mutex_family; typedef MemoryAlgorithm memory_algorithm; /// @cond //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; /// @endcond //!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 size, size_type reserved_bytes) : MemoryAlgorithm(size, reserved_bytes) { BOOST_ASSERT((sizeof(segment_manager_base) == 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, std::nothrow_t) { return MemoryAlgorithm::allocate(nbytes); } /// @cond //Experimental. Dont' use. //!Allocates n_elements of //!elem_size bytes. Throws bad_alloc on failure. multiallocation_chain allocate_many(size_type elem_bytes, size_type num_elements) { multiallocation_chain mem(MemoryAlgorithm::allocate_many(elem_bytes, num_elements)); if(mem.empty()) throw bad_alloc(); return boost::move(mem); } //!Allocates n_elements, each one of //!element_lenghts[i]*sizeof_element bytes. Throws bad_alloc on failure. multiallocation_chain allocate_many (const size_type *element_lenghts, size_type n_elements, size_type sizeof_element = 1) { multiallocation_chain mem(MemoryAlgorithm::allocate_many(element_lenghts, n_elements, sizeof_element)); if(mem.empty()) throw bad_alloc(); return boost::move(mem); } //!Allocates n_elements of //!elem_size bytes. Returns a default constructed iterator on failure. multiallocation_chain allocate_many (size_type elem_bytes, size_type num_elements, std::nothrow_t) { return MemoryAlgorithm::allocate_many(elem_bytes, num_elements); } //!Allocates n_elements, each one of //!element_lenghts[i]*sizeof_element bytes. //!Returns a default constructed iterator on failure. multiallocation_chain allocate_many (const size_type *elem_sizes, size_type n_elements, size_type sizeof_element, std::nothrow_t) { return MemoryAlgorithm::allocate_many(elem_sizes, n_elements, sizeof_element); } //!Deallocates elements pointed by the //!multiallocation iterator range. void deallocate_many(multiallocation_chain chain) { MemoryAlgorithm::deallocate_many(boost::move(chain)); } /// @endcond //!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, 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; } template std::pair allocation_command (boost::interprocess::allocation_type command, size_type limit_size, size_type preferred_size,size_type &received_size, T *reuse_ptr = 0) { std::pair ret = MemoryAlgorithm::allocation_command ( command | boost::interprocess::nothrow_allocation, limit_size, preferred_size, received_size , reuse_ptr); if(!(command & boost::interprocess::nothrow_allocation) && !ret.first) throw bad_alloc(); return ret; } std::pair raw_allocation_command (boost::interprocess::allocation_type command, size_type limit_objects, size_type preferred_objects,size_type &received_objects, void *reuse_ptr = 0, size_type sizeof_object = 1) { std::pair ret = MemoryAlgorithm::raw_allocation_command ( command | boost::interprocess::nothrow_allocation, limit_objects, preferred_objects, received_objects , reuse_ptr, sizeof_object); if(!(command & boost::interprocess::nothrow_allocation) && !ret.first) throw bad_alloc(); return ret; } //!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) { 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); } /// @cond protected: void * prot_anonymous_construct (size_type num, bool dothrow, ipcdetail::in_place_interface &table) { typedef ipcdetail::block_header block_header_t; block_header_t block_info ( size_type(table.size*num) , size_type(table.alignment) , anonymous_type , 1 , 0); //Allocate memory void *ptr_struct = this->allocate(block_info.total_size(), std::nothrow_t()); //Check if there is enough memory if(!ptr_struct){ if(dothrow){ throw bad_alloc(); } else{ return 0; } } //Build scoped ptr to avoid leaks with constructor exception ipcdetail::mem_algo_deallocator mem(ptr_struct, *this); //Now construct the header block_header_t * hdr = new(ptr_struct) block_header_t(block_info); void *ptr = 0; //avoid gcc warning ptr = hdr->value(); //Now call constructors ipcdetail::array_construct(ptr, num, table); //All constructors successful, we don't want erase memory mem.release(); return ptr; } //!Calls the destructor and makes an anonymous deallocate void prot_anonymous_destroy(const void *object, ipcdetail::in_place_interface &table) { //Get control data from associated with this object typedef ipcdetail::block_header block_header_t; block_header_t *ctrl_data = block_header_t::block_header_from_value(object, table.size, table.alignment); //------------------------------- //scoped_lock guard(m_header); //------------------------------- if(ctrl_data->alloc_type() != anonymous_type){ //This is not an anonymous object, the pointer is wrong! BOOST_ASSERT(0); } //Call destructors and free memory //Build scoped ptr to avoid leaks with destructor exception std::size_t destroyed = 0; table.destroy_n(const_cast(object), ctrl_data->m_value_bytes/table.size, destroyed); this->deallocate(ctrl_data); } /// @endcond }; //!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 inherits publicly //!from segment_manager_base 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 IndexType> class segment_manager : public segment_manager_base { /// @cond //Non-copyable segment_manager(); segment_manager(const segment_manager &); segment_manager &operator=(const segment_manager &); typedef segment_manager_base Base; /// @endcond public: typedef MemoryAlgorithm memory_algorithm; typedef typename Base::void_pointer void_pointer; typedef typename Base::size_type size_type; typedef typename Base::difference_type difference_type; typedef CharType char_type; typedef segment_manager_base segment_manager_base_type; static const size_type PayloadPerAllocation = Base::PayloadPerAllocation; /// @cond private: typedef ipcdetail::block_header block_header_t; typedef ipcdetail::index_config index_config_named; typedef ipcdetail::index_config index_config_unique; typedef IndexType index_type; typedef ipcdetail::bool_::value > is_intrusive_t; typedef ipcdetail::bool_::value> is_node_index_t; public: typedef IndexType named_index_t; typedef IndexType unique_index_t; typedef ipcdetail::char_ptr_holder char_ptr_holder_t; typedef ipcdetail::segment_manager_iterator_transform ::value> named_transform; typedef ipcdetail::segment_manager_iterator_transform ::value> unique_transform; /// @endcond typedef typename Base::mutex_family mutex_family; typedef transform_iterator const_named_iterator; typedef transform_iterator const_unique_iterator; /// @cond //!Constructor proxy object definition helper class template struct construct_proxy { typedef ipcdetail::named_proxy type; }; //!Constructor proxy object definition helper class template struct construct_iter_proxy { typedef ipcdetail::named_proxy type; }; /// @endcond //!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 size) : Base(size, priv_get_reserved_bytes()) , m_header(static_cast(get_this_pointer())) { (void) anonymous_instance; (void) unique_instance; BOOST_ASSERT(static_cast(this) == static_cast(static_cast(this))); } //!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 std::pair find (const CharType* name) { return this->priv_find_impl(name, true); } //!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 std::pair find (const ipcdetail::unique_instance_t* name) { return this->priv_find_impl(name, true); } //!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. This search is not mutex-protected! template std::pair find_no_lock (const CharType* name) { return this->priv_find_impl(name, false); } //!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. This search is not mutex-protected! template std::pair find_no_lock (const ipcdetail::unique_instance_t* name) { return this->priv_find_impl(name, false); } //!Returns throwing "construct" proxy //!object template typename construct_proxy::type construct(char_ptr_holder_t name) { return typename construct_proxy::type (this, name, false, true); } //!Returns throwing "search or construct" proxy //!object template typename construct_proxy::type find_or_construct(char_ptr_holder_t name) { return typename construct_proxy::type (this, name, true, true); } //!Returns no throwing "construct" proxy //!object template typename construct_proxy::type construct(char_ptr_holder_t name, std::nothrow_t) { return typename construct_proxy::type (this, name, false, false); } //!Returns no throwing "search or construct" //!proxy object template typename construct_proxy::type find_or_construct(char_ptr_holder_t name, std::nothrow_t) { return typename construct_proxy::type (this, name, true, false); } //!Returns throwing "construct from iterators" proxy object template typename construct_iter_proxy::type construct_it(char_ptr_holder_t name) { return typename construct_iter_proxy::type (this, name, false, true); } //!Returns throwing "search or construct from iterators" //!proxy object template typename construct_iter_proxy::type find_or_construct_it(char_ptr_holder_t name) { return typename construct_iter_proxy::type (this, name, true, true); } //!Returns no throwing "construct from iterators" //!proxy object template typename construct_iter_proxy::type construct_it(char_ptr_holder_t name, std::nothrow_t) { return typename construct_iter_proxy::type (this, name, false, false); } //!Returns no throwing "search or construct from iterators" //!proxy object template typename construct_iter_proxy::type find_or_construct_it(char_ptr_holder_t name, std::nothrow_t) { return typename construct_iter_proxy::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 void atomic_func(Func &f) { scoped_lock 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 bool try_atomic_func(Func &f) { scoped_lock guard(m_header, try_to_lock); if(guard){ f(); return true; } else{ return false; } } //!Destroys a previously created unique instance. //!Returns false if the object was not present. template bool destroy(const ipcdetail::unique_instance_t *) { ipcdetail::placement_destroy dtor; return this->priv_generic_named_destroy (typeid(T).name(), m_header.m_unique_index, dtor, is_intrusive_t()); } //!Destroys the named object with //!the given name. Returns false if that object can't be found. template bool destroy(const CharType *name) { ipcdetail::placement_destroy dtor; return this->priv_generic_named_destroy (name, m_header.m_named_index, dtor, is_intrusive_t()); } //!Destroys an anonymous, unique or named object //!using it's address template void destroy_ptr(const T *p) { //If T is void transform it to char typedef typename ipcdetail::char_if_void::type data_t; ipcdetail::placement_destroy dtor; priv_destroy_ptr(p, dtor); } //!Returns the name of an object created with construct/find_or_construct //!functions. Does not throw template 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 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 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 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 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 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 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 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 Base::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 struct allocator { typedef boost::interprocess::allocator type; }; //!Returns an instance of the default allocator for type T //!initialized that allocates memory from this segment manager. template typename allocator::type get_allocator() { return typename allocator::type(this); } //!This is the default deleter to delete types T //!from this managed segment. template struct deleter { typedef boost::interprocess::deleter type; }; //!Returns an instance of the default allocator for type T //!initialized that allocates memory from this segment manager. template typename deleter::type get_deleter() { return typename deleter::type(this); } /// @cond //!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 T *generic_construct(const CharType *name, size_type num, bool try2find, bool dothrow, ipcdetail::in_place_interface &table) { return static_cast (priv_generic_construct(name, num, try2find, dothrow, table)); } 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 std::pair 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); ipcdetail::placement_destroy table; size_type size; void *ret; if(name == reinterpret_cast(-1)){ ret = priv_generic_find (typeid(T).name(), m_header.m_unique_index, table, size, is_intrusive_t(), lock); } else{ ret = priv_generic_find (name, m_header.m_named_index, table, size, is_intrusive_t(), lock); } return std::pair(static_cast(ret), size); } //!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 std::pair priv_find__impl (const ipcdetail::unique_instance_t* name, bool lock) { ipcdetail::placement_destroy table; size_type size; void *ret = priv_generic_find(name, m_header.m_unique_index, table, size, is_intrusive_t(), lock); return std::pair(static_cast(ret), size); } void *priv_generic_construct(const CharType *name, size_type num, bool try2find, bool dothrow, ipcdetail::in_place_interface &table) { void *ret; //Security overflow check if(num > ((std::size_t)-1)/table.size){ if(dothrow) throw bad_alloc(); else return 0; } if(name == 0){ ret = this->prot_anonymous_construct(num, dothrow, table); } else if(name == reinterpret_cast(-1)){ ret = this->priv_generic_named_construct (unique_type, table.type_name, num, try2find, dothrow, table, m_header.m_unique_index, is_intrusive_t()); } else{ ret = this->priv_generic_named_construct (named_type, name, num, try2find, dothrow, table, m_header.m_named_index, is_intrusive_t()); } return ret; } void priv_destroy_ptr(const void *ptr, ipcdetail::in_place_interface &dtor) { block_header_t *ctrl_data = block_header_t::block_header_from_value(ptr, dtor.size, dtor.alignment); switch(ctrl_data->alloc_type()){ case anonymous_type: this->prot_anonymous_destroy(ptr, dtor); break; case named_type: this->priv_generic_named_destroy (ctrl_data, m_header.m_named_index, dtor, is_node_index_t()); break; case unique_type: this->priv_generic_named_destroy (ctrl_data, m_header.m_unique_index, dtor, is_node_index_t()); break; default: //This type is unknown, bad pointer passed to this function! BOOST_ASSERT(0); break; } } //!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 != named_type){ BOOST_ASSERT((type == anonymous_type && ctrl_data->m_num_char == 0) || (type == unique_type && ctrl_data->m_num_char != 0) ); return 0; } CharType *name = static_cast(ctrl_data->template name()); //Sanity checks BOOST_ASSERT(ctrl_data->sizeof_char() == sizeof(CharType)); BOOST_ASSERT(ctrl_data->m_num_char == std::char_traits::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 Base base. return sizeof(segment_manager) - sizeof(Base); } template void *priv_generic_find (const CharT* name, IndexType > &index, ipcdetail::in_place_interface &table, size_type &length, ipcdetail::true_ is_intrusive, bool use_lock) { (void)is_intrusive; typedef IndexType > index_type; typedef ipcdetail::index_key index_key_t; typedef typename index_type::iterator index_it; //------------------------------- scoped_lock guard(priv_get_lock(use_lock)); //------------------------------- //Find name in index ipcdetail::intrusive_compare_key key (name, std::char_traits::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 = it->get_block_header(); //Sanity check BOOST_ASSERT((ctrl_data->m_value_bytes % table.size) == 0); BOOST_ASSERT(ctrl_data->sizeof_char() == sizeof(CharT)); ret_ptr = ctrl_data->value(); length = ctrl_data->m_value_bytes/table.size; } return ret_ptr; } template void *priv_generic_find (const CharT* name, IndexType > &index, ipcdetail::in_place_interface &table, size_type &length, ipcdetail::false_ is_intrusive, bool use_lock) { (void)is_intrusive; typedef IndexType > index_type; typedef typename index_type::key_type key_type; typedef typename index_type::iterator index_it; //------------------------------- scoped_lock guard(priv_get_lock(use_lock)); //------------------------------- //Find name in index index_it it = index.find(key_type(name, std::char_traits::length(name))); //Initialize return values void *ret_ptr = 0; length = 0; //If found, assign values if(it != index.end()){ //Get header block_header_t *ctrl_data = reinterpret_cast (ipcdetail::to_raw_pointer(it->second.m_ptr)); //Sanity check BOOST_ASSERT((ctrl_data->m_value_bytes % table.size) == 0); BOOST_ASSERT(ctrl_data->sizeof_char() == sizeof(CharT)); ret_ptr = ctrl_data->value(); length = ctrl_data->m_value_bytes/table.size; } return ret_ptr; } template bool priv_generic_named_destroy (block_header_t *block_header, IndexType > &index, ipcdetail::in_place_interface &table, ipcdetail::true_ is_node_index) { (void)is_node_index; typedef typename IndexType >::iterator index_it; index_it *ihdr = block_header_t::template to_first_header(block_header); return this->priv_generic_named_destroy_impl(*ihdr, index, table); } template bool priv_generic_named_destroy (block_header_t *block_header, IndexType > &index, ipcdetail::in_place_interface &table, ipcdetail::false_ is_node_index) { (void)is_node_index; CharT *name = static_cast(block_header->template name()); return this->priv_generic_named_destroy(name, index, table, is_intrusive_t()); } template bool priv_generic_named_destroy(const CharT *name, IndexType > &index, ipcdetail::in_place_interface &table, ipcdetail::true_ is_intrusive_index) { (void)is_intrusive_index; typedef IndexType > index_type; typedef ipcdetail::index_key index_key_t; typedef typename index_type::iterator index_it; typedef typename index_type::value_type intrusive_value_type; //------------------------------- scoped_lock guard(m_header); //------------------------------- //Find name in index ipcdetail::intrusive_compare_key key (name, std::char_traits::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; } block_header_t *ctrl_data = it->get_block_header(); intrusive_value_type *iv = intrusive_value_type::get_intrusive_value_type(ctrl_data); void *memory = iv; void *values = ctrl_data->value(); std::size_t num = ctrl_data->m_value_bytes/table.size; //Sanity check BOOST_ASSERT((ctrl_data->m_value_bytes % table.size) == 0); BOOST_ASSERT(sizeof(CharT) == ctrl_data->sizeof_char()); //Erase node from index index.erase(it); //Destroy the headers ctrl_data->~block_header_t(); iv->~intrusive_value_type(); //Call destructors and free memory std::size_t destroyed; table.destroy_n(values, num, destroyed); this->deallocate(memory); return true; } template bool priv_generic_named_destroy(const CharT *name, IndexType > &index, ipcdetail::in_place_interface &table, ipcdetail::false_ is_intrusive_index) { (void)is_intrusive_index; typedef IndexType > index_type; typedef typename index_type::iterator index_it; typedef typename index_type::key_type key_type; //------------------------------- scoped_lock guard(m_header); //------------------------------- //Try to find the name in the index index_it it = index.find(key_type (name, std::char_traits::length(name))); //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(it, index, table); } template bool priv_generic_named_destroy_impl (const typename IndexType >::iterator &it, IndexType > &index, ipcdetail::in_place_interface &table) { typedef IndexType > index_type; typedef typename index_type::iterator index_it; //Get allocation parameters block_header_t *ctrl_data = reinterpret_cast (ipcdetail::to_raw_pointer(it->second.m_ptr)); char *stored_name = static_cast(static_cast(const_cast(it->first.name()))); (void)stored_name; //Check if the distance between the name pointer and the memory pointer //is correct (this can detect incorrect type in destruction) std::size_t num = ctrl_data->m_value_bytes/table.size; void *values = ctrl_data->value(); //Sanity check BOOST_ASSERT((ctrl_data->m_value_bytes % table.size) == 0); BOOST_ASSERT(static_cast(stored_name) == static_cast(ctrl_data->template name())); BOOST_ASSERT(sizeof(CharT) == ctrl_data->sizeof_char()); //Erase node from index index.erase(it); //Destroy the header ctrl_data->~block_header_t(); void *memory; if(is_node_index_t::value){ index_it *ihdr = block_header_t::template to_first_header(ctrl_data); ihdr->~index_it(); memory = ihdr; } else{ memory = ctrl_data; } //Call destructors and free memory std::size_t destroyed; table.destroy_n(values, num, destroyed); this->deallocate(memory); return true; } template void * priv_generic_named_construct(unsigned char type, const CharT *name, size_type num, bool try2find, bool dothrow, ipcdetail::in_place_interface &table, IndexType > &index, ipcdetail::true_ is_intrusive) { (void)is_intrusive; std::size_t namelen = std::char_traits::length(name); block_header_t block_info ( size_type(table.size*num) , size_type(table.alignment) , type , sizeof(CharT) , namelen); typedef IndexType > index_type; typedef typename index_type::iterator index_it; typedef std::pair index_ib; //------------------------------- scoped_lock guard(m_header); //------------------------------- //Insert the node. This can throw. //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. // //Since equal_range(key) + insert(hint, value) approach is //quite inefficient in container implementations //(they re-test if the position is correct), I've chosen //to insert the node, do an ugly un-const cast and modify //the key (which is a smart pointer) to an equivalent one index_ib insert_ret; typename index_type::insert_commit_data commit_data; typedef typename index_type::value_type intrusive_value_type; BOOST_TRY{ ipcdetail::intrusive_compare_key key(name, namelen); insert_ret = index.insert_check(key, commit_data); } //Ignore exceptions BOOST_CATCH(...){ if(dothrow) BOOST_RETHROW return 0; } BOOST_CATCH_END index_it it = insert_ret.first; //If found and this is find or construct, return data //else return null if(!insert_ret.second){ if(try2find){ return it->get_block_header()->value(); } if(dothrow){ throw interprocess_exception(already_exists_error); } else{ return 0; } } //Allocates buffer for name + data, this can throw (it hurts) void *buffer_ptr; //Check if there is enough memory if(dothrow){ buffer_ptr = this->allocate (block_info.template total_size_with_header()); } else{ buffer_ptr = this->allocate (block_info.template total_size_with_header(), std::nothrow_t()); if(!buffer_ptr) return 0; } //Now construct the intrusive hook plus the header intrusive_value_type * intrusive_hdr = new(buffer_ptr) intrusive_value_type(); block_header_t * hdr = new(intrusive_hdr->get_block_header())block_header_t(block_info); void *ptr = 0; //avoid gcc warning ptr = hdr->value(); //Copy name to memory segment and insert data CharT *name_ptr = static_cast(hdr->template name()); std::char_traits::copy(name_ptr, name, namelen+1); BOOST_TRY{ //Now commit the insertion using previous context data it = index.insert_commit(*intrusive_hdr, commit_data); } //Ignore exceptions BOOST_CATCH(...){ if(dothrow) BOOST_RETHROW return 0; } BOOST_CATCH_END //Avoid constructions if constructor is trivial //Build scoped ptr to avoid leaks with constructor exception ipcdetail::mem_algo_deallocator mem (buffer_ptr, *static_cast(this)); //Initialize the node value_eraser to erase inserted node //if something goes wrong. This will be executed *before* //the memory allocation as the intrusive value is built in that //memory value_eraser v_eraser(index, it); //Construct array, this can throw ipcdetail::array_construct(ptr, num, table); //Release rollbacks since construction was successful v_eraser.release(); mem.release(); return ptr; } //!Generic named new function for //!named functions template void * priv_generic_named_construct(unsigned char type, const CharT *name, size_type num, bool try2find, bool dothrow, ipcdetail::in_place_interface &table, IndexType > &index, ipcdetail::false_ is_intrusive) { (void)is_intrusive; std::size_t namelen = std::char_traits::length(name); block_header_t block_info ( size_type(table.size*num) , size_type(table.alignment) , type , sizeof(CharT) , namelen); typedef IndexType > index_type; typedef typename index_type::key_type key_type; typedef typename index_type::mapped_type mapped_type; typedef typename index_type::value_type value_type; typedef typename index_type::iterator index_it; typedef std::pair index_ib; //------------------------------- scoped_lock guard(m_header); //------------------------------- //Insert the node. This can throw. //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. // //Since equal_range(key) + insert(hint, value) approach is //quite inefficient in container implementations //(they re-test if the position is correct), I've chosen //to insert the node, do an ugly un-const cast and modify //the key (which is a smart pointer) to an equivalent one index_ib insert_ret; BOOST_TRY{ insert_ret = index.insert(value_type(key_type (name, namelen), mapped_type(0))); } //Ignore exceptions BOOST_CATCH(...){ if(dothrow) BOOST_RETHROW; return 0; } BOOST_CATCH_END index_it it = insert_ret.first; //If found and this is find or construct, return data //else return null if(!insert_ret.second){ if(try2find){ block_header_t *hdr = static_cast (ipcdetail::to_raw_pointer(it->second.m_ptr)); return hdr->value(); } return 0; } //Initialize the node value_eraser to erase inserted node //if something goes wrong value_eraser v_eraser(index, it); //Allocates buffer for name + data, this can throw (it hurts) void *buffer_ptr; block_header_t * hdr; //Allocate and construct the headers if(is_node_index_t::value){ size_type total_size = block_info.template total_size_with_header(); if(dothrow){ buffer_ptr = this->allocate(total_size); } else{ buffer_ptr = this->allocate(total_size, std::nothrow_t()); if(!buffer_ptr) return 0; } index_it *idr = new(buffer_ptr) index_it(it); hdr = block_header_t::template from_first_header(idr); } else{ if(dothrow){ buffer_ptr = this->allocate(block_info.total_size()); } else{ buffer_ptr = this->allocate(block_info.total_size(), std::nothrow_t()); if(!buffer_ptr) return 0; } hdr = static_cast(buffer_ptr); } hdr = new(hdr)block_header_t(block_info); void *ptr = 0; //avoid gcc warning ptr = hdr->value(); //Copy name to memory segment and insert data CharT *name_ptr = static_cast(hdr->template name()); std::char_traits::copy(name_ptr, name, namelen+1); //Do the ugly cast, please mama, forgive me! //This new key points to an identical string, so it must have the //same position than the overwritten key according to the predicate const_cast(it->first).name(name_ptr); it->second.m_ptr = hdr; //Build scoped ptr to avoid leaks with constructor exception ipcdetail::mem_algo_deallocator mem (buffer_ptr, *static_cast(this)); //Construct array, this can throw ipcdetail::array_construct(ptr, num, table); //All constructors successful, we don't want to release memory mem.release(); //Release node v_eraser since construction was successful v_eraser.release(); return ptr; } private: //!Returns the this pointer segment_manager *get_this_pointer() { return this; } typedef typename MemoryAlgorithm::mutex_family::recursive_mutex_type rmutex; scoped_lock priv_get_lock(bool use_lock) { scoped_lock local(m_header, defer_lock); if(use_lock){ local.lock(); } return scoped_lock(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(Base *restricted_segment_mngr) : m_named_index (restricted_segment_mngr) , m_unique_index(restricted_segment_mngr) {} } m_header; /// @endcond }; }} //namespace boost { namespace interprocess #include #endif //#ifndef BOOST_INTERPROCESS_SEGMENT_MANAGER_HPP