adaptive_node_pool.hpp
来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 655 行 · 第 1/2 页
HPP
655 行
////////////////////////////////////////////////////////////////////////////////// (C) Copyright Ion Gaztanaga 2005-2008. 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_DETAIL_ADAPTIVE_NODE_POOL_HPP#define BOOST_INTERPROCESS_DETAIL_ADAPTIVE_NODE_POOL_HPP#if (defined _MSC_VER) && (_MSC_VER >= 1200)# pragma once#endif#include <boost/interprocess/detail/config_begin.hpp>#include <boost/interprocess/detail/workaround.hpp>#include <boost/interprocess/interprocess_fwd.hpp>#include <boost/interprocess/sync/interprocess_mutex.hpp>#include <boost/interprocess/detail/utilities.hpp>#include <boost/interprocess/detail/min_max.hpp>#include <boost/interprocess/detail/math_functions.hpp>#include <boost/interprocess/exceptions.hpp>#include <boost/intrusive/set.hpp>#include <boost/intrusive/slist.hpp>#include <boost/math/common_factor_ct.hpp>#include <boost/interprocess/detail/type_traits.hpp>#include <boost/interprocess/allocators/detail/node_tools.hpp>#include <boost/interprocess/allocators/detail/allocator_common.hpp>#include <cstddef>#include <boost/config/no_tr1/cmath.hpp>#include <cassert>//!\file//!Describes the real adaptive pool shared by many Interprocess pool allocatorsnamespace boost {namespace interprocess {namespace detail {template<class SegmentManagerBase>class private_adaptive_node_pool_impl{ //Non-copyable private_adaptive_node_pool_impl(); private_adaptive_node_pool_impl(const private_adaptive_node_pool_impl &); private_adaptive_node_pool_impl &operator=(const private_adaptive_node_pool_impl &); typedef typename SegmentManagerBase::void_pointer void_pointer; static const std::size_t PayloadPerAllocation = SegmentManagerBase::PayloadPerAllocation; public: typedef typename node_slist<void_pointer>::node_t node_t; typedef typename node_slist<void_pointer>::node_slist_t free_nodes_t; typedef typename SegmentManagerBase::multiallocation_iterator multiallocation_iterator; typedef typename SegmentManagerBase::multiallocation_chain multiallocation_chain; private: typedef typename bi::make_set_base_hook < bi::void_pointer<void_pointer> , bi::optimize_size<true> , bi::constant_time_size<false> , bi::link_mode<bi::normal_link> >::type multiset_hook_t; struct hdr_offset_holder { hdr_offset_holder(std::size_t offset = 0) : hdr_offset(offset) {} std::size_t hdr_offset; }; struct block_info_t : public hdr_offset_holder, public multiset_hook_t { //An intrusive list of free node from this block free_nodes_t free_nodes; friend bool operator <(const block_info_t &l, const block_info_t &r) {// { return l.free_nodes.size() < r.free_nodes.size(); } //Let's order blocks first by free nodes and then by address //so that highest address fully free blocks are deallocated. //This improves returning memory to the OS (trimming). const bool is_less = l.free_nodes.size() < r.free_nodes.size(); const bool is_equal = l.free_nodes.size() == r.free_nodes.size(); return is_less || (is_equal && (&l < &r)); } }; typedef typename bi::make_multiset <block_info_t, bi::base_hook<multiset_hook_t> >::type block_multiset_t; typedef typename block_multiset_t::iterator block_iterator; static const std::size_t MaxAlign = alignment_of<node_t>::value; static const std::size_t HdrSize = ((sizeof(block_info_t)-1)/MaxAlign+1)*MaxAlign; static const std::size_t HdrOffsetSize = ((sizeof(hdr_offset_holder)-1)/MaxAlign+1)*MaxAlign; static std::size_t calculate_alignment (std::size_t overhead_percent, std::size_t real_node_size) { //to-do: handle real_node_size != node_size const std::size_t divisor = overhead_percent*real_node_size; const std::size_t dividend = HdrOffsetSize*100; std::size_t elements_per_subblock = (dividend - 1)/divisor + 1; std::size_t candidate_power_of_2 = upper_power_of_2(elements_per_subblock*real_node_size + HdrOffsetSize); bool overhead_satisfied = false; //Now calculate the wors-case overhead for a subblock const std::size_t max_subblock_overhead = HdrSize + PayloadPerAllocation; while(!overhead_satisfied){ elements_per_subblock = (candidate_power_of_2 - max_subblock_overhead)/real_node_size; const std::size_t overhead_size = candidate_power_of_2 - elements_per_subblock*real_node_size; if(overhead_size*100/candidate_power_of_2 < overhead_percent){ overhead_satisfied = true; } else{ candidate_power_of_2 <<= 1; } } return candidate_power_of_2; } static void calculate_num_subblocks (std::size_t alignment, std::size_t real_node_size, std::size_t elements_per_block ,std::size_t &num_subblocks, std::size_t &real_num_node, std::size_t overhead_percent) { std::size_t elements_per_subblock = (alignment - HdrOffsetSize)/real_node_size; std::size_t possible_num_subblock = (elements_per_block - 1)/elements_per_subblock + 1; std::size_t hdr_subblock_elements = (alignment - HdrSize - PayloadPerAllocation)/real_node_size; while(((possible_num_subblock-1)*elements_per_subblock + hdr_subblock_elements) < elements_per_block){ ++possible_num_subblock; } elements_per_subblock = (alignment - HdrOffsetSize)/real_node_size; bool overhead_satisfied = false; while(!overhead_satisfied){ const std::size_t total_data = (elements_per_subblock*(possible_num_subblock-1) + hdr_subblock_elements)*real_node_size; const std::size_t total_size = alignment*possible_num_subblock; if((total_size - total_data)*100/total_size < overhead_percent){ overhead_satisfied = true; } else{ ++possible_num_subblock; } } num_subblocks = possible_num_subblock; real_num_node = (possible_num_subblock-1)*elements_per_subblock + hdr_subblock_elements; } public: //!Segment manager typedef typedef SegmentManagerBase segment_manager_base_type; //!Constructor from a segment manager. Never throws private_adaptive_node_pool_impl ( segment_manager_base_type *segment_mngr_base, std::size_t node_size , std::size_t nodes_per_block, std::size_t max_free_blocks , unsigned char overhead_percent ) : m_max_free_blocks(max_free_blocks) , m_real_node_size(lcm(node_size, std::size_t(alignment_of<node_t>::value))) //Round the size to a power of two value. //This is the total memory size (including payload) that we want to //allocate from the general-purpose allocator , m_real_block_alignment(calculate_alignment(overhead_percent, m_real_node_size)) //This is the real number of nodes per block , m_num_subblocks(0) , m_real_num_node(0) //General purpose allocator , mp_segment_mngr_base(segment_mngr_base) , m_block_multiset() , m_totally_free_blocks(0) { calculate_num_subblocks(m_real_block_alignment, m_real_node_size, nodes_per_block, m_num_subblocks, m_real_num_node, overhead_percent); } //!Destructor. Deallocates all allocated blocks. Never throws ~private_adaptive_node_pool_impl() { priv_clear(); } std::size_t get_real_num_node() const { return m_real_num_node; } //!Returns the segment manager. Never throws segment_manager_base_type* get_segment_manager_base()const { return detail::get_pointer(mp_segment_mngr_base); } //!Allocates array of count elements. Can throw boost::interprocess::bad_alloc void *allocate_node() { priv_invariants(); //If there are no free nodes we allocate a new block if (m_block_multiset.empty()){ priv_alloc_block(1); } //We take the first free node the multiset can't be empty return priv_take_first_node(); } //!Deallocates an array pointed by ptr. Never throws void deallocate_node(void *pElem) { this->priv_reinsert_nodes_in_block (multiallocation_iterator::create_simple_range(pElem)); //Update free block count if(m_totally_free_blocks > m_max_free_blocks){ this->priv_deallocate_free_blocks(m_max_free_blocks); } priv_invariants(); } //!Allocates a singly linked list of n nodes ending in null pointer. //!can throw boost::interprocess::bad_alloc void allocate_nodes(multiallocation_chain &nodes, const std::size_t n) { try{ priv_invariants(); std::size_t i = 0; while(i != n){ //If there are no free nodes we allocate all needed blocks if (m_block_multiset.empty()){ priv_alloc_block(((n - i) - 1)/m_real_num_node + 1); } free_nodes_t &free_nodes = m_block_multiset.begin()->free_nodes; const std::size_t free_nodes_count_before = free_nodes.size(); if(free_nodes_count_before == m_real_num_node){ --m_totally_free_blocks; } const std::size_t num_elems = ((n-i) < free_nodes_count_before) ? (n-i) : free_nodes_count_before; for(std::size_t j = 0; j != num_elems; ++j){ void *new_node = &free_nodes.front(); free_nodes.pop_front(); nodes.push_back(new_node); } if(free_nodes.empty()){ m_block_multiset.erase(m_block_multiset.begin()); } i += num_elems; } } catch(...){ this->deallocate_nodes(nodes, nodes.size()); throw; } priv_invariants(); } //!Allocates n nodes, pointed by the multiallocation_iterator. //!Can throw boost::interprocess::bad_alloc multiallocation_iterator allocate_nodes(const std::size_t n) { multiallocation_chain chain; this->allocate_nodes(chain, n); return chain.get_it(); } //!Deallocates a linked list of nodes. Never throws void deallocate_nodes(multiallocation_chain &nodes) { this->deallocate_nodes(nodes.get_it()); nodes.reset(); } //!Deallocates the first n nodes of a linked list of nodes. Never throws void deallocate_nodes(multiallocation_chain &nodes, std::size_t n) { assert(nodes.size() >= n); for(std::size_t i = 0; i < n; ++i){ this->deallocate_node(nodes.pop_front()); } } //!Deallocates the nodes pointed by the multiallocation iterator. Never throws void deallocate_nodes(multiallocation_iterator it) { this->priv_reinsert_nodes_in_block(it); if(m_totally_free_blocks > m_max_free_blocks){ this->priv_deallocate_free_blocks(m_max_free_blocks); } } void deallocate_free_blocks() { this->priv_deallocate_free_blocks(0); } std::size_t num_free_nodes() { typedef typename block_multiset_t::const_iterator citerator; std::size_t count = 0; citerator it (m_block_multiset.begin()), itend(m_block_multiset.end()); for(; it != itend; ++it){ count += it->free_nodes.size(); } return count; } void swap(private_adaptive_node_pool_impl &other) { assert(m_max_free_blocks == other.m_max_free_blocks); assert(m_real_node_size == other.m_real_node_size); assert(m_real_block_alignment == other.m_real_block_alignment); assert(m_real_num_node == other.m_real_num_node); std::swap(mp_segment_mngr_base, other.mp_segment_mngr_base); std::swap(m_totally_free_blocks, other.m_totally_free_blocks); m_block_multiset.swap(other.m_block_multiset); } //Deprecated, use deallocate_free_blocks void deallocate_free_chunks() { this->priv_deallocate_free_blocks(0); } private: void priv_deallocate_free_blocks(std::size_t max_free_blocks) { priv_invariants(); //Now check if we've reached the free nodes limit //and check if we have free blocks. If so, deallocate as much //as we can to stay below the limit for( block_iterator itend = m_block_multiset.end() ; m_totally_free_blocks > max_free_blocks ; --m_totally_free_blocks ){ assert(!m_block_multiset.empty()); block_iterator it = itend; --it; std::size_t num_nodes = it->free_nodes.size(); assert(num_nodes == m_real_num_node); (void)num_nodes;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?