Memory.h 9.5 KB

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