Allocator.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <AnKi/Util/Assert.h>
  7. #include <AnKi/Util/Memory.h>
  8. #include <AnKi/Util/Logger.h>
  9. #include <cstddef> // For ptrdiff_t
  10. #include <utility> // For forward
  11. #include <new> // For placement new
  12. #include <type_traits> // For some checks
  13. namespace anki {
  14. /// @addtogroup util_memory
  15. /// @{
  16. /// Pool based allocator
  17. ///
  18. /// This is a template that accepts memory pools with a specific interface
  19. ///
  20. /// @tparam T The type
  21. ///
  22. /// @note Don't ever EVER remove the double copy constructor and the double operator=. The compiler will create defaults
  23. template<typename T, typename TPool>
  24. class GenericPoolAllocator
  25. {
  26. template<typename, typename>
  27. friend class GenericPoolAllocator;
  28. public:
  29. // Typedefs
  30. using size_type = size_t;
  31. using difference_type = ptrdiff_t;
  32. using pointer = T*;
  33. using const_pointer = const T*;
  34. using reference = T&;
  35. using const_reference = const T&;
  36. using value_type = T;
  37. /// Move assignments between containers will copy the allocator as well. If propagate_on_container_move_assignment
  38. /// is not defined then not moves are going to happen.
  39. using propagate_on_container_move_assignment = std::true_type;
  40. /// A struct to rebind the allocator to another allocator of type Y
  41. template<typename Y>
  42. struct rebind
  43. {
  44. using other = GenericPoolAllocator<Y, TPool>;
  45. };
  46. /// Default constructor
  47. GenericPoolAllocator()
  48. {
  49. }
  50. /// Copy constructor
  51. GenericPoolAllocator(const GenericPoolAllocator& b)
  52. {
  53. *this = b;
  54. }
  55. /// Copy constructor
  56. template<typename Y>
  57. GenericPoolAllocator(const GenericPoolAllocator<Y, TPool>& b)
  58. {
  59. *this = b;
  60. }
  61. /// Copy constructor, uses another type of allocator
  62. template<typename Y, typename YPool>
  63. GenericPoolAllocator(const GenericPoolAllocator<Y, YPool>& b)
  64. {
  65. auto balloc = b;
  66. m_pool = &balloc.getMemoryPool();
  67. m_pool->getRefcount().fetchAdd(1);
  68. }
  69. /// Constuctor that creates a pool
  70. template<typename... TArgs>
  71. GenericPoolAllocator(AllocAlignedCallback allocCb, void* allocCbUserData, TArgs&&... args)
  72. {
  73. m_pool = static_cast<TPool*>(allocCb(allocCbUserData, nullptr, sizeof(TPool), alignof(TPool)));
  74. if(ANKI_UNLIKELY(!m_pool))
  75. {
  76. ANKI_UTIL_LOGF("Out of memory");
  77. }
  78. ::new(m_pool) TPool();
  79. m_pool->init(allocCb, allocCbUserData, std::forward<TArgs>(args)...);
  80. m_pool->getRefcount().store(1);
  81. }
  82. /// Destructor
  83. ~GenericPoolAllocator()
  84. {
  85. clear();
  86. }
  87. /// Copy
  88. GenericPoolAllocator& operator=(const GenericPoolAllocator& b)
  89. {
  90. copy(b);
  91. return *this;
  92. }
  93. /// Copy
  94. template<typename Y>
  95. GenericPoolAllocator& operator=(const GenericPoolAllocator<Y, TPool>& b)
  96. {
  97. copy(b);
  98. return *this;
  99. }
  100. /// Get the address of a reference
  101. pointer address(reference x) const
  102. {
  103. return &x;
  104. }
  105. /// Get the const address of a const reference
  106. const_pointer address(const_reference x) const
  107. {
  108. return &x;
  109. }
  110. /// Allocate memory
  111. /// @param n The elements of type T to allocate
  112. /// @param hint It's been used to override the alignment. The type should be PtrSize.
  113. pointer allocate(size_type n, const void* hint = nullptr)
  114. {
  115. ANKI_ASSERT(m_pool);
  116. (void)hint;
  117. size_type size = n * sizeof(value_type);
  118. // Operator new doesn't respect alignment (in GCC at least) so use the type's alignment. If hint override the
  119. // alignment
  120. PtrSize alignment = (hint != nullptr) ? *static_cast<const PtrSize*>(hint) : alignof(value_type);
  121. void* out = m_pool->allocate(size, alignment);
  122. if(ANKI_UNLIKELY(out == nullptr))
  123. {
  124. ANKI_UTIL_LOGF("Out of memory");
  125. }
  126. return static_cast<pointer>(out);
  127. }
  128. /// Allocate memory
  129. /// @param n The elements of type T to allocate
  130. /// @param alignment The alignment of the allocation.
  131. ///
  132. /// @note It's not part of the STL interface
  133. pointer allocate(size_type n, U32 alignment)
  134. {
  135. PtrSize hint = alignment;
  136. return allocate(n, &hint);
  137. }
  138. /// Deallocate memory
  139. void deallocate(void* p, size_type n)
  140. {
  141. ANKI_ASSERT(m_pool);
  142. (void)n;
  143. m_pool->free(p);
  144. }
  145. /// Call constructor
  146. void construct(pointer p, const T& val)
  147. {
  148. ::new(p) T(val);
  149. }
  150. /// Call constructor with many arguments
  151. template<typename Y, typename... Args>
  152. void construct(Y* p, Args&&... args)
  153. {
  154. // Placement new
  155. ::new(static_cast<void*>(p)) Y(std::forward<Args>(args)...);
  156. }
  157. /// Call default constructor only for non-trivially constructible types.
  158. template<typename Y>
  159. void construct(Y* p)
  160. {
  161. if(!std::is_trivially_constructible<Y>::value)
  162. {
  163. ::new(static_cast<void*>(p)) Y();
  164. }
  165. }
  166. /// Call destructor
  167. void destroy(pointer p)
  168. {
  169. static_assert(sizeof(T) > 0, "Incomplete type");
  170. ANKI_ASSERT(p != nullptr);
  171. p->~T();
  172. }
  173. /// Call destructor
  174. template<typename Y>
  175. void destroy(Y* p)
  176. {
  177. static_assert(sizeof(T) > 0, "Incomplete type");
  178. ANKI_ASSERT(p != nullptr);
  179. p->~Y();
  180. }
  181. /// Get the max allocation size
  182. size_type max_size() const
  183. {
  184. return MAX_PTR_SIZE;
  185. }
  186. /// Get the memory pool
  187. /// @note This is AnKi specific
  188. const TPool& getMemoryPool() const
  189. {
  190. ANKI_ASSERT(m_pool);
  191. return *m_pool;
  192. }
  193. /// Get the memory pool
  194. /// @note This is AnKi specific
  195. TPool& getMemoryPool()
  196. {
  197. ANKI_ASSERT(m_pool);
  198. return *m_pool;
  199. }
  200. /// Allocate a new object and call it's constructor
  201. /// @note This is AnKi specific
  202. template<typename Y, typename... Args>
  203. Y* newInstance(Args&&... args)
  204. {
  205. typename rebind<Y>::other alloc(*this);
  206. Y* ptr = alloc.allocate(1);
  207. if(ptr)
  208. {
  209. alloc.construct(ptr, std::forward<Args>(args)...);
  210. }
  211. return ptr;
  212. }
  213. /// Allocate a new array of objects and call their constructor
  214. /// @note This is AnKi specific
  215. template<typename Y>
  216. Y* newArray(size_type n)
  217. {
  218. typename rebind<Y>::other alloc(*this);
  219. Y* ptr = alloc.allocate(n);
  220. if(ptr)
  221. {
  222. // Call the constuctors
  223. for(size_type i = 0; i < n; i++)
  224. {
  225. alloc.construct(&ptr[i]);
  226. }
  227. }
  228. return ptr;
  229. }
  230. /// Allocate a new array of objects and call their constructor
  231. /// @note This is AnKi specific
  232. template<typename Y>
  233. Y* newArray(size_type n, const Y& v)
  234. {
  235. typename rebind<Y>::other alloc(*this);
  236. Y* ptr = alloc.allocate(n);
  237. if(ptr)
  238. {
  239. // Call the constuctors
  240. for(size_type i = 0; i < n; i++)
  241. {
  242. alloc.construct(&ptr[i], v);
  243. }
  244. }
  245. return ptr;
  246. }
  247. /// Call the destructor and deallocate an object
  248. /// @note This is AnKi specific
  249. template<typename Y>
  250. void deleteInstance(Y* ptr)
  251. {
  252. if(ptr != nullptr)
  253. {
  254. typename rebind<Y>::other alloc(*this);
  255. alloc.destroy(ptr);
  256. alloc.deallocate(ptr, 1);
  257. }
  258. }
  259. /// Call the destructor and deallocate an array of objects
  260. /// @note This is AnKi specific
  261. template<typename Y>
  262. void deleteArray(Y* ptr, size_type n)
  263. {
  264. typename rebind<Y>::other alloc(*this);
  265. if(ptr != nullptr)
  266. {
  267. // Call the destructors
  268. for(size_type i = 0; i < n; i++)
  269. {
  270. alloc.destroy(&ptr[i]);
  271. }
  272. alloc.deallocate(ptr, n);
  273. }
  274. else
  275. {
  276. ANKI_ASSERT(n == 0);
  277. }
  278. }
  279. private:
  280. TPool* m_pool = nullptr;
  281. template<typename Y>
  282. void copy(const GenericPoolAllocator<Y, TPool>& b)
  283. {
  284. clear();
  285. if(b.m_pool)
  286. {
  287. m_pool = b.m_pool;
  288. m_pool->getRefcount().fetchAdd(1);
  289. }
  290. }
  291. void clear()
  292. {
  293. if(m_pool)
  294. {
  295. auto count = m_pool->getRefcount().fetchSub(1);
  296. if(count == 1)
  297. {
  298. auto allocCb = m_pool->getAllocationCallback();
  299. auto ud = m_pool->getAllocationCallbackUserData();
  300. ANKI_ASSERT(allocCb);
  301. m_pool->~TPool();
  302. allocCb(ud, m_pool, 0, 0);
  303. }
  304. m_pool = nullptr;
  305. }
  306. }
  307. };
  308. /// @name GenericPoolAllocator global functions
  309. /// @{
  310. /// Another allocator of the same type can deallocate from this one
  311. template<typename T1, typename T2, typename TPool>
  312. inline Bool operator==(const GenericPoolAllocator<T1, TPool>&, const GenericPoolAllocator<T2, TPool>&)
  313. {
  314. return true;
  315. }
  316. /// Another allocator of the another type cannot deallocate from this one
  317. template<typename T1, typename AnotherAllocator, typename TPool>
  318. inline Bool operator==(const GenericPoolAllocator<T1, TPool>&, const AnotherAllocator&)
  319. {
  320. return false;
  321. }
  322. /// Another allocator of the same type can deallocate from this one
  323. template<typename T1, typename T2, typename TPool>
  324. inline Bool operator!=(const GenericPoolAllocator<T1, TPool>&, const GenericPoolAllocator<T2, TPool>&)
  325. {
  326. return false;
  327. }
  328. /// Another allocator of the another type cannot deallocate from this one
  329. template<typename T1, typename AnotherAllocator, typename TPool>
  330. inline Bool operator!=(const GenericPoolAllocator<T1, TPool>&, const AnotherAllocator&)
  331. {
  332. return true;
  333. }
  334. /// @}
  335. /// Allocator using the base memory pool.
  336. template<typename T>
  337. using GenericMemoryPoolAllocator = GenericPoolAllocator<T, BaseMemoryPool>;
  338. /// Heap based allocator. The default allocator. It uses malloc and free for allocations/deallocations
  339. template<typename T>
  340. using HeapAllocator = GenericPoolAllocator<T, HeapMemoryPool>;
  341. /// Allocator that uses a StackMemoryPool
  342. template<typename T>
  343. using StackAllocator = GenericPoolAllocator<T, StackMemoryPool>;
  344. /// Allocator that uses a ChainMemoryPool
  345. template<typename T>
  346. using ChainAllocator = GenericPoolAllocator<T, ChainMemoryPool>;
  347. /// @}
  348. } // end namespace anki