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 + -
显示快捷键?