Memory.h 10 KB

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