node_allocator.hpp 12 KB

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