Memory.h 9.6 KB

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