heap.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. // TODO: Replace with a lambda printing the name from capture and optional serialized content, because memory efficiency is not required in debug mode.
  55. #ifdef SAFE_POINTER_CHECKS
  56. // Assign a debug name to the allocation.
  57. // Does nothing if allocation is nullptr.
  58. // Only assign constant ascii string literals.
  59. void heap_setAllocationName(void * const allocation, const char *name);
  60. // Get the ascii name of allocation, or "none" if allocation is nullptr.
  61. const char * heap_getAllocationName(void * const allocation);
  62. // Gets the size padded out to whole blocks of heap alignment, for constructing a SafePointer.
  63. uintptr_t heap_getPaddedSize(void const * const allocation);
  64. #endif
  65. // TODO: Allow allocating fixed size allocations using a typename and pre-calculate the bin index in compile time.
  66. // This requires the bin sizes to be independent of DSR_MAXIMUM_ALIGNMENT, possibly by leaving a few bins
  67. // unused in the beginning and start counting with the header size as the smallest allocation size.
  68. // Allocate memory in the heap.
  69. // The minimumSize argument is the minimum number of bytes to allocate, but the result may give you more than you asked for.
  70. // To allow representing empty files using buffers, it is allowed to create an allocation of zero bytes.
  71. // When zeroed is true, the new memory will be zeroed. Otherwise it may contain uninitialized data.
  72. // Post-condition: Returns pointers to the payload and header.
  73. UnsafeAllocation heap_allocate(uintptr_t minimumSize, bool zeroed = true);
  74. // Increase the use count of an allocation.
  75. // Does nothing if the allocation is nullptr.
  76. void heap_increaseUseCount(void const * const allocation);
  77. // Decrease the use count of an allocation and recycle it when reaching zero.
  78. // Does nothing if the allocation is nullptr.
  79. void heap_decreaseUseCount(void const * const allocation);
  80. // Pre-condition:
  81. // allocation points to memory allocated as heap_allocate(...).data because this feature is specific to this allocator.
  82. // Post-condition:
  83. // 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.
  84. // Returns 0 if allocation is nullptr.
  85. uintptr_t heap_getUsedSize(void const * const allocation);
  86. // Side-effect:
  87. // Assigns a new used size to allocation.
  88. // Has no effect if allocation is nullptr.
  89. // Pre-condition:
  90. // You may not reserve more memory than what is available in total.
  91. // size <= heap_getAllocationSize(allocation)
  92. // If exceeded, size will be limited by the allocation's size.
  93. // Post-condition:
  94. // Returns the assigned size, which is the given size, an exceeded allocation size, or zero for an allocation that does not exist.
  95. uintptr_t heap_setUsedSize(void * const allocation, uintptr_t size);
  96. // A function pointer for destructors.
  97. using HeapDestructorPointer = void(*)(void *toDestroy, void *externalResource);
  98. struct HeapDestructor {
  99. // A function pointer to a method taking toDestroy and externalResource as arguments.
  100. HeapDestructorPointer destructor = nullptr;
  101. // A pointer for freeing external resources owning the allocation.
  102. void *externalResource = nullptr;
  103. // Constructor.
  104. HeapDestructor(HeapDestructorPointer destructor = nullptr, void *externalResource = nullptr)
  105. : destructor(destructor), externalResource(externalResource) {}
  106. };
  107. // Register a destructor function pointer to be called automatically when the allocation is freed.
  108. // externalResource is the second argument that will be given to destructor together with the freed memory to destruct.
  109. void heap_setAllocationDestructor(void * const allocation, const HeapDestructor &destructor);
  110. // Get the use count outside of transactions without locking.
  111. uintptr_t heap_getUseCount(void const * const allocation);
  112. // Pre-condition: The allocation pointer must point to the start of a payload allocated using heap_allocate, no offsets nor other allocators allowed.
  113. // Post-condition: Returns the number of available bytes in the allocation.
  114. // You may not read a single byte outside of it, because it might include padding that ends at uneven addresses.
  115. // To use more memory than requested, you must round it down to whole elements.
  116. // If the element's size is a power of two, you can pre-compute a bit mask using memory_createAlignmentAndMask for rounding down.
  117. uintptr_t heap_getAllocationSize(void const * const allocation);
  118. // Get the alignment of the heap, which depends on the largest cache line size.
  119. uintptr_t heap_getHeapAlignment();
  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 a pointer to the heap allocation's header, which is used to construct safe pointers.
  122. AllocationHeader *heap_getHeader(void * const allocation);
  123. // Call back for each used heap allocation.
  124. // Recycled allocations are not included.
  125. void heap_forAllHeapAllocations(std::function<void(AllocationHeader * header, void * allocation)> callback);
  126. // Called by DSR_MAIN_CALLER when the program starts.
  127. void heap_startingApplication();
  128. // Called by DSR_MAIN_CALLER when the program closes.
  129. void heap_terminatingApplication();
  130. // If terminating the program using std::exit, you can call this first to free all heap memory in the allocator, leaked or not.
  131. void heap_hardExitCleaning();
  132. // Helper methods to prevent cyclic dependencies between strings and buffers when handles must be inlined for performance. Do not call these yourself.
  133. void impl_throwAllocationFailure();
  134. void impl_throwNullException();
  135. void impl_throwIdentityMismatch(uint64_t allocationIdentity, uint64_t pointerIdentity);
  136. }
  137. #endif