node_pool_impl.hpp 13 KB

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