node_allocator.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2008-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_POOLED_NODE_ALLOCATOR_HPP
  11. #define BOOST_CONTAINER_POOLED_NODE_ALLOCATOR_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/throw_exception.hpp>
  22. #include <boost/container/detail/node_pool.hpp>
  23. #include <boost/container/detail/mpl.hpp>
  24. #include <boost/container/detail/multiallocation_chain.hpp>
  25. #include <boost/container/detail/dlmalloc.hpp>
  26. #include <boost/container/detail/singleton.hpp>
  27. #include <boost/assert.hpp>
  28. #include <boost/static_assert.hpp>
  29. #include <cstddef>
  30. namespace boost {
  31. namespace container {
  32. //!An STL node allocator that uses a modified DlMalloc as memory
  33. //!source.
  34. //!
  35. //!This node allocator shares a segregated storage between all instances
  36. //!of node_allocator with equal sizeof(T).
  37. //!
  38. //!NodesPerBlock is the number of nodes allocated at once when the allocator
  39. //!runs out of nodes
  40. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
  41. template
  42. < class T
  43. , std::size_t NodesPerBlock = NodeAlloc_nodes_per_block>
  44. #else
  45. template
  46. < class T
  47. , std::size_t NodesPerBlock
  48. , std::size_t Version>
  49. #endif
  50. class node_allocator
  51. {
  52. #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  53. //! If Version is 1, the allocator is a STL conforming allocator. If Version is 2,
  54. //! the allocator offers advanced expand in place and burst allocation capabilities.
  55. public:
  56. typedef unsigned int allocation_type;
  57. typedef node_allocator<T, NodesPerBlock, Version> self_t;
  58. static const std::size_t nodes_per_block = NodesPerBlock;
  59. BOOST_STATIC_ASSERT((Version <=2));
  60. #endif
  61. public:
  62. //-------
  63. typedef T value_type;
  64. typedef T * pointer;
  65. typedef const T * const_pointer;
  66. typedef typename ::boost::container::
  67. dtl::unvoid_ref<T>::type reference;
  68. typedef typename ::boost::container::
  69. dtl::unvoid_ref<const T>::type const_reference;
  70. typedef std::size_t size_type;
  71. typedef std::ptrdiff_t difference_type;
  72. typedef boost::container::dtl::
  73. version_type<self_t, Version> version;
  74. #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  75. typedef boost::container::dtl::
  76. basic_multiallocation_chain<void*> multiallocation_chain_void;
  77. typedef boost::container::dtl::
  78. transform_multiallocation_chain
  79. <multiallocation_chain_void, T> multiallocation_chain;
  80. #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  81. //!Obtains node_allocator from
  82. //!node_allocator
  83. template<class T2>
  84. struct rebind
  85. {
  86. typedef node_allocator< T2, NodesPerBlock
  87. #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  88. , Version
  89. #endif
  90. > other;
  91. };
  92. #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  93. private:
  94. //!Not assignable from related node_allocator
  95. template<class T2, std::size_t N2>
  96. node_allocator& operator=
  97. (const node_allocator<T2, N2>&);
  98. #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  99. public:
  100. //!Default constructor
  101. node_allocator() BOOST_NOEXCEPT_OR_NOTHROW
  102. {}
  103. //!Copy constructor from other node_allocator.
  104. node_allocator(const node_allocator &) BOOST_NOEXCEPT_OR_NOTHROW
  105. {}
  106. //!Copy constructor from related node_allocator.
  107. template<class T2>
  108. node_allocator
  109. (const node_allocator<T2, NodesPerBlock
  110. #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
  111. , Version
  112. #endif
  113. > &) BOOST_NOEXCEPT_OR_NOTHROW
  114. {}
  115. //!Destructor
  116. ~node_allocator() BOOST_NOEXCEPT_OR_NOTHROW
  117. {}
  118. //!Returns the number of elements that could be allocated.
  119. //!Never throws
  120. size_type max_size() const
  121. { return size_type(-1)/sizeof(T); }
  122. //!Allocate memory for an array of count elements.
  123. //!Throws std::bad_alloc if there is no enough memory
  124. pointer allocate(size_type count, const void * = 0)
  125. {
  126. if(BOOST_UNLIKELY(count > this->max_size()))
  127. boost::container::throw_bad_alloc();
  128. if(Version == 1 && count == 1){
  129. typedef dtl::shared_node_pool
  130. <sizeof(T), NodesPerBlock> shared_pool_t;
  131. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  132. return pointer(static_cast<T*>(singleton_t::instance().allocate_node()));
  133. }
  134. else{
  135. void *ret = dlmalloc_malloc(count*sizeof(T));
  136. if(BOOST_UNLIKELY(!ret))
  137. boost::container::throw_bad_alloc();
  138. return static_cast<pointer>(ret);
  139. }
  140. }
  141. //!Deallocate allocated memory.
  142. //!Never throws
  143. void deallocate(const pointer &ptr, size_type count) BOOST_NOEXCEPT_OR_NOTHROW
  144. {
  145. (void)count;
  146. if(Version == 1 && count == 1){
  147. typedef dtl::shared_node_pool
  148. <sizeof(T), NodesPerBlock> shared_pool_t;
  149. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  150. singleton_t::instance().deallocate_node(ptr);
  151. }
  152. else{
  153. dlmalloc_free(ptr);
  154. }
  155. }
  156. //!Deallocates all free blocks of the pool
  157. static void deallocate_free_blocks() BOOST_NOEXCEPT_OR_NOTHROW
  158. {
  159. typedef dtl::shared_node_pool
  160. <sizeof(T), NodesPerBlock> shared_pool_t;
  161. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  162. singleton_t::instance().deallocate_free_blocks();
  163. }
  164. pointer allocation_command
  165. (allocation_type command, size_type limit_size, size_type &prefer_in_recvd_out_size, pointer &reuse)
  166. {
  167. BOOST_STATIC_ASSERT(( Version > 1 ));
  168. pointer ret = this->priv_allocation_command(command, limit_size, prefer_in_recvd_out_size, reuse);
  169. if(BOOST_UNLIKELY(!ret && !(command & BOOST_CONTAINER_NOTHROW_ALLOCATION)))
  170. boost::container::throw_bad_alloc();
  171. return ret;
  172. }
  173. //!Returns maximum the number of objects the previously allocated memory
  174. //!pointed by p can hold.
  175. size_type size(pointer p) const BOOST_NOEXCEPT_OR_NOTHROW
  176. {
  177. BOOST_STATIC_ASSERT(( Version > 1 ));
  178. return dlmalloc_size(p);
  179. }
  180. //!Allocates just one object. Memory allocated with this function
  181. //!must be deallocated only with deallocate_one().
  182. //!Throws bad_alloc if there is no enough memory
  183. pointer allocate_one()
  184. {
  185. BOOST_STATIC_ASSERT(( Version > 1 ));
  186. typedef dtl::shared_node_pool
  187. <sizeof(T), NodesPerBlock> shared_pool_t;
  188. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  189. return (pointer)singleton_t::instance().allocate_node();
  190. }
  191. //!Allocates many elements of size == 1.
  192. //!Elements must be individually deallocated with deallocate_one()
  193. void allocate_individual(std::size_t num_elements, multiallocation_chain &chain)
  194. {
  195. BOOST_STATIC_ASSERT(( Version > 1 ));
  196. typedef dtl::shared_node_pool
  197. <sizeof(T), NodesPerBlock> shared_pool_t;
  198. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  199. typename shared_pool_t::multiallocation_chain ch;
  200. singleton_t::instance().allocate_nodes(num_elements, ch);
  201. chain.incorporate_after(chain.before_begin(), (T*)&*ch.begin(), (T*)&*ch.last(), ch.size());
  202. }
  203. //!Deallocates memory previously allocated with allocate_one().
  204. //!You should never use deallocate_one to deallocate memory allocated
  205. //!with other functions different from allocate_one(). Never throws
  206. void deallocate_one(pointer p) BOOST_NOEXCEPT_OR_NOTHROW
  207. {
  208. BOOST_STATIC_ASSERT(( Version > 1 ));
  209. typedef dtl::shared_node_pool
  210. <sizeof(T), NodesPerBlock> shared_pool_t;
  211. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  212. singleton_t::instance().deallocate_node(p);
  213. }
  214. void deallocate_individual(multiallocation_chain &chain) BOOST_NOEXCEPT_OR_NOTHROW
  215. {
  216. BOOST_STATIC_ASSERT(( Version > 1 ));
  217. typedef dtl::shared_node_pool
  218. <sizeof(T), NodesPerBlock> shared_pool_t;
  219. typedef dtl::singleton_default<shared_pool_t> singleton_t;
  220. typename shared_pool_t::multiallocation_chain ch(&*chain.begin(), &*chain.last(), chain.size());
  221. singleton_t::instance().deallocate_nodes(ch);
  222. }
  223. //!Allocates many elements of size elem_size.
  224. //!Elements must be individually deallocated with deallocate()
  225. void allocate_many(size_type elem_size, std::size_t n_elements, multiallocation_chain &chain)
  226. {
  227. BOOST_STATIC_ASSERT(( Version > 1 ));
  228. dlmalloc_memchain ch;
  229. BOOST_CONTAINER_MEMCHAIN_INIT(&ch);
  230. if(BOOST_UNLIKELY(!dlmalloc_multialloc_nodes(n_elements, elem_size*sizeof(T), BOOST_CONTAINER_DL_MULTIALLOC_DEFAULT_CONTIGUOUS, &ch))){
  231. boost::container::throw_bad_alloc();
  232. }
  233. chain.incorporate_after( chain.before_begin()
  234. , (T*)BOOST_CONTAINER_MEMCHAIN_LASTMEM(&ch)
  235. , (T*)BOOST_CONTAINER_MEMCHAIN_LASTMEM(&ch)
  236. , BOOST_CONTAINER_MEMCHAIN_SIZE(&ch));
  237. }
  238. //!Allocates n_elements elements, each one of size elem_sizes[i]
  239. //!Elements must be individually deallocated with deallocate()
  240. void allocate_many(const size_type *elem_sizes, size_type n_elements, multiallocation_chain &chain)
  241. {
  242. BOOST_STATIC_ASSERT(( Version > 1 ));
  243. dlmalloc_memchain ch;
  244. dlmalloc_multialloc_arrays(n_elements, elem_sizes, sizeof(T), BOOST_CONTAINER_DL_MULTIALLOC_DEFAULT_CONTIGUOUS, &ch);
  245. if(BOOST_UNLIKELY(BOOST_CONTAINER_MEMCHAIN_EMPTY(&ch))){
  246. boost::container::throw_bad_alloc();
  247. }
  248. chain.incorporate_after( chain.before_begin()
  249. , (T*)BOOST_CONTAINER_MEMCHAIN_LASTMEM(&ch)
  250. , (T*)BOOST_CONTAINER_MEMCHAIN_LASTMEM(&ch)
  251. , BOOST_CONTAINER_MEMCHAIN_SIZE(&ch));
  252. }
  253. void deallocate_many(multiallocation_chain &chain) BOOST_NOEXCEPT_OR_NOTHROW
  254. {
  255. BOOST_STATIC_ASSERT(( Version > 1 ));
  256. void *first = &*chain.begin();
  257. void *last = &*chain.last();
  258. size_t num = chain.size();
  259. dlmalloc_memchain ch;
  260. BOOST_CONTAINER_MEMCHAIN_INIT_FROM(&ch, first, last, num);
  261. dlmalloc_multidealloc(&ch);
  262. }
  263. //!Swaps allocators. Does not throw. If each allocator is placed in a
  264. //!different memory segment, the result is undefined.
  265. friend void swap(self_t &, self_t &) BOOST_NOEXCEPT_OR_NOTHROW
  266. {}
  267. //!An allocator always compares to true, as memory allocated with one
  268. //!instance can be deallocated by another instance
  269. friend bool operator==(const node_allocator &, const node_allocator &) BOOST_NOEXCEPT_OR_NOTHROW
  270. { return true; }
  271. //!An allocator always compares to false, as memory allocated with one
  272. //!instance can be deallocated by another instance
  273. friend bool operator!=(const node_allocator &, const node_allocator &) BOOST_NOEXCEPT_OR_NOTHROW
  274. { return false; }
  275. private:
  276. pointer priv_allocation_command
  277. (allocation_type command, std::size_t limit_size
  278. ,size_type &prefer_in_recvd_out_size
  279. ,pointer &reuse)
  280. {
  281. std::size_t const preferred_size = prefer_in_recvd_out_size;
  282. dlmalloc_command_ret_t ret = {0 , 0};
  283. if((limit_size > this->max_size()) | (preferred_size > this->max_size())){
  284. return pointer();
  285. }
  286. std::size_t l_size = limit_size*sizeof(T);
  287. std::size_t p_size = preferred_size*sizeof(T);
  288. std::size_t r_size;
  289. {
  290. void* reuse_ptr_void = reuse;
  291. ret = dlmalloc_allocation_command(command, sizeof(T), l_size, p_size, &r_size, reuse_ptr_void);
  292. reuse = static_cast<T*>(reuse_ptr_void);
  293. }
  294. prefer_in_recvd_out_size = r_size/sizeof(T);
  295. return (pointer)ret.first;
  296. }
  297. };
  298. } //namespace container {
  299. } //namespace boost {
  300. #include <boost/container/detail/config_end.hpp>
  301. #endif //#ifndef BOOST_CONTAINER_POOLED_NODE_ALLOCATOR_HPP