MemoryPool.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // Copyright (C) 2009-present, 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/StdTypes.h>
  7. #include <AnKi/Util/Atomic.h>
  8. #include <AnKi/Util/Assert.h>
  9. #include <AnKi/Util/Thread.h>
  10. #include <AnKi/Util/StackAllocatorBuilder.h>
  11. #include <utility> // For forward
  12. namespace anki {
  13. /// @addtogroup util_memory
  14. /// @{
  15. #define ANKI_MEM_EXTRA_CHECKS ANKI_EXTRA_CHECKS
  16. /// Allocate aligned memory
  17. void* mallocAligned(PtrSize size, PtrSize alignmentBytes);
  18. /// Free aligned memory
  19. void freeAligned(void* ptr);
  20. /// The function signature of a memory allocation/deallocation. See allocAligned function for the explanation of
  21. /// arguments
  22. using AllocAlignedCallback = void* (*)(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  23. /// An internal type.
  24. using PoolSignature = U32;
  25. /// This is a function that allocates and deallocates heap memory. If the @a ptr is nullptr then it allocates using the @a size and @a alignment. If
  26. /// the @a ptr is not nullptr it deallocates the memory and the @a size and @a alignment is ignored.
  27. ///
  28. /// @param userData Used defined data
  29. /// @param ptr The pointer to deallocate or nullptr
  30. /// @param size The size to allocate or 0
  31. /// @param alignment The allocation alignment or 0
  32. /// @return On allocation mode it will return the newelly allocated block or nullptr on error. On deallocation returns nullptr
  33. void* allocAligned(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  34. /// Generic memory pool. The base of HeapMemoryPool or StackMemoryPool.
  35. class BaseMemoryPool
  36. {
  37. public:
  38. BaseMemoryPool(const BaseMemoryPool&) = delete; // Non-copyable
  39. virtual ~BaseMemoryPool()
  40. {
  41. }
  42. BaseMemoryPool& operator=(const BaseMemoryPool&) = delete; // Non-copyable
  43. /// Allocate memory. This operation MAY be thread safe
  44. /// @param size The size to allocate
  45. /// @param alignmentBytes The alignment of the returned address
  46. /// @return The allocated memory or nullptr on failure
  47. void* allocate(PtrSize size, PtrSize alignmentBytes);
  48. /// Free memory.
  49. /// @param[in, out] ptr Memory block to deallocate
  50. void free(void* ptr);
  51. /// Get allocation callback.
  52. AllocAlignedCallback getAllocationCallback() const
  53. {
  54. return m_allocCb;
  55. }
  56. /// Get allocation callback user data.
  57. void* getAllocationCallbackUserData() const
  58. {
  59. return m_allocCbUserData;
  60. }
  61. /// Return number of allocations
  62. U32 getAllocationCount() const
  63. {
  64. return m_allocationCount.load();
  65. }
  66. /// Get the name of the pool.
  67. const Char* getName() const
  68. {
  69. return (m_name) ? m_name : "Unamed";
  70. }
  71. protected:
  72. /// Pool type.
  73. enum class Type : U8
  74. {
  75. kNone,
  76. kHeap,
  77. kStack,
  78. };
  79. /// User allocation function.
  80. AllocAlignedCallback m_allocCb = nullptr;
  81. /// User allocation function data.
  82. void* m_allocCbUserData = nullptr;
  83. /// Allocations count.
  84. Atomic<U32> m_allocationCount = {0};
  85. BaseMemoryPool(Type type)
  86. : m_type(type)
  87. {
  88. }
  89. void init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name);
  90. void destroy();
  91. private:
  92. /// Optional name.
  93. char* m_name = nullptr;
  94. /// Type.
  95. Type m_type = Type::kNone;
  96. };
  97. /// A dummy interface to match the StackMemoryPool interfaces in order to be used by the same allocator template.
  98. class HeapMemoryPool : public BaseMemoryPool
  99. {
  100. public:
  101. /// Construct it.
  102. HeapMemoryPool()
  103. : BaseMemoryPool(Type::kHeap)
  104. {
  105. }
  106. /// @see init
  107. HeapMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name = nullptr)
  108. : HeapMemoryPool()
  109. {
  110. init(allocCb, allocCbUserData, name);
  111. }
  112. /// Destroy
  113. ~HeapMemoryPool()
  114. {
  115. destroy();
  116. }
  117. /// Init.
  118. /// @param allocCb The allocation function callback.
  119. /// @param allocCbUserData The user data to pass to the allocation function.
  120. /// @param name An optional name.
  121. void init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name = nullptr);
  122. /// Manual destroy. The destructor calls that as well.
  123. void destroy();
  124. /// Allocate memory
  125. void* allocate(PtrSize size, PtrSize alignment);
  126. /// Free memory.
  127. /// @param[in, out] ptr Memory block to deallocate.
  128. void free(void* ptr);
  129. private:
  130. #if ANKI_MEM_EXTRA_CHECKS
  131. PoolSignature m_signature = 0;
  132. #endif
  133. };
  134. /// The default global memory pool.
  135. class DefaultMemoryPool : public HeapMemoryPool, public MakeSingleton<DefaultMemoryPool>
  136. {
  137. template<typename>
  138. friend class MakeSingleton;
  139. private:
  140. DefaultMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData)
  141. : HeapMemoryPool(allocCb, allocCbUserData, "DefaultMemPool")
  142. {
  143. }
  144. ~DefaultMemoryPool() = default;
  145. };
  146. /// Thread safe memory pool. It's a preallocated memory pool that is used for memory allocations on top of that
  147. /// preallocated memory. It is mainly used by fast stack allocators
  148. class StackMemoryPool : public BaseMemoryPool
  149. {
  150. public:
  151. StackMemoryPool()
  152. : BaseMemoryPool(Type::kStack)
  153. {
  154. }
  155. /// @see init
  156. StackMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F64 nextChunkScale = 2.0,
  157. PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true, const Char* name = nullptr)
  158. : StackMemoryPool()
  159. {
  160. init(allocCb, allocCbUserData, initialChunkSize, nextChunkScale, nextChunkBias, ignoreDeallocationErrors, name);
  161. }
  162. /// Destroy
  163. ~StackMemoryPool()
  164. {
  165. destroy();
  166. }
  167. /// Init with parameters.
  168. /// @param allocCb The allocation function callback.
  169. /// @param allocCbUserData The user data to pass to the allocation function.
  170. /// @param initialChunkSize The size of the first chunk.
  171. /// @param nextChunkScale Value that controls the next chunk.
  172. /// @param nextChunkBias Value that controls the next chunk.
  173. /// @param ignoreDeallocationErrors Method free() may fail if the ptr is not in the top of the stack. Set that to true to suppress such errors.
  174. /// @param name An optional name.
  175. void init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F64 nextChunkScale = 2.0, PtrSize nextChunkBias = 0,
  176. Bool ignoreDeallocationErrors = true, const Char* name = nullptr);
  177. /// Manual destroy. The destructor calls that as well.
  178. void destroy();
  179. /// Allocate aligned memory.
  180. /// @param size The size to allocate.
  181. /// @param alignmentBytes The alignment of the returned address.
  182. /// @return The allocated memory or nullptr on failure.
  183. ///
  184. /// @note The operation is thread safe with allocate() and free() methods.
  185. void* allocate(PtrSize size, PtrSize alignmentBytes);
  186. /// Free memory in StackMemoryPool. It will not actually do anything.
  187. /// @param[in, out] ptr Memory block to deallocate.
  188. void free(void* ptr);
  189. /// Reinit the pool. All existing allocated memory is effectively invalidated.
  190. /// @note It's not thread safe with other methods.
  191. void reset();
  192. /// Get the physical memory allocated by the pool.
  193. /// @note It's not thread safe with other methods.
  194. PtrSize getMemoryCapacity() const
  195. {
  196. return m_builder.getMemoryCapacity();
  197. }
  198. private:
  199. /// This is the absolute max alignment.
  200. static constexpr U32 kMaxAlignment = ANKI_SAFE_ALIGNMENT;
  201. /// This is the chunk the StackAllocatorBuilder will be allocating.
  202. class alignas(kMaxAlignment) Chunk
  203. {
  204. public:
  205. /// Required by StackAllocatorBuilder.
  206. Chunk* m_nextChunk;
  207. /// Required by StackAllocatorBuilder.
  208. Atomic<PtrSize> m_offsetInChunk;
  209. /// Required by StackAllocatorBuilder.
  210. PtrSize m_chunkSize;
  211. /// The start of the actual CPU memory.
  212. alignas(kMaxAlignment) U8 m_memoryStart[1];
  213. };
  214. /// Implements the StackAllocatorBuilder TInterface
  215. class StackAllocatorBuilderInterface
  216. {
  217. public:
  218. StackMemoryPool* m_parent = nullptr;
  219. Bool m_ignoreDeallocationErrors = false;
  220. PtrSize m_initialChunkSize = 0;
  221. F64 m_nextChunkScale = 0.0;
  222. PtrSize m_nextChunkBias = 0;
  223. // The rest of the functions implement the StackAllocatorBuilder TInterface.
  224. PtrSize getInitialChunkSize() const
  225. {
  226. ANKI_ASSERT(m_initialChunkSize > 0);
  227. return m_initialChunkSize;
  228. }
  229. F64 getNextChunkGrowScale() const
  230. {
  231. ANKI_ASSERT(m_nextChunkScale >= 1.0);
  232. return m_nextChunkScale;
  233. }
  234. PtrSize getNextChunkGrowBias() const
  235. {
  236. return m_nextChunkBias;
  237. }
  238. static constexpr PtrSize getMaxChunkSize()
  239. {
  240. return 32_GB;
  241. }
  242. Bool ignoreDeallocationErrors() const
  243. {
  244. return m_ignoreDeallocationErrors;
  245. }
  246. Error allocateChunk(PtrSize size, Chunk*& out);
  247. void freeChunk(Chunk* chunk);
  248. void recycleChunk(Chunk& chunk);
  249. Atomic<U32>* getAllocationCount()
  250. {
  251. return (m_parent) ? &m_parent->m_allocationCount : nullptr;
  252. }
  253. };
  254. /// The allocator helper.
  255. StackAllocatorBuilder<Chunk, StackAllocatorBuilderInterface, Mutex> m_builder;
  256. };
  257. /// A wrapper class that makes a pointer to a memory pool act like a reference.
  258. template<typename TMemPool>
  259. class MemoryPoolPtrWrapper
  260. {
  261. public:
  262. TMemPool* m_pool = nullptr;
  263. MemoryPoolPtrWrapper() = default;
  264. MemoryPoolPtrWrapper(TMemPool* pool)
  265. : m_pool(pool)
  266. {
  267. }
  268. TMemPool* operator&()
  269. {
  270. ANKI_ASSERT(m_pool);
  271. return m_pool;
  272. }
  273. operator TMemPool&()
  274. {
  275. ANKI_ASSERT(m_pool);
  276. return *m_pool;
  277. }
  278. void* allocate(PtrSize size, PtrSize alignmentBytes)
  279. {
  280. ANKI_ASSERT(m_pool);
  281. return m_pool->allocate(size, alignmentBytes);
  282. }
  283. void free(void* ptr)
  284. {
  285. ANKI_ASSERT(m_pool);
  286. m_pool->free(ptr);
  287. }
  288. };
  289. /// A wrapper class that adds a refcount to a memory pool.
  290. template<typename TMemPool>
  291. class RefCountedMemoryPool : public TMemPool
  292. {
  293. public:
  294. void retain() const
  295. {
  296. m_refcount.fetchAdd(1);
  297. }
  298. I32 release() const
  299. {
  300. return m_refcount.fetchSub(1);
  301. }
  302. private:
  303. mutable Atomic<I32> m_refcount = {0};
  304. };
  305. template<typename TMemoryPool>
  306. class SingletonMemoryPoolWrapper
  307. {
  308. public:
  309. TMemoryPool* operator&()
  310. {
  311. return &TMemoryPool::getSingleton();
  312. }
  313. operator TMemoryPool&()
  314. {
  315. return TMemoryPool::getSingleton();
  316. }
  317. void* allocate(PtrSize size, PtrSize alignmentBytes)
  318. {
  319. return TMemoryPool::getSingleton().allocate(size, alignmentBytes);
  320. }
  321. void free(void* ptr)
  322. {
  323. TMemoryPool::getSingleton().free(ptr);
  324. }
  325. };
  326. template<typename TMemoryPool>
  327. class SingletonMemoryPoolDeleter
  328. {
  329. public:
  330. template<typename T>
  331. void operator()(T* x)
  332. {
  333. deleteInstance<T>(TMemoryPool::getSingleton(), x);
  334. }
  335. };
  336. inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
  337. {
  338. void* out = nullptr;
  339. switch(m_type)
  340. {
  341. case Type::kHeap:
  342. out = static_cast<HeapMemoryPool*>(this)->allocate(size, alignmentBytes);
  343. break;
  344. case Type::kStack:
  345. out = static_cast<StackMemoryPool*>(this)->allocate(size, alignmentBytes);
  346. break;
  347. default:
  348. ANKI_ASSERT(0);
  349. }
  350. return out;
  351. }
  352. inline void BaseMemoryPool::free(void* ptr)
  353. {
  354. switch(m_type)
  355. {
  356. case Type::kHeap:
  357. static_cast<HeapMemoryPool*>(this)->free(ptr);
  358. break;
  359. case Type::kStack:
  360. static_cast<StackMemoryPool*>(this)->free(ptr);
  361. break;
  362. default:
  363. ANKI_ASSERT(0);
  364. }
  365. }
  366. /// @}
  367. } // end namespace anki