Memory.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. // Copyright (C) 2009-2020, 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/NonCopyable.h>
  8. #include <anki/util/Atomic.h>
  9. #include <anki/util/Assert.h>
  10. #include <anki/util/Array.h>
  11. #include <anki/util/Thread.h>
  12. #include <utility> // For forward
  13. namespace anki
  14. {
  15. // Forward
  16. class SpinLock;
  17. /// @addtogroup util_memory
  18. /// @{
  19. #define ANKI_MEM_USE_SIGNATURES ANKI_EXTRA_CHECKS
  20. /// Allocate aligned memory
  21. void* mallocAligned(PtrSize size, PtrSize alignmentBytes);
  22. /// Free aligned memory
  23. void freeAligned(void* ptr);
  24. /// The function signature of a memory allocation/deallocation. See allocAligned function for the explanation of
  25. /// arguments
  26. using AllocAlignedCallback = void* (*)(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  27. /// An internal type.
  28. using AllocationSignature = U32;
  29. /// This is a function that allocates and deallocates heap memory. If the @a ptr is nullptr then it allocates using the
  30. /// @a size and @a alignment. If the @a ptr is not nullptr it deallocates the memory and the @a size and @a alignment is
  31. /// ignored.
  32. ///
  33. /// @param userData Used defined data
  34. /// @param ptr The pointer to deallocate or nullptr
  35. /// @param size The size to allocate or 0
  36. /// @param alignment The allocation alignment or 0
  37. /// @return On allocation mode it will return the newelly allocated block or nullptr on error. On deallocation mode
  38. /// returns nullptr
  39. void* allocAligned(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  40. /// Generic memory pool. The base of HeapMemoryPool or StackMemoryPool or ChainMemoryPool.
  41. class BaseMemoryPool : public NonCopyable
  42. {
  43. public:
  44. virtual ~BaseMemoryPool();
  45. /// Allocate memory. This operation MAY be thread safe
  46. /// @param size The size to allocate
  47. /// @param alignmentBytes The alignment of the returned address
  48. /// @return The allocated memory or nullptr on failure
  49. void* allocate(PtrSize size, PtrSize alignmentBytes);
  50. /// Free memory.
  51. /// @param[in, out] ptr Memory block to deallocate
  52. void free(void* ptr);
  53. /// Get refcount.
  54. Atomic<U32>& getRefcount()
  55. {
  56. return m_refcount;
  57. }
  58. /// Get number of users.
  59. U32 getUsersCount() const
  60. {
  61. return m_refcount.load();
  62. }
  63. /// Get allocation callback.
  64. AllocAlignedCallback getAllocationCallback() const
  65. {
  66. return m_allocCb;
  67. }
  68. /// Get allocation callback user data.
  69. void* getAllocationCallbackUserData() const
  70. {
  71. return m_allocCbUserData;
  72. }
  73. /// Return number of allocations
  74. U32 getAllocationsCount() const
  75. {
  76. return m_allocationsCount.load();
  77. }
  78. protected:
  79. /// Pool type.
  80. enum class Type : U8
  81. {
  82. NONE,
  83. HEAP,
  84. STACK,
  85. CHAIN
  86. };
  87. /// User allocation function.
  88. AllocAlignedCallback m_allocCb = nullptr;
  89. /// User allocation function data.
  90. void* m_allocCbUserData = nullptr;
  91. /// Allocations count.
  92. Atomic<U32> m_allocationsCount = {0};
  93. BaseMemoryPool(Type type)
  94. : m_type(type)
  95. {
  96. }
  97. /// Check if already created.
  98. Bool isCreated() const;
  99. private:
  100. /// Refcount.
  101. Atomic<U32> m_refcount = {0};
  102. /// Type.
  103. Type m_type = Type::NONE;
  104. };
  105. /// A dummy interface to match the StackMemoryPool and ChainMemoryPool interfaces in order to be used by the same
  106. /// allocator template.
  107. class HeapMemoryPool final : public BaseMemoryPool
  108. {
  109. public:
  110. /// Default constructor.
  111. HeapMemoryPool();
  112. /// Destroy
  113. ~HeapMemoryPool();
  114. /// The real constructor.
  115. /// @param allocCb The allocation function callback
  116. /// @param allocCbUserData The user data to pass to the allocation function
  117. void create(AllocAlignedCallback allocCb, void* allocCbUserData);
  118. /// Allocate memory
  119. void* allocate(PtrSize size, PtrSize alignment);
  120. /// Free memory.
  121. /// @param[in, out] ptr Memory block to deallocate.
  122. void free(void* ptr);
  123. private:
  124. #if ANKI_MEM_USE_SIGNATURES
  125. AllocationSignature m_signature = 0;
  126. static const U32 MAX_ALIGNMENT = 64;
  127. U32 m_headerSize = 0;
  128. #endif
  129. };
  130. /// Thread safe memory pool. It's a preallocated memory pool that is used for memory allocations on top of that
  131. /// preallocated memory. It is mainly used by fast stack allocators
  132. class StackMemoryPool final : public BaseMemoryPool
  133. {
  134. public:
  135. /// The type of the pool's snapshot
  136. using Snapshot = void*;
  137. /// Default constructor
  138. StackMemoryPool();
  139. /// Destroy
  140. ~StackMemoryPool();
  141. /// Create with parameters
  142. /// @param allocCb The allocation function callback
  143. /// @param allocCbUserData The user data to pass to the allocation function
  144. /// @param initialChunkSize The size of the first chunk.
  145. /// @param nextChunkScale Value that controls the next chunk.
  146. /// @param nextChunkBias Value that controls the next chunk.
  147. /// @param ignoreDeallocationErrors Method free() may fail if the ptr is not in the top of the stack. Set that to
  148. /// true to suppress such errors
  149. /// @param alignmentBytes The maximum supported alignment for returned memory
  150. void create(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F32 nextChunkScale = 2.0,
  151. PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true,
  152. PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT);
  153. /// Allocate aligned memory. The operation is thread safe
  154. /// @param size The size to allocate
  155. /// @param alignmentBytes The alignment of the returned address
  156. /// @return The allocated memory or nullptr on failure
  157. void* allocate(PtrSize size, PtrSize alignmentBytes);
  158. /// Free memory in StackMemoryPool. It will not actially free anything.
  159. /// @param[in, out] ptr Memory block to deallocate
  160. void free(void* ptr);
  161. /// Reinit the pool. All existing allocated memory will be lost
  162. void reset();
  163. /// Get the current capacity of the pool. It's not thread safe.
  164. PtrSize getMemoryCapacity() const;
  165. private:
  166. /// The memory chunk.
  167. class Chunk
  168. {
  169. public:
  170. /// The base memory of the chunk.
  171. U8* m_baseMem = nullptr;
  172. /// The moving ptr for the next allocation.
  173. Atomic<U8*> m_mem = {nullptr};
  174. /// The chunk size.
  175. PtrSize m_size = 0;
  176. /// Check that it's initialized.
  177. void check() const
  178. {
  179. ANKI_ASSERT(m_baseMem != nullptr);
  180. ANKI_ASSERT(m_mem.load() >= m_baseMem);
  181. ANKI_ASSERT(m_size > 0);
  182. }
  183. // Check that it's in reset state.
  184. void checkReset() const
  185. {
  186. ANKI_ASSERT(m_baseMem != nullptr);
  187. ANKI_ASSERT(m_mem.load() == m_baseMem);
  188. ANKI_ASSERT(m_size > 0);
  189. }
  190. };
  191. /// Alignment of allocations
  192. PtrSize m_alignmentBytes = 0;
  193. /// The size of the first chunk.
  194. PtrSize m_initialChunkSize = 0;
  195. /// Chunk scale.
  196. F32 m_nextChunkScale = 0.0;
  197. /// Chunk bias.
  198. PtrSize m_nextChunkBias = 0;
  199. /// Ignore deallocation errors.
  200. Bool m_ignoreDeallocationErrors = false;
  201. /// The current chunk. Chose the more strict memory order to avoid compiler
  202. /// re-ordering of instructions
  203. Atomic<U32, AtomicMemoryOrder::SEQ_CST> m_crntChunkIdx = {0};
  204. /// The max number of chunks.
  205. static const U MAX_CHUNKS = 256;
  206. /// The chunks.
  207. Array<Chunk, MAX_CHUNKS> m_chunks;
  208. /// Protect the m_crntChunkIdx.
  209. Mutex m_lock;
  210. };
  211. /// Chain memory pool. Almost similar to StackMemoryPool but more flexible and at the same time a bit slower.
  212. class ChainMemoryPool final : public BaseMemoryPool
  213. {
  214. public:
  215. /// Default constructor
  216. ChainMemoryPool();
  217. /// Destroy
  218. ~ChainMemoryPool();
  219. /// Creates the pool.
  220. /// @param allocCb The allocation function callback.
  221. /// @param allocCbUserData The user data to pass to the allocation function.
  222. /// @param initialChunkSize The size of the first chunk.
  223. /// @param nextChunkScale Value that controls the next chunk.
  224. /// @param nextChunkBias Value that controls the next chunk.
  225. /// @param alignmentBytes The maximum supported alignment for returned memory.
  226. void create(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F32 nextChunkScale = 2.0,
  227. PtrSize nextChunkBias = 0, PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT);
  228. /// Allocate memory. This operation is thread safe
  229. /// @param size The size to allocate
  230. /// @param alignmentBytes The alignment of the returned address
  231. /// @return The allocated memory or nullptr on failure
  232. void* allocate(PtrSize size, PtrSize alignmentBytes);
  233. /// Free memory. If the ptr is not the last allocation of the chunk then nothing happens and the method returns
  234. /// false.
  235. /// @param[in, out] ptr Memory block to deallocate
  236. void free(void* ptr);
  237. /// @name Methods used for optimizing future chains.
  238. /// @{
  239. PtrSize getChunksCount() const;
  240. PtrSize getAllocatedSize() const;
  241. /// @}
  242. private:
  243. /// A chunk of memory
  244. struct Chunk
  245. {
  246. /// Pre-allocated memory chunk.
  247. U8* m_memory = nullptr;
  248. /// Size of the pre-allocated memory chunk
  249. PtrSize m_memsize = 0;
  250. /// Points to the memory and more specifically to the top of the stack
  251. U8* m_top = nullptr;
  252. /// Used to identify if the chunk can be deleted
  253. PtrSize m_allocationsCount = 0;
  254. /// Previous chunk in the list
  255. Chunk* m_prev = nullptr;
  256. /// Next chunk in the list
  257. Chunk* m_next = nullptr;
  258. };
  259. /// Alignment of allocations.
  260. PtrSize m_alignmentBytes = 0;
  261. /// The first chunk.
  262. Chunk* m_headChunk = nullptr;
  263. /// Current chunk to allocate from.
  264. Chunk* m_tailChunk = nullptr;
  265. /// Fast thread locking.
  266. SpinLock* m_lock = nullptr;
  267. /// Size of the first chunk.
  268. PtrSize m_initSize = 0;
  269. /// Chunk scale.
  270. F32 m_scale = 2.0;
  271. /// Chunk bias.
  272. PtrSize m_bias = 0;
  273. /// Cache a value.
  274. PtrSize m_headerSize = 0;
  275. /// Compute the size for the next chunk.
  276. /// @param size The current allocation size.
  277. PtrSize computeNewChunkSize(PtrSize size) const;
  278. /// Create a new chunk.
  279. Chunk* createNewChunk(PtrSize size);
  280. /// Allocate from chunk.
  281. void* allocateFromChunk(Chunk* ch, PtrSize size, PtrSize alignment);
  282. /// Destroy a chunk.
  283. void destroyChunk(Chunk* ch);
  284. };
  285. inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
  286. {
  287. void* out = nullptr;
  288. switch(m_type)
  289. {
  290. case Type::HEAP:
  291. out = static_cast<HeapMemoryPool*>(this)->allocate(size, alignmentBytes);
  292. break;
  293. case Type::STACK:
  294. out = static_cast<StackMemoryPool*>(this)->allocate(size, alignmentBytes);
  295. break;
  296. default:
  297. ANKI_ASSERT(m_type == Type::CHAIN);
  298. out = static_cast<ChainMemoryPool*>(this)->allocate(size, alignmentBytes);
  299. }
  300. return out;
  301. }
  302. inline void BaseMemoryPool::free(void* ptr)
  303. {
  304. switch(m_type)
  305. {
  306. case Type::HEAP:
  307. static_cast<HeapMemoryPool*>(this)->free(ptr);
  308. break;
  309. case Type::STACK:
  310. static_cast<StackMemoryPool*>(this)->free(ptr);
  311. break;
  312. default:
  313. ANKI_ASSERT(m_type == Type::CHAIN);
  314. static_cast<ChainMemoryPool*>(this)->free(ptr);
  315. }
  316. }
  317. /// @}
  318. } // end namespace anki