Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: // Chris@101: // (C) Copyright Ion Gaztanaga 2005-2013. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // See http://www.boost.org/libs/container for documentation. Chris@16: // Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: #ifndef BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP Chris@16: #define BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP Chris@16: Chris@101: #ifndef BOOST_CONFIG_HPP Chris@101: # include Chris@101: #endif Chris@101: Chris@101: #if defined(BOOST_HAS_PRAGMA_ONCE) Chris@16: # pragma once Chris@16: #endif Chris@16: Chris@101: #include Chris@101: #include Chris@101: Chris@101: // container Chris@16: #include Chris@101: #include Chris@101: // container/detail Chris@101: #include Chris@101: #include Chris@101: #include Chris@101: #include Chris@101: #include Chris@101: #include Chris@101: #include Chris@101: // intrusive Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: // other Chris@16: #include Chris@101: #include Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace container { Chris@16: Chris@16: namespace adaptive_pool_flag { Chris@16: Chris@16: static const unsigned int none = 0u; Chris@16: static const unsigned int align_only = 1u << 0u; Chris@16: static const unsigned int size_ordered = 1u << 1u; Chris@16: static const unsigned int address_ordered = 1u << 2u; Chris@16: Chris@16: } //namespace adaptive_pool_flag{ Chris@16: Chris@16: namespace container_detail { Chris@16: Chris@16: template Chris@16: struct hdr_offset_holder_t Chris@16: { Chris@16: hdr_offset_holder_t(size_type offset = 0) Chris@16: : hdr_offset(offset) Chris@16: {} Chris@16: size_type hdr_offset; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct less_func; Chris@16: Chris@16: template Chris@16: struct less_func Chris@16: { Chris@16: static bool less(SizeType, SizeType, const void *, const void *) Chris@16: { return true; } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct less_func Chris@16: { Chris@16: static bool less(SizeType ls, SizeType rs, const void *, const void *) Chris@16: { return ls < rs; } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct less_func Chris@16: { Chris@16: static bool less(SizeType, SizeType, const void *la, const void *ra) Chris@16: { return &la < &ra; } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct less_func Chris@16: { Chris@16: static bool less(SizeType ls, SizeType rs, const void *la, const void *ra) Chris@16: { return (ls < rs) || ((ls == rs) && (la < ra)); } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct block_container_traits Chris@16: { Chris@16: typedef typename bi::make_set_base_hook Chris@16: < bi::void_pointer Chris@16: , bi::optimize_size Chris@16: , bi::link_mode >::type hook_t; Chris@16: Chris@16: template Chris@16: struct container Chris@16: { Chris@16: typedef typename bi::make_multiset Chris@16: , bi::size_type >::type type; Chris@16: }; Chris@16: Chris@16: template Chris@16: static void reinsert_was_used(Container &container, typename Container::reference v, bool) Chris@16: { Chris@16: typedef typename Container::const_iterator const_block_iterator; Chris@16: const const_block_iterator this_block Chris@16: (Container::s_iterator_to(const_cast(v))); Chris@16: const_block_iterator next_block(this_block); Chris@16: if(++next_block != container.cend()){ Chris@16: if(this_block->free_nodes.size() > next_block->free_nodes.size()){ Chris@16: container.erase(this_block); Chris@16: container.insert(v); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: static void insert_was_empty(Container &container, typename Container::value_type &v, bool) Chris@16: { Chris@16: container.insert(v); Chris@16: } Chris@16: Chris@16: template Chris@16: static void erase_first(Container &container) Chris@16: { Chris@16: container.erase(container.cbegin()); Chris@16: } Chris@16: Chris@16: template Chris@16: static void erase_last(Container &container) Chris@16: { Chris@16: container.erase(--container.cend()); Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct block_container_traits Chris@16: { Chris@16: typedef typename bi::make_list_base_hook Chris@16: < bi::void_pointer Chris@16: , bi::link_mode >::type hook_t; Chris@16: Chris@16: template Chris@16: struct container Chris@16: { Chris@16: typedef typename bi::make_list Chris@16: , bi::size_type, bi::constant_time_size >::type type; Chris@16: }; Chris@16: Chris@16: template Chris@16: static void reinsert_was_used(Container &container, typename Container::value_type &v, bool is_full) Chris@16: { Chris@16: if(is_full){ Chris@16: container.erase(Container::s_iterator_to(v)); Chris@16: container.push_back(v); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: static void insert_was_empty(Container &container, typename Container::value_type &v, bool is_full) Chris@16: { Chris@16: if(is_full){ Chris@16: container.push_back(v); Chris@16: } Chris@16: else{ Chris@16: container.push_front(v); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: static void erase_first(Container &container) Chris@16: { Chris@16: container.pop_front(); Chris@16: } Chris@16: Chris@16: template Chris@16: static void erase_last(Container &container) Chris@16: { Chris@16: container.pop_back(); Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct adaptive_pool_types Chris@16: { Chris@16: typedef VoidPointer void_pointer; Chris@16: static const bool ordered = (Flags & (adaptive_pool_flag::size_ordered | adaptive_pool_flag::address_ordered)) != 0; Chris@16: typedef block_container_traits block_container_traits_t; Chris@16: typedef typename block_container_traits_t::hook_t hook_t; Chris@16: typedef hdr_offset_holder_t hdr_offset_holder; Chris@16: static const unsigned int order_flags = Flags & (adaptive_pool_flag::size_ordered | adaptive_pool_flag::address_ordered); Chris@16: typedef MultiallocationChain free_nodes_t; Chris@16: Chris@16: struct block_info_t Chris@16: : public hdr_offset_holder, Chris@16: public hook_t Chris@16: { Chris@16: //An intrusive list of free node from this block Chris@16: free_nodes_t free_nodes; Chris@16: friend bool operator <(const block_info_t &l, const block_info_t &r) Chris@16: { Chris@16: return less_func:: Chris@16: less(l.free_nodes.size(), r.free_nodes.size(), &l , &r); Chris@16: } Chris@16: Chris@16: friend bool operator ==(const block_info_t &l, const block_info_t &r) Chris@16: { return &l == &r; } Chris@16: }; Chris@16: typedef typename block_container_traits_t:: template container::type block_container_t; Chris@16: }; Chris@16: Chris@16: template Chris@16: inline size_type calculate_alignment Chris@16: ( size_type overhead_percent, size_type real_node_size Chris@16: , size_type hdr_size, size_type hdr_offset_size, size_type payload_per_allocation) Chris@16: { Chris@16: //to-do: handle real_node_size != node_size Chris@16: const size_type divisor = overhead_percent*real_node_size; Chris@16: const size_type dividend = hdr_offset_size*100; Chris@16: size_type elements_per_subblock = (dividend - 1)/divisor + 1; Chris@16: size_type candidate_power_of_2 = Chris@16: upper_power_of_2(elements_per_subblock*real_node_size + hdr_offset_size); Chris@16: bool overhead_satisfied = false; Chris@16: //Now calculate the wors-case overhead for a subblock Chris@16: const size_type max_subblock_overhead = hdr_size + payload_per_allocation; Chris@16: while(!overhead_satisfied){ Chris@16: elements_per_subblock = (candidate_power_of_2 - max_subblock_overhead)/real_node_size; Chris@16: const size_type overhead_size = candidate_power_of_2 - elements_per_subblock*real_node_size; Chris@16: if(overhead_size*100/candidate_power_of_2 < overhead_percent){ Chris@16: overhead_satisfied = true; Chris@16: } Chris@16: else{ Chris@16: candidate_power_of_2 <<= 1; Chris@16: } Chris@16: } Chris@16: return candidate_power_of_2; Chris@16: } Chris@16: Chris@16: template Chris@16: inline void calculate_num_subblocks Chris@16: (size_type alignment, size_type real_node_size, size_type elements_per_block Chris@16: , size_type &num_subblocks, size_type &real_num_node, size_type overhead_percent Chris@16: , size_type hdr_size, size_type hdr_offset_size, size_type payload_per_allocation) Chris@16: { Chris@16: const size_type hdr_subblock_elements = (alignment - hdr_size - payload_per_allocation)/real_node_size; Chris@16: size_type elements_per_subblock = (alignment - hdr_offset_size)/real_node_size; Chris@16: size_type possible_num_subblock = (elements_per_block - 1)/elements_per_subblock + 1; Chris@16: while(((possible_num_subblock-1)*elements_per_subblock + hdr_subblock_elements) < elements_per_block){ Chris@16: ++possible_num_subblock; Chris@16: } Chris@16: elements_per_subblock = (alignment - hdr_offset_size)/real_node_size; Chris@16: bool overhead_satisfied = false; Chris@16: while(!overhead_satisfied){ Chris@16: const size_type total_data = (elements_per_subblock*(possible_num_subblock-1) + hdr_subblock_elements)*real_node_size; Chris@16: const size_type total_size = alignment*possible_num_subblock; Chris@16: if((total_size - total_data)*100/total_size < overhead_percent){ Chris@16: overhead_satisfied = true; Chris@16: } Chris@16: else{ Chris@16: ++possible_num_subblock; Chris@16: } Chris@16: } Chris@16: num_subblocks = possible_num_subblock; Chris@16: real_num_node = (possible_num_subblock-1)*elements_per_subblock + hdr_subblock_elements; Chris@16: } Chris@16: Chris@16: template Chris@16: class private_adaptive_node_pool_impl Chris@16: { Chris@16: //Non-copyable Chris@16: private_adaptive_node_pool_impl(); Chris@16: private_adaptive_node_pool_impl(const private_adaptive_node_pool_impl &); Chris@16: private_adaptive_node_pool_impl &operator=(const private_adaptive_node_pool_impl &); Chris@16: typedef private_adaptive_node_pool_impl this_type; Chris@16: Chris@16: typedef typename SegmentManagerBase::void_pointer void_pointer; Chris@16: static const typename SegmentManagerBase:: Chris@16: size_type PayloadPerAllocation = SegmentManagerBase::PayloadPerAllocation; Chris@16: //Flags Chris@16: //align_only Chris@16: static const bool AlignOnly = (Flags & adaptive_pool_flag::align_only) != 0; Chris@16: typedef bool_ IsAlignOnly; Chris@16: typedef true_ AlignOnlyTrue; Chris@16: typedef false_ AlignOnlyFalse; Chris@16: //size_ordered Chris@16: static const bool SizeOrdered = (Flags & adaptive_pool_flag::size_ordered) != 0; Chris@16: typedef bool_ IsSizeOrdered; Chris@16: typedef true_ SizeOrderedTrue; Chris@16: typedef false_ SizeOrderedFalse; Chris@16: //address_ordered Chris@16: static const bool AddressOrdered = (Flags & adaptive_pool_flag::address_ordered) != 0; Chris@16: typedef bool_ IsAddressOrdered; Chris@16: typedef true_ AddressOrderedTrue; Chris@16: typedef false_ AddressOrderedFalse; Chris@16: Chris@16: public: Chris@16: typedef typename SegmentManagerBase::multiallocation_chain multiallocation_chain; Chris@16: typedef typename SegmentManagerBase::size_type size_type; Chris@16: Chris@16: private: Chris@16: typedef adaptive_pool_types Chris@16: adaptive_pool_types_t; Chris@16: typedef typename adaptive_pool_types_t::free_nodes_t free_nodes_t; Chris@16: typedef typename adaptive_pool_types_t::block_info_t block_info_t; Chris@16: typedef typename adaptive_pool_types_t::block_container_t block_container_t; Chris@16: typedef typename adaptive_pool_types_t::block_container_traits_t block_container_traits_t; Chris@16: typedef typename block_container_t::iterator block_iterator; Chris@16: typedef typename block_container_t::const_iterator const_block_iterator; Chris@16: typedef typename adaptive_pool_types_t::hdr_offset_holder hdr_offset_holder; Chris@16: Chris@16: static const size_type MaxAlign = alignment_of::value; Chris@16: static const size_type HdrSize = ((sizeof(block_info_t)-1)/MaxAlign+1)*MaxAlign; Chris@16: static const size_type HdrOffsetSize = ((sizeof(hdr_offset_holder)-1)/MaxAlign+1)*MaxAlign; Chris@16: Chris@16: public: Chris@16: //!Segment manager typedef Chris@16: typedef SegmentManagerBase segment_manager_base_type; Chris@16: Chris@16: //!Constructor from a segment manager. Never throws Chris@16: private_adaptive_node_pool_impl Chris@16: ( segment_manager_base_type *segment_mngr_base Chris@16: , size_type node_size Chris@16: , size_type nodes_per_block Chris@16: , size_type max_free_blocks Chris@16: , unsigned char overhead_percent Chris@16: ) Chris@16: : m_max_free_blocks(max_free_blocks) Chris@16: , m_real_node_size(lcm(node_size, size_type(alignment_of::value))) Chris@16: //Round the size to a power of two value. Chris@16: //This is the total memory size (including payload) that we want to Chris@16: //allocate from the general-purpose allocator Chris@16: , m_real_block_alignment Chris@16: (AlignOnly ? Chris@16: upper_power_of_2(HdrSize + m_real_node_size*nodes_per_block) : Chris@16: calculate_alignment( (size_type)overhead_percent, m_real_node_size Chris@16: , HdrSize, HdrOffsetSize, PayloadPerAllocation)) Chris@16: //This is the real number of nodes per block Chris@16: , m_num_subblocks(0) Chris@16: , m_real_num_node(AlignOnly ? (m_real_block_alignment - PayloadPerAllocation - HdrSize)/m_real_node_size : 0) Chris@16: //General purpose allocator Chris@16: , mp_segment_mngr_base(segment_mngr_base) Chris@16: , m_block_container() Chris@16: , m_totally_free_blocks(0) Chris@16: { Chris@16: if(!AlignOnly){ Chris@16: calculate_num_subblocks Chris@16: ( m_real_block_alignment Chris@16: , m_real_node_size Chris@16: , nodes_per_block Chris@16: , m_num_subblocks Chris@16: , m_real_num_node Chris@16: , (size_type)overhead_percent Chris@16: , HdrSize Chris@16: , HdrOffsetSize Chris@16: , PayloadPerAllocation); Chris@16: } Chris@16: } Chris@16: Chris@16: //!Destructor. Deallocates all allocated blocks. Never throws Chris@16: ~private_adaptive_node_pool_impl() Chris@16: { this->priv_clear(); } Chris@16: Chris@16: size_type get_real_num_node() const Chris@16: { return m_real_num_node; } Chris@16: Chris@16: //!Returns the segment manager. Never throws Chris@16: segment_manager_base_type* get_segment_manager_base()const Chris@16: { return container_detail::to_raw_pointer(mp_segment_mngr_base); } Chris@16: Chris@16: //!Allocates array of count elements. Can throw Chris@16: void *allocate_node() Chris@16: { Chris@16: this->priv_invariants(); Chris@16: //If there are no free nodes we allocate a new block Chris@16: if(!m_block_container.empty()){ Chris@16: //We take the first free node the multiset can't be empty Chris@16: free_nodes_t &free_nodes = m_block_container.begin()->free_nodes; Chris@16: BOOST_ASSERT(!free_nodes.empty()); Chris@16: const size_type free_nodes_count = free_nodes.size(); Chris@16: void *first_node = container_detail::to_raw_pointer(free_nodes.pop_front()); Chris@16: if(free_nodes.empty()){ Chris@16: block_container_traits_t::erase_first(m_block_container); Chris@16: } Chris@16: m_totally_free_blocks -= static_cast(free_nodes_count == m_real_num_node); Chris@16: this->priv_invariants(); Chris@16: return first_node; Chris@16: } Chris@16: else{ Chris@16: multiallocation_chain chain; Chris@16: this->priv_append_from_new_blocks(1, chain, IsAlignOnly()); Chris@16: return container_detail::to_raw_pointer(chain.pop_front()); Chris@16: } Chris@16: } Chris@16: Chris@16: //!Deallocates an array pointed by ptr. Never throws Chris@16: void deallocate_node(void *pElem) Chris@16: { Chris@16: this->priv_invariants(); Chris@16: block_info_t &block_info = *this->priv_block_from_node(pElem); Chris@16: BOOST_ASSERT(block_info.free_nodes.size() < m_real_num_node); Chris@16: Chris@16: //We put the node at the beginning of the free node list Chris@16: block_info.free_nodes.push_back(void_pointer(pElem)); Chris@16: Chris@16: //The loop reinserts all blocks except the last one Chris@16: this->priv_reinsert_block(block_info, block_info.free_nodes.size() == 1); Chris@16: this->priv_deallocate_free_blocks(m_max_free_blocks); Chris@16: this->priv_invariants(); Chris@16: } Chris@16: Chris@16: //!Allocates n nodes. Chris@16: //!Can throw Chris@16: void allocate_nodes(const size_type n, multiallocation_chain &chain) Chris@16: { Chris@16: size_type i = 0; Chris@16: BOOST_TRY{ Chris@16: this->priv_invariants(); Chris@16: while(i != n){ Chris@16: //If there are no free nodes we allocate all needed blocks Chris@16: if (m_block_container.empty()){ Chris@16: this->priv_append_from_new_blocks(n - i, chain, IsAlignOnly()); Chris@16: BOOST_ASSERT(m_block_container.empty() || (++m_block_container.cbegin() == m_block_container.cend())); Chris@16: BOOST_ASSERT(chain.size() == n); Chris@16: break; Chris@16: } Chris@16: free_nodes_t &free_nodes = m_block_container.begin()->free_nodes; Chris@16: const size_type free_nodes_count_before = free_nodes.size(); Chris@16: m_totally_free_blocks -= static_cast(free_nodes_count_before == m_real_num_node); Chris@16: const size_type num_left = n-i; Chris@16: const size_type num_elems = (num_left < free_nodes_count_before) ? num_left : free_nodes_count_before; Chris@16: typedef typename free_nodes_t::iterator free_nodes_iterator; Chris@16: Chris@16: if(num_left < free_nodes_count_before){ Chris@16: const free_nodes_iterator it_bbeg(free_nodes.before_begin()); Chris@16: free_nodes_iterator it_bend(it_bbeg); Chris@16: for(size_type j = 0; j != num_elems; ++j){ Chris@16: ++it_bend; Chris@16: } Chris@16: free_nodes_iterator it_end = it_bend; ++it_end; Chris@16: free_nodes_iterator it_beg = it_bbeg; ++it_beg; Chris@16: free_nodes.erase_after(it_bbeg, it_end, num_elems); Chris@16: chain.incorporate_after(chain.last(), &*it_beg, &*it_bend, num_elems); Chris@16: //chain.splice_after(chain.last(), free_nodes, it_bbeg, it_bend, num_elems); Chris@16: BOOST_ASSERT(!free_nodes.empty()); Chris@16: } Chris@16: else{ Chris@16: const free_nodes_iterator it_beg(free_nodes.begin()), it_bend(free_nodes.last()); Chris@16: free_nodes.clear(); Chris@16: chain.incorporate_after(chain.last(), &*it_beg, &*it_bend, num_elems); Chris@16: block_container_traits_t::erase_first(m_block_container); Chris@16: } Chris@16: i += num_elems; Chris@16: } Chris@16: } Chris@16: BOOST_CATCH(...){ Chris@16: this->deallocate_nodes(chain); Chris@16: BOOST_RETHROW Chris@16: } Chris@16: BOOST_CATCH_END Chris@16: this->priv_invariants(); Chris@16: } Chris@16: Chris@16: //!Deallocates a linked list of nodes. Never throws Chris@16: void deallocate_nodes(multiallocation_chain &nodes) Chris@16: { Chris@16: this->priv_invariants(); Chris@16: //To take advantage of node locality, wait until two Chris@16: //nodes belong to different blocks. Only then reinsert Chris@16: //the block of the first node in the block tree. Chris@16: //Cache of the previous block Chris@16: block_info_t *prev_block_info = 0; Chris@16: Chris@16: //If block was empty before this call, it's not already Chris@16: //inserted in the block tree. Chris@16: bool prev_block_was_empty = false; Chris@16: typedef typename free_nodes_t::iterator free_nodes_iterator; Chris@16: { Chris@16: const free_nodes_iterator itbb(nodes.before_begin()), ite(nodes.end()); Chris@16: free_nodes_iterator itf(nodes.begin()), itbf(itbb); Chris@16: size_type splice_node_count = size_type(-1); Chris@16: while(itf != ite){ Chris@101: void *pElem = container_detail::to_raw_pointer(container_detail::iterator_to_raw_pointer(itf)); Chris@16: block_info_t &block_info = *this->priv_block_from_node(pElem); Chris@16: BOOST_ASSERT(block_info.free_nodes.size() < m_real_num_node); Chris@16: ++splice_node_count; Chris@16: Chris@16: //If block change is detected calculate the cached block position in the tree Chris@16: if(&block_info != prev_block_info){ Chris@16: if(prev_block_info){ //Make sure we skip the initial "dummy" cache Chris@16: free_nodes_iterator it(itbb); ++it; Chris@16: nodes.erase_after(itbb, itf, splice_node_count); Chris@16: prev_block_info->free_nodes.incorporate_after(prev_block_info->free_nodes.last(), &*it, &*itbf, splice_node_count); Chris@16: this->priv_reinsert_block(*prev_block_info, prev_block_was_empty); Chris@16: splice_node_count = 0; Chris@16: } Chris@16: //Update cache with new data Chris@16: prev_block_was_empty = block_info.free_nodes.empty(); Chris@16: prev_block_info = &block_info; Chris@16: } Chris@16: itbf = itf; Chris@16: ++itf; Chris@16: } Chris@16: } Chris@16: if(prev_block_info){ Chris@16: //The loop reinserts all blocks except the last one Chris@16: const free_nodes_iterator itfirst(nodes.begin()), itlast(nodes.last()); Chris@16: const size_type splice_node_count = nodes.size(); Chris@16: nodes.clear(); Chris@16: prev_block_info->free_nodes.incorporate_after(prev_block_info->free_nodes.last(), &*itfirst, &*itlast, splice_node_count); Chris@16: this->priv_reinsert_block(*prev_block_info, prev_block_was_empty); Chris@16: this->priv_invariants(); Chris@16: this->priv_deallocate_free_blocks(m_max_free_blocks); Chris@16: } Chris@16: } Chris@16: Chris@16: void deallocate_free_blocks() Chris@16: { this->priv_deallocate_free_blocks(0); } Chris@16: Chris@16: size_type num_free_nodes() Chris@16: { Chris@16: typedef typename block_container_t::const_iterator citerator; Chris@16: size_type count = 0; Chris@16: citerator it (m_block_container.begin()), itend(m_block_container.end()); Chris@16: for(; it != itend; ++it){ Chris@16: count += it->free_nodes.size(); Chris@16: } Chris@16: return count; Chris@16: } Chris@16: Chris@16: void swap(private_adaptive_node_pool_impl &other) Chris@16: { Chris@16: BOOST_ASSERT(m_max_free_blocks == other.m_max_free_blocks); Chris@16: BOOST_ASSERT(m_real_node_size == other.m_real_node_size); Chris@16: BOOST_ASSERT(m_real_block_alignment == other.m_real_block_alignment); Chris@16: BOOST_ASSERT(m_real_num_node == other.m_real_num_node); Chris@16: std::swap(mp_segment_mngr_base, other.mp_segment_mngr_base); Chris@16: std::swap(m_totally_free_blocks, other.m_totally_free_blocks); Chris@16: m_block_container.swap(other.m_block_container); Chris@16: } Chris@16: Chris@16: //Deprecated, use deallocate_free_blocks Chris@16: void deallocate_free_chunks() Chris@16: { this->priv_deallocate_free_blocks(0); } Chris@16: Chris@16: private: Chris@16: Chris@16: void priv_deallocate_free_blocks(size_type max_free_blocks) Chris@16: { //Trampoline function to ease inlining Chris@16: if(m_totally_free_blocks > max_free_blocks){ Chris@16: this->priv_deallocate_free_blocks_impl(max_free_blocks); Chris@16: } Chris@16: } Chris@16: Chris@16: void priv_deallocate_free_blocks_impl(size_type max_free_blocks) Chris@16: { Chris@16: this->priv_invariants(); Chris@16: //Now check if we've reached the free nodes limit Chris@16: //and check if we have free blocks. If so, deallocate as much Chris@16: //as we can to stay below the limit Chris@16: multiallocation_chain chain; Chris@16: { Chris@16: const const_block_iterator itend = m_block_container.cend(); Chris@16: const_block_iterator it = itend; Chris@16: --it; Chris@16: size_type totally_free_blocks = m_totally_free_blocks; Chris@16: Chris@16: for( ; totally_free_blocks > max_free_blocks; --totally_free_blocks){ Chris@16: BOOST_ASSERT(it->free_nodes.size() == m_real_num_node); Chris@16: void *addr = priv_first_subblock_from_block(const_cast(&*it)); Chris@16: --it; Chris@16: block_container_traits_t::erase_last(m_block_container); Chris@16: chain.push_front(void_pointer(addr)); Chris@16: } Chris@16: BOOST_ASSERT((m_totally_free_blocks - max_free_blocks) == chain.size()); Chris@16: m_totally_free_blocks = max_free_blocks; Chris@16: } Chris@16: this->mp_segment_mngr_base->deallocate_many(chain); Chris@16: } Chris@16: Chris@16: void priv_reinsert_block(block_info_t &prev_block_info, const bool prev_block_was_empty) Chris@16: { Chris@16: //Cache the free nodes from the block Chris@16: const size_type this_block_free_nodes = prev_block_info.free_nodes.size(); Chris@16: const bool is_full = this_block_free_nodes == m_real_num_node; Chris@16: Chris@16: //Update free block count Chris@16: m_totally_free_blocks += static_cast(is_full); Chris@16: if(prev_block_was_empty){ Chris@16: block_container_traits_t::insert_was_empty(m_block_container, prev_block_info, is_full); Chris@16: } Chris@16: else{ Chris@16: block_container_traits_t::reinsert_was_used(m_block_container, prev_block_info, is_full); Chris@16: } Chris@16: } Chris@16: Chris@16: class block_destroyer; Chris@16: friend class block_destroyer; Chris@16: Chris@16: class block_destroyer Chris@16: { Chris@16: public: Chris@16: block_destroyer(const this_type *impl, multiallocation_chain &chain) Chris@16: : mp_impl(impl), m_chain(chain) Chris@16: {} Chris@16: Chris@16: void operator()(typename block_container_t::pointer to_deallocate) Chris@16: { return this->do_destroy(to_deallocate, IsAlignOnly()); } Chris@16: Chris@16: private: Chris@16: void do_destroy(typename block_container_t::pointer to_deallocate, AlignOnlyTrue) Chris@16: { Chris@16: BOOST_ASSERT(to_deallocate->free_nodes.size() == mp_impl->m_real_num_node); Chris@16: m_chain.push_back(to_deallocate); Chris@16: } Chris@16: Chris@16: void do_destroy(typename block_container_t::pointer to_deallocate, AlignOnlyFalse) Chris@16: { Chris@16: BOOST_ASSERT(to_deallocate->free_nodes.size() == mp_impl->m_real_num_node); Chris@16: BOOST_ASSERT(0 == to_deallocate->hdr_offset); Chris@16: hdr_offset_holder *hdr_off_holder = Chris@16: mp_impl->priv_first_subblock_from_block(container_detail::to_raw_pointer(to_deallocate)); Chris@16: m_chain.push_back(hdr_off_holder); Chris@16: } Chris@16: Chris@16: const this_type *mp_impl; Chris@16: multiallocation_chain &m_chain; Chris@16: }; Chris@16: Chris@16: //This macro will activate invariant checking. Slow, but helpful for debugging the code. Chris@16: //#define BOOST_CONTAINER_ADAPTIVE_NODE_POOL_CHECK_INVARIANTS Chris@16: void priv_invariants() Chris@16: #ifdef BOOST_CONTAINER_ADAPTIVE_NODE_POOL_CHECK_INVARIANTS Chris@16: #undef BOOST_CONTAINER_ADAPTIVE_NODE_POOL_CHECK_INVARIANTS Chris@16: { Chris@16: const const_block_iterator itend(m_block_container.end()); Chris@16: Chris@16: { //We iterate through the block tree to free the memory Chris@16: const_block_iterator it(m_block_container.begin()); Chris@101: Chris@16: if(it != itend){ Chris@16: for(++it; it != itend; ++it){ Chris@16: const_block_iterator prev(it); Chris@16: --prev; Chris@16: BOOST_ASSERT(*prev < *it); Chris@16: (void)prev; (void)it; Chris@16: } Chris@16: } Chris@16: } Chris@16: { //Check that the total free nodes are correct Chris@16: const_block_iterator it(m_block_container.cbegin()); Chris@16: size_type total_free_nodes = 0; Chris@16: for(; it != itend; ++it){ Chris@16: total_free_nodes += it->free_nodes.size(); Chris@16: } Chris@16: BOOST_ASSERT(total_free_nodes >= m_totally_free_blocks*m_real_num_node); Chris@16: } Chris@16: { //Check that the total totally free blocks are correct Chris@16: BOOST_ASSERT(m_block_container.size() >= m_totally_free_blocks); Chris@16: const_block_iterator it = m_block_container.cend(); Chris@16: size_type total_free_blocks = m_totally_free_blocks; Chris@16: while(total_free_blocks--){ Chris@16: BOOST_ASSERT((--it)->free_nodes.size() == m_real_num_node); Chris@16: } Chris@16: } Chris@16: Chris@16: if(!AlignOnly){ Chris@16: //Check that header offsets are correct Chris@16: const_block_iterator it = m_block_container.begin(); Chris@16: for(; it != itend; ++it){ Chris@16: hdr_offset_holder *hdr_off_holder = this->priv_first_subblock_from_block(const_cast(&*it)); Chris@16: for(size_type i = 0, max = m_num_subblocks; i < max; ++i){ Chris@16: const size_type offset = reinterpret_cast(const_cast(&*it)) - reinterpret_cast(hdr_off_holder); Chris@16: BOOST_ASSERT(hdr_off_holder->hdr_offset == offset); Chris@16: BOOST_ASSERT(0 == ((size_type)hdr_off_holder & (m_real_block_alignment - 1))); Chris@16: BOOST_ASSERT(0 == (hdr_off_holder->hdr_offset & (m_real_block_alignment - 1))); Chris@16: hdr_off_holder = reinterpret_cast(reinterpret_cast(hdr_off_holder) + m_real_block_alignment); Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: #else Chris@16: {} //empty Chris@16: #endif Chris@16: Chris@16: //!Deallocates all used memory. Never throws Chris@16: void priv_clear() Chris@16: { Chris@16: #ifndef NDEBUG Chris@16: block_iterator it = m_block_container.begin(); Chris@16: block_iterator itend = m_block_container.end(); Chris@16: size_type n_free_nodes = 0; Chris@16: for(; it != itend; ++it){ Chris@16: //Check for memory leak Chris@16: BOOST_ASSERT(it->free_nodes.size() == m_real_num_node); Chris@16: ++n_free_nodes; Chris@16: } Chris@16: BOOST_ASSERT(n_free_nodes == m_totally_free_blocks); Chris@16: #endif Chris@16: //Check for memory leaks Chris@16: this->priv_invariants(); Chris@16: multiallocation_chain chain; Chris@16: m_block_container.clear_and_dispose(block_destroyer(this, chain)); Chris@16: this->mp_segment_mngr_base->deallocate_many(chain); Chris@16: m_totally_free_blocks = 0; Chris@16: } Chris@16: Chris@16: block_info_t *priv_block_from_node(void *node, AlignOnlyFalse) const Chris@16: { Chris@16: hdr_offset_holder *hdr_off_holder = Chris@16: reinterpret_cast((std::size_t)node & size_type(~(m_real_block_alignment - 1))); Chris@16: BOOST_ASSERT(0 == ((std::size_t)hdr_off_holder & (m_real_block_alignment - 1))); Chris@16: BOOST_ASSERT(0 == (hdr_off_holder->hdr_offset & (m_real_block_alignment - 1))); Chris@16: block_info_t *block = reinterpret_cast Chris@16: (reinterpret_cast(hdr_off_holder) + hdr_off_holder->hdr_offset); Chris@16: BOOST_ASSERT(block->hdr_offset == 0); Chris@16: return block; Chris@16: } Chris@16: Chris@16: block_info_t *priv_block_from_node(void *node, AlignOnlyTrue) const Chris@16: { Chris@16: return (block_info_t *)((std::size_t)node & std::size_t(~(m_real_block_alignment - 1))); Chris@16: } Chris@16: Chris@16: block_info_t *priv_block_from_node(void *node) const Chris@16: { return this->priv_block_from_node(node, IsAlignOnly()); } Chris@16: Chris@16: hdr_offset_holder *priv_first_subblock_from_block(block_info_t *block) const Chris@16: { return this->priv_first_subblock_from_block(block, IsAlignOnly()); } Chris@16: Chris@16: hdr_offset_holder *priv_first_subblock_from_block(block_info_t *block, AlignOnlyFalse) const Chris@16: { Chris@16: hdr_offset_holder *const hdr_off_holder = reinterpret_cast Chris@16: (reinterpret_cast(block) - (m_num_subblocks-1)*m_real_block_alignment); Chris@16: BOOST_ASSERT(hdr_off_holder->hdr_offset == size_type(reinterpret_cast(block) - reinterpret_cast(hdr_off_holder))); Chris@16: BOOST_ASSERT(0 == ((std::size_t)hdr_off_holder & (m_real_block_alignment - 1))); Chris@16: BOOST_ASSERT(0 == (hdr_off_holder->hdr_offset & (m_real_block_alignment - 1))); Chris@16: return hdr_off_holder; Chris@16: } Chris@16: Chris@16: hdr_offset_holder *priv_first_subblock_from_block(block_info_t *block, AlignOnlyTrue) const Chris@16: { Chris@16: return reinterpret_cast(block); Chris@16: } Chris@16: Chris@16: void priv_dispatch_block_chain_or_free Chris@16: ( multiallocation_chain &chain, block_info_t &c_info, size_type num_node Chris@16: , char *mem_address, size_type total_elements, bool insert_block_if_free) Chris@16: { Chris@16: BOOST_ASSERT(chain.size() <= total_elements); Chris@16: //First add all possible nodes to the chain Chris@16: const size_type left = total_elements - chain.size(); Chris@16: const size_type max_chain = (num_node < left) ? num_node : left; Chris@16: mem_address = static_cast(container_detail::to_raw_pointer Chris@16: (chain.incorporate_after(chain.last(), void_pointer(mem_address), m_real_node_size, max_chain))); Chris@16: //Now store remaining nodes in the free list Chris@16: if(const size_type max_free = num_node - max_chain){ Chris@16: free_nodes_t & free_nodes = c_info.free_nodes; Chris@16: free_nodes.incorporate_after(free_nodes.last(), void_pointer(mem_address), m_real_node_size, max_free); Chris@16: if(insert_block_if_free){ Chris@16: m_block_container.push_front(c_info); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: //!Allocates a several blocks of nodes. Can throw Chris@16: void priv_append_from_new_blocks(size_type min_elements, multiallocation_chain &chain, AlignOnlyTrue) Chris@16: { Chris@16: BOOST_ASSERT(m_block_container.empty()); Chris@16: BOOST_ASSERT(min_elements > 0); Chris@16: const size_type n = (min_elements - 1)/m_real_num_node + 1; Chris@16: const size_type real_block_size = m_real_block_alignment - PayloadPerAllocation; Chris@16: const size_type total_elements = chain.size() + min_elements; Chris@16: for(size_type i = 0; i != n; ++i){ Chris@16: //We allocate a new NodeBlock and put it the last Chris@16: //element of the tree Chris@16: char *mem_address = static_cast Chris@16: (mp_segment_mngr_base->allocate_aligned(real_block_size, m_real_block_alignment)); Chris@16: if(!mem_address){ Chris@16: //In case of error, free memory deallocating all nodes (the new ones allocated Chris@16: //in this function plus previously stored nodes in chain). Chris@16: this->deallocate_nodes(chain); Chris@16: throw_bad_alloc(); Chris@16: } Chris@16: block_info_t &c_info = *new(mem_address)block_info_t(); Chris@16: mem_address += HdrSize; Chris@16: if(i != (n-1)){ Chris@16: chain.incorporate_after(chain.last(), void_pointer(mem_address), m_real_node_size, m_real_num_node); Chris@16: } Chris@16: else{ Chris@16: this->priv_dispatch_block_chain_or_free(chain, c_info, m_real_num_node, mem_address, total_elements, true); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: void priv_append_from_new_blocks(size_type min_elements, multiallocation_chain &chain, AlignOnlyFalse) Chris@16: { Chris@16: BOOST_ASSERT(m_block_container.empty()); Chris@16: BOOST_ASSERT(min_elements > 0); Chris@16: const size_type n = (min_elements - 1)/m_real_num_node + 1; Chris@16: const size_type real_block_size = m_real_block_alignment*m_num_subblocks - PayloadPerAllocation; Chris@16: const size_type elements_per_subblock = (m_real_block_alignment - HdrOffsetSize)/m_real_node_size; Chris@16: const size_type hdr_subblock_elements = (m_real_block_alignment - HdrSize - PayloadPerAllocation)/m_real_node_size; Chris@16: const size_type total_elements = chain.size() + min_elements; Chris@16: Chris@16: for(size_type i = 0; i != n; ++i){ Chris@16: //We allocate a new NodeBlock and put it the last Chris@16: //element of the tree Chris@16: char *mem_address = static_cast Chris@16: (mp_segment_mngr_base->allocate_aligned(real_block_size, m_real_block_alignment)); Chris@16: if(!mem_address){ Chris@16: //In case of error, free memory deallocating all nodes (the new ones allocated Chris@16: //in this function plus previously stored nodes in chain). Chris@16: this->deallocate_nodes(chain); Chris@16: throw_bad_alloc(); Chris@16: } Chris@16: //First initialize header information on the last subblock Chris@16: char *hdr_addr = mem_address + m_real_block_alignment*(m_num_subblocks-1); Chris@16: block_info_t &c_info = *new(hdr_addr)block_info_t(); Chris@16: //Some structural checks Chris@16: BOOST_ASSERT(static_cast(&static_cast(c_info).hdr_offset) == Chris@16: static_cast(&c_info)); (void)c_info; Chris@16: if(i != (n-1)){ Chris@16: for( size_type subblock = 0, maxsubblock = m_num_subblocks - 1 Chris@16: ; subblock < maxsubblock Chris@16: ; ++subblock, mem_address += m_real_block_alignment){ Chris@16: //Initialize header offset mark Chris@16: new(mem_address) hdr_offset_holder(size_type(hdr_addr - mem_address)); Chris@16: chain.incorporate_after Chris@16: (chain.last(), void_pointer(mem_address + HdrOffsetSize), m_real_node_size, elements_per_subblock); Chris@16: } Chris@16: chain.incorporate_after(chain.last(), void_pointer(hdr_addr + HdrSize), m_real_node_size, hdr_subblock_elements); Chris@16: } Chris@16: else{ Chris@16: for( size_type subblock = 0, maxsubblock = m_num_subblocks - 1 Chris@16: ; subblock < maxsubblock Chris@16: ; ++subblock, mem_address += m_real_block_alignment){ Chris@16: //Initialize header offset mark Chris@16: new(mem_address) hdr_offset_holder(size_type(hdr_addr - mem_address)); Chris@16: this->priv_dispatch_block_chain_or_free Chris@16: (chain, c_info, elements_per_subblock, mem_address + HdrOffsetSize, total_elements, false); Chris@16: } Chris@16: this->priv_dispatch_block_chain_or_free Chris@16: (chain, c_info, hdr_subblock_elements, hdr_addr + HdrSize, total_elements, true); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@16: typedef typename boost::intrusive::pointer_traits Chris@16: ::template rebind_pointer::type segment_mngr_base_ptr_t; Chris@16: const size_type m_max_free_blocks; Chris@16: const size_type m_real_node_size; Chris@16: //Round the size to a power of two value. Chris@16: //This is the total memory size (including payload) that we want to Chris@16: //allocate from the general-purpose allocator Chris@16: const size_type m_real_block_alignment; Chris@16: size_type m_num_subblocks; Chris@16: //This is the real number of nodes per block Chris@16: //const Chris@16: size_type m_real_num_node; Chris@16: segment_mngr_base_ptr_t mp_segment_mngr_base; //Segment manager Chris@16: block_container_t m_block_container; //Intrusive block list Chris@16: size_type m_totally_free_blocks; //Free blocks Chris@16: }; Chris@16: Chris@16: } //namespace container_detail { Chris@16: } //namespace container { Chris@16: } //namespace boost { Chris@16: Chris@16: #include Chris@16: Chris@16: #endif //#ifndef BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP