node_pool_impl.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2005-2013. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // See http://www.boost.org/libs/container for documentation.
  8. //
  9. //////////////////////////////////////////////////////////////////////////////
  10. #ifndef BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP
  11. #define BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP
  12. #ifndef BOOST_CONFIG_HPP
  13. # include <boost/config.hpp>
  14. #endif
  15. #if defined(BOOST_HAS_PRAGMA_ONCE)
  16. # pragma once
  17. #endif
  18. #include <boost/container/detail/config_begin.hpp>
  19. #include <boost/container/detail/workaround.hpp>
  20. #include <boost/container/container_fwd.hpp>
  21. #include <boost/container/detail/math_functions.hpp>
  22. #include <boost/container/detail/mpl.hpp>
  23. #include <boost/container/detail/pool_common.hpp>
  24. #include <boost/move/detail/to_raw_pointer.hpp>
  25. #include <boost/container/detail/type_traits.hpp>
  26. #include <boost/intrusive/pointer_traits.hpp>
  27. #include <boost/intrusive/set.hpp>
  28. #include <boost/intrusive/slist.hpp>
  29. #include <boost/core/no_exceptions_support.hpp>
  30. #include <boost/assert.hpp>
  31. #include <cstddef>
  32. namespace boost {
  33. namespace container {
  34. namespace dtl {
  35. template<class SegmentManagerBase>
  36. class private_node_pool_impl
  37. {
  38. //Non-copyable
  39. private_node_pool_impl();
  40. private_node_pool_impl(const private_node_pool_impl &);
  41. private_node_pool_impl &operator=(const private_node_pool_impl &);
  42. //A node object will hold node_t when it's not allocated
  43. public:
  44. typedef typename SegmentManagerBase::void_pointer void_pointer;
  45. typedef typename node_slist<void_pointer>::slist_hook_t slist_hook_t;
  46. typedef typename node_slist<void_pointer>::node_t node_t;
  47. typedef typename node_slist<void_pointer>::node_slist_t free_nodes_t;
  48. typedef typename SegmentManagerBase::multiallocation_chain multiallocation_chain;
  49. typedef typename SegmentManagerBase::size_type size_type;
  50. private:
  51. typedef typename bi::make_slist
  52. < node_t, bi::base_hook<slist_hook_t>
  53. , bi::linear<true>
  54. , bi::constant_time_size<false> >::type blockslist_t;
  55. static size_type get_rounded_size(size_type orig_size, size_type round_to)
  56. { return ((orig_size-1)/round_to+1)*round_to; }
  57. public:
  58. //!Segment manager typedef
  59. typedef SegmentManagerBase segment_manager_base_type;
  60. //!Constructor from a segment manager. Never throws
  61. private_node_pool_impl(segment_manager_base_type *segment_mngr_base, size_type node_size, size_type nodes_per_block)
  62. : m_nodes_per_block(nodes_per_block)
  63. , m_real_node_size(lcm(node_size, size_type(alignment_of<node_t>::value)))
  64. //General purpose allocator
  65. , mp_segment_mngr_base(segment_mngr_base)
  66. , m_blocklist()
  67. , m_freelist()
  68. //Debug node count
  69. , m_allocated(0)
  70. {}
  71. //!Destructor. Deallocates all allocated blocks. Never throws
  72. ~private_node_pool_impl()
  73. { this->purge_blocks(); }
  74. size_type get_real_num_node() const
  75. { return m_nodes_per_block; }
  76. //!Returns the segment manager. Never throws
  77. segment_manager_base_type* get_segment_manager_base()const
  78. { return boost::movelib::to_raw_pointer(mp_segment_mngr_base); }
  79. void *allocate_node()
  80. { return this->priv_alloc_node(); }
  81. //!Deallocates an array pointed by ptr. Never throws
  82. void deallocate_node(void *ptr)
  83. { this->priv_dealloc_node(ptr); }
  84. //!Allocates a singly linked list of n nodes ending in null pointer.
  85. void allocate_nodes(const size_type n, multiallocation_chain &chain)
  86. {
  87. //Preallocate all needed blocks to fulfill the request
  88. size_type cur_nodes = m_freelist.size();
  89. if(cur_nodes < n){
  90. this->priv_alloc_block(((n - cur_nodes) - 1)/m_nodes_per_block + 1);
  91. }
  92. //We just iterate the needed nodes to get the last we'll erase
  93. typedef typename free_nodes_t::iterator free_iterator;
  94. free_iterator before_last_new_it = m_freelist.before_begin();
  95. for(size_type j = 0; j != n; ++j){
  96. ++before_last_new_it;
  97. }
  98. //Cache the first node of the allocated range before erasing
  99. free_iterator first_node(m_freelist.begin());
  100. free_iterator last_node (before_last_new_it);
  101. //Erase the range. Since we already have the distance, this is O(1)
  102. m_freelist.erase_after( m_freelist.before_begin()
  103. , ++free_iterator(before_last_new_it)
  104. , n);
  105. //Now take the last erased node and just splice it in the end
  106. //of the intrusive list that will be traversed by the multialloc iterator.
  107. chain.incorporate_after(chain.before_begin(), &*first_node, &*last_node, n);
  108. m_allocated += n;
  109. }
  110. void deallocate_nodes(multiallocation_chain &chain)
  111. {
  112. typedef typename multiallocation_chain::iterator iterator;
  113. iterator it(chain.begin()), itend(chain.end());
  114. while(it != itend){
  115. void *pElem = &*it;
  116. ++it;
  117. this->priv_dealloc_node(pElem);
  118. }
  119. }
  120. //!Deallocates all the free blocks of memory. Never throws
  121. void deallocate_free_blocks()
  122. {
  123. typedef typename free_nodes_t::iterator nodelist_iterator;
  124. typename blockslist_t::iterator bit(m_blocklist.before_begin()),
  125. it(m_blocklist.begin()),
  126. itend(m_blocklist.end());
  127. free_nodes_t backup_list;
  128. nodelist_iterator backup_list_last = backup_list.before_begin();
  129. //Execute the algorithm and get an iterator to the last value
  130. size_type blocksize = (get_rounded_size)
  131. (m_real_node_size*m_nodes_per_block, (size_type) alignment_of<node_t>::value);
  132. while(it != itend){
  133. //Collect all the nodes from the block pointed by it
  134. //and push them in the list
  135. free_nodes_t free_nodes;
  136. nodelist_iterator last_it = free_nodes.before_begin();
  137. const void *addr = get_block_from_hook(&*it, blocksize);
  138. m_freelist.remove_and_dispose_if
  139. (is_between(addr, blocksize), push_in_list(free_nodes, last_it));
  140. //If the number of nodes is equal to m_nodes_per_block
  141. //this means that the block can be deallocated
  142. if(free_nodes.size() == m_nodes_per_block){
  143. //Unlink the nodes
  144. free_nodes.clear();
  145. it = m_blocklist.erase_after(bit);
  146. mp_segment_mngr_base->deallocate((void*)addr);
  147. }
  148. //Otherwise, insert them in the backup list, since the
  149. //next "remove_if" does not need to check them again.
  150. else{
  151. //Assign the iterator to the last value if necessary
  152. if(backup_list.empty() && !m_freelist.empty()){
  153. backup_list_last = last_it;
  154. }
  155. //Transfer nodes. This is constant time.
  156. backup_list.splice_after
  157. ( backup_list.before_begin()
  158. , free_nodes
  159. , free_nodes.before_begin()
  160. , last_it
  161. , free_nodes.size());
  162. bit = it;
  163. ++it;
  164. }
  165. }
  166. //We should have removed all the nodes from the free list
  167. BOOST_ASSERT(m_freelist.empty());
  168. //Now pass all the node to the free list again
  169. m_freelist.splice_after
  170. ( m_freelist.before_begin()
  171. , backup_list
  172. , backup_list.before_begin()
  173. , backup_list_last
  174. , backup_list.size());
  175. }
  176. size_type num_free_nodes()
  177. { return m_freelist.size(); }
  178. //!Deallocates all used memory. Precondition: all nodes allocated from this pool should
  179. //!already be deallocated. Otherwise, undefined behaviour. Never throws
  180. void purge_blocks()
  181. {
  182. //check for memory leaks
  183. BOOST_ASSERT(m_allocated==0);
  184. size_type blocksize = (get_rounded_size)
  185. (m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value);
  186. //We iterate though the NodeBlock list to free the memory
  187. while(!m_blocklist.empty()){
  188. void *addr = get_block_from_hook(&m_blocklist.front(), blocksize);
  189. m_blocklist.pop_front();
  190. mp_segment_mngr_base->deallocate((void*)addr);
  191. }
  192. //Just clear free node list
  193. m_freelist.clear();
  194. }
  195. void swap(private_node_pool_impl &other)
  196. {
  197. BOOST_ASSERT(m_nodes_per_block == other.m_nodes_per_block);
  198. BOOST_ASSERT(m_real_node_size == other.m_real_node_size);
  199. std::swap(mp_segment_mngr_base, other.mp_segment_mngr_base);
  200. m_blocklist.swap(other.m_blocklist);
  201. m_freelist.swap(other.m_freelist);
  202. std::swap(m_allocated, other.m_allocated);
  203. }
  204. private:
  205. struct push_in_list
  206. {
  207. push_in_list(free_nodes_t &l, typename free_nodes_t::iterator &it)
  208. : slist_(l), last_it_(it)
  209. {}
  210. void operator()(typename free_nodes_t::pointer p) const
  211. {
  212. slist_.push_front(*p);
  213. if(slist_.size() == 1){ //Cache last element
  214. ++last_it_ = slist_.begin();
  215. }
  216. }
  217. private:
  218. free_nodes_t &slist_;
  219. typename free_nodes_t::iterator &last_it_;
  220. };
  221. struct is_between
  222. {
  223. typedef typename free_nodes_t::value_type argument_type;
  224. typedef bool result_type;
  225. is_between(const void *addr, std::size_t size)
  226. : beg_(static_cast<const char *>(addr)), end_(beg_+size)
  227. {}
  228. bool operator()(typename free_nodes_t::const_reference v) const
  229. {
  230. return (beg_ <= reinterpret_cast<const char *>(&v) &&
  231. end_ > reinterpret_cast<const char *>(&v));
  232. }
  233. private:
  234. const char * beg_;
  235. const char * end_;
  236. };
  237. //!Allocates one node, using single segregated storage algorithm.
  238. //!Never throws
  239. node_t *priv_alloc_node()
  240. {
  241. //If there are no free nodes we allocate a new block
  242. if (m_freelist.empty())
  243. this->priv_alloc_block(1);
  244. //We take the first free node
  245. node_t *n = (node_t*)&m_freelist.front();
  246. m_freelist.pop_front();
  247. ++m_allocated;
  248. return n;
  249. }
  250. //!Deallocates one node, using single segregated storage algorithm.
  251. //!Never throws
  252. void priv_dealloc_node(void *pElem)
  253. {
  254. //We put the node at the beginning of the free node list
  255. node_t * to_deallocate = static_cast<node_t*>(pElem);
  256. m_freelist.push_front(*to_deallocate);
  257. BOOST_ASSERT(m_allocated>0);
  258. --m_allocated;
  259. }
  260. //!Allocates several blocks of nodes. Can throw
  261. void priv_alloc_block(size_type num_blocks)
  262. {
  263. BOOST_ASSERT(num_blocks > 0);
  264. size_type blocksize =
  265. (get_rounded_size)(m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value);
  266. BOOST_TRY{
  267. for(size_type i = 0; i != num_blocks; ++i){
  268. //We allocate a new NodeBlock and put it as first
  269. //element in the free Node list
  270. char *pNode = reinterpret_cast<char*>
  271. (mp_segment_mngr_base->allocate(blocksize + sizeof(node_t)));
  272. char *pBlock = pNode;
  273. m_blocklist.push_front(get_block_hook(pBlock, blocksize));
  274. //We initialize all Nodes in Node Block to insert
  275. //them in the free Node list
  276. for(size_type j = 0; j < m_nodes_per_block; ++j, pNode += m_real_node_size){
  277. m_freelist.push_front(*new (pNode) node_t);
  278. }
  279. }
  280. }
  281. BOOST_CATCH(...){
  282. //to-do: if possible, an efficient way to deallocate allocated blocks
  283. BOOST_RETHROW
  284. }
  285. BOOST_CATCH_END
  286. }
  287. //!Deprecated, use deallocate_free_blocks
  288. void deallocate_free_chunks()
  289. { this->deallocate_free_blocks(); }
  290. //!Deprecated, use purge_blocks
  291. void purge_chunks()
  292. { this->purge_blocks(); }
  293. private:
  294. //!Returns a reference to the block hook placed in the end of the block
  295. static node_t & get_block_hook (void *block, size_type blocksize)
  296. {
  297. return *reinterpret_cast<node_t*>(reinterpret_cast<char*>(block) + blocksize);
  298. }
  299. //!Returns the starting address of the block reference to the block hook placed in the end of the block
  300. void *get_block_from_hook (node_t *hook, size_type blocksize)
  301. {
  302. return (reinterpret_cast<char*>(hook) - blocksize);
  303. }
  304. private:
  305. typedef typename boost::intrusive::pointer_traits
  306. <void_pointer>::template rebind_pointer<segment_manager_base_type>::type segment_mngr_base_ptr_t;
  307. const size_type m_nodes_per_block;
  308. const size_type m_real_node_size;
  309. segment_mngr_base_ptr_t mp_segment_mngr_base; //Segment manager
  310. blockslist_t m_blocklist; //Intrusive container of blocks
  311. free_nodes_t m_freelist; //Intrusive container of free nods
  312. size_type m_allocated; //Used nodes for debugging
  313. };
  314. } //namespace dtl {
  315. } //namespace container {
  316. } //namespace boost {
  317. #include <boost/container/detail/config_end.hpp>
  318. #endif //#ifndef BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP