heap.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2024 to 2025 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. // An arena memory allocator with recycling bins to provide heap memory.
  24. // All allocations are reference counted, because the memory allocator itself may increase the reference count as needed.
  25. // * An allocation with use count 0 will remain until the use count changes and reaches zero again.
  26. // All allocations are aligned to DSR_MAXIMUM_ALIGNMENT to prevent false sharing of cache lines between threads.
  27. // The space in front of each allocation contains a HeapHeader including:
  28. // * The total size of the allocation including padding and the header.
  29. // * How many of the allocated bytes that are actually used.
  30. // * A reference counter.
  31. // * A destructor to allow freeing the memory no matter where the reference counter decreases to zero.
  32. // * A bin index for fast recycling of memory.
  33. // * Bit flags for keeping track of the allocation's state.
  34. // In debug mode, the header also contains:
  35. // * A thread hash to keep track of data ownership.
  36. // All heap allocations are currently shared among all threads unlike virtual stack memory.
  37. // * An allocation identity that is unique for each new allocation.
  38. // When freed, the header's allocation identity is set to zero to prevent accidental use of freed memory.
  39. // When recycled as a new allocation, the same address gets a new identity, which invalidates any old SafePointer to the same address.
  40. // * An ascii name to make memory debugging easier.
  41. // Dimensions
  42. // used size <= padded size <= allocation size
  43. // * Size defines the used bytes that represent something, which affects destruction of elements.
  44. // Size can change from 0 to allocation size without having to move the data.
  45. // * The padded size defines the region where memory access is allowed for SafePointer.
  46. // Padded size is computed by rounding up to whole blocks of DSR_MAXIMUM_ALIGNMENT.
  47. // * Allocation size is the available space to work with.
  48. // To change the allocation's size, one must move to another memory location.
  49. #ifndef DFPSR_HEAP
  50. #define DFPSR_HEAP
  51. #include "SafePointer.h"
  52. #include <functional>
  53. namespace dsr {
  54. #ifdef SAFE_POINTER_CHECKS
  55. // Assign a debug name to the allocation.
  56. // Does nothing if allocation is nullptr.
  57. // Only assign constant ascii string literals.
  58. void heap_setAllocationName(void const * const allocation, const char *name);
  59. // Get the ascii name of allocation, or "none" if allocation is nullptr.
  60. const char * heap_getAllocationName(void const * const allocation);
  61. // Gets the size padded out to whole blocks of heap alignment, for constructing a SafePointer.
  62. uintptr_t heap_getPaddedSize(void const * const allocation);
  63. // Set the serialization function for a certain allocation, so that going through memory allocations will display its content when debugging memory leaks.
  64. void setAllocationSerialization(void const * const allocation, AllocationSerialization method);
  65. // Get the function used to interpret the allocation's content.
  66. AllocationSerialization getAllocationSerialization(void const * const allocation);
  67. #endif
  68. // TODO: Allow allocating fixed size allocations using a typename and pre-calculate the bin index in compile time.
  69. // This requires the bin sizes to be independent of DSR_MAXIMUM_ALIGNMENT, possibly by leaving a few bins
  70. // unused in the beginning and start counting with the header size as the smallest allocation size.
  71. // Allocate memory in the heap.
  72. // The minimumSize argument is the minimum number of bytes to allocate, but the result may give you more than you asked for.
  73. // To allow representing empty files using buffers, it is allowed to create an allocation of zero bytes.
  74. // When zeroed is true, the new memory will be zeroed. Otherwise it may contain uninitialized data.
  75. // Post-condition: Returns pointers to the payload and header.
  76. UnsafeAllocation heap_allocate(uintptr_t minimumSize, bool zeroed = true);
  77. // Increase the use count of an allocation.
  78. // Does nothing if the allocation is nullptr.
  79. void heap_increaseUseCount(void const * const allocation);
  80. void heap_increaseUseCount(AllocationHeader const * const header);
  81. // Decrease the use count of an allocation and recycle it when reaching zero.
  82. // Does nothing if the allocation is nullptr.
  83. void heap_decreaseUseCount(void const * const allocation);
  84. void heap_decreaseUseCount(AllocationHeader const * const header);
  85. // Pre-condition:
  86. // allocation points to memory allocated as heap_allocate(...).data because this feature is specific to this allocator.
  87. // Post-condition:
  88. // Returns the number of bytes in the allocation that are actually used, which is used for tight bound checks and knowing how large a buffer is.
  89. // Returns 0 if allocation is nullptr.
  90. uintptr_t heap_getUsedSize(void const * const allocation);
  91. uintptr_t heap_getUsedSize(AllocationHeader const * const header);
  92. // Side-effect:
  93. // Assigns a new used size to allocation.
  94. // Has no effect if allocation is nullptr.
  95. // Pre-condition:
  96. // You may not reserve more memory than what is available in total.
  97. // size <= heap_getAllocationSize(allocation)
  98. // If exceeded, size will be limited by the allocation's size.
  99. // Post-condition:
  100. // Returns the assigned size, which is the given size, an exceeded allocation size, or zero for an allocation that does not exist.
  101. uintptr_t heap_setUsedSize(void * const allocation, uintptr_t size);
  102. uintptr_t heap_setUsedSize(AllocationHeader * const header, uintptr_t size);
  103. // A function pointer for destructors.
  104. using HeapDestructorPointer = void(*)(void *toDestroy, void *externalResource);
  105. struct HeapDestructor {
  106. // A function pointer to a method taking toDestroy and externalResource as arguments.
  107. HeapDestructorPointer destructor = nullptr;
  108. // A pointer for freeing external resources owning the allocation.
  109. void *externalResource = nullptr;
  110. // Constructor.
  111. HeapDestructor(HeapDestructorPointer destructor = nullptr, void *externalResource = nullptr)
  112. : destructor(destructor), externalResource(externalResource) {}
  113. };
  114. // Register a destructor function pointer to be called automatically when the allocation is freed.
  115. // externalResource is the second argument that will be given to destructor together with the freed memory to destruct.
  116. void heap_setAllocationDestructor(void * const allocation, const HeapDestructor &destructor);
  117. // Get the use count outside of transactions without locking.
  118. uintptr_t heap_getUseCount(void const * const allocation);
  119. uintptr_t heap_getUseCount(AllocationHeader const * const header);
  120. // Pre-condition: The allocation pointer must point to the start of a payload allocated using heap_allocate, no offsets nor other allocators allowed.
  121. // Post-condition: Returns the number of available bytes in the allocation.
  122. uintptr_t heap_getAllocationSize(void const * const allocation);
  123. // Pre-condition: The header pointer must point to the allocation head, as returned from heap_allocate or heap_getHeader.
  124. // Post-condition: Returns the number of available bytes in the allocation.
  125. uintptr_t heap_getAllocationSize(AllocationHeader const * const header);
  126. // Get the alignment of the heap, which depends on the largest cache line size.
  127. uintptr_t heap_getHeapAlignment();
  128. // Pre-condition: The allocation pointer must point to the start of a payload allocated using heap_allocate, no offsets nor other allocators allowed.
  129. // Post-condition: Returns a pointer to the heap allocation's header, which is used to construct safe pointers.
  130. AllocationHeader *heap_getHeader(void * const allocation);
  131. // Call back for each used heap allocation.
  132. // Recycled allocations are not included.
  133. void heap_forAllHeapAllocations(std::function<void(AllocationHeader * header, void * allocation)> callback);
  134. // Prints the allocation to the terminal.
  135. void heap_debugPrintAllocation(void const * const allocation, uintptr_t maxLength = 128u);
  136. // Prints a list of allocations to the terminal, without creating new memory allocations.
  137. void heap_debugPrintAllocations(uintptr_t maxLength = 128u);
  138. // Called by DSR_MAIN_CALLER when the program starts.
  139. void heap_startingApplication();
  140. // Called by DSR_MAIN_CALLER when the program closes.
  141. void heap_terminatingApplication();
  142. // If terminating the program using std::exit, you can call this first to free all heap memory in the allocator, leaked or not.
  143. void heap_hardExitCleaning();
  144. // Used to find the origin of memory leaks in single-threaded tests.
  145. intptr_t heap_getAllocationCount();
  146. // Store application defined custom flags, which can be used for debugging memory leaks.
  147. // The flags do not take any additional memory, because an allocation head can not allocate less than a whole cache line.
  148. uint32_t heap_getAllocationCustomFlags(void const * const allocation);
  149. uint32_t heap_getAllocationCustomFlags(AllocationHeader * header);
  150. void heap_setAllocationCustomFlags(void const * const allocation, uint32_t customFlags);
  151. void heap_setAllocationCustomFlags(AllocationHeader * header, uint32_t customFlags);
  152. // TODO: Allow storing custom information in allocation heads for debugging memory.
  153. // Helper methods to prevent cyclic dependencies between strings and buffers when handles must be inlined for performance. Do not call these yourself.
  154. void impl_throwAllocationFailure();
  155. void impl_throwNullException();
  156. void impl_throwIdentityMismatch(uint64_t allocationIdentity, uint64_t pointerIdentity);
  157. }
  158. #endif