FixedSizeFreeList.inl 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. JPH_NAMESPACE_BEGIN
  4. template <typename Object>
  5. FixedSizeFreeList<Object>::~FixedSizeFreeList()
  6. {
  7. // Ensure everything is freed before the freelist is destructed
  8. JPH_ASSERT(mNumFreeObjects.load(memory_order_relaxed) == mNumPages * mPageSize);
  9. // Free memory for pages
  10. uint32 num_pages = mNumObjectsAllocated / mPageSize;
  11. for (uint32 page = 0; page < num_pages; ++page)
  12. AlignedFree(mPages[page]);
  13. Free(mPages);
  14. }
  15. template <typename Object>
  16. void FixedSizeFreeList<Object>::Init(uint inMaxObjects, uint inPageSize)
  17. {
  18. // Check sanity
  19. JPH_ASSERT(inPageSize > 0 && IsPowerOf2(inPageSize));
  20. JPH_ASSERT(mPages == nullptr);
  21. // Store configuration parameters
  22. mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize;
  23. mPageSize = inPageSize;
  24. mPageShift = CountTrailingZeros(inPageSize);
  25. mObjectMask = inPageSize - 1;
  26. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects = mNumPages * inPageSize;)
  27. // Allocate page table
  28. mPages = reinterpret_cast<ObjectStorage **>(Allocate(mNumPages * sizeof(ObjectStorage *)));
  29. // We didn't yet use any objects of any page
  30. mNumObjectsAllocated = 0;
  31. mFirstFreeObjectInNewPage = 0;
  32. // Start with 1 as the first tag
  33. mAllocationTag = 1;
  34. // Set first free object (with tag 0)
  35. mFirstFreeObjectAndTag = cInvalidObjectIndex;
  36. }
  37. template <typename Object>
  38. template <typename... Parameters>
  39. uint32 FixedSizeFreeList<Object>::ConstructObject(Parameters &&... inParameters)
  40. {
  41. for (;;)
  42. {
  43. // Get first object from the linked list
  44. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
  45. uint32 first_free = uint32(first_free_object_and_tag);
  46. if (first_free == cInvalidObjectIndex)
  47. {
  48. // The free list is empty, we take an object from the page that has never been used before
  49. first_free = mFirstFreeObjectInNewPage.fetch_add(1, memory_order_relaxed);
  50. if (first_free >= mNumObjectsAllocated)
  51. {
  52. // Allocate new page
  53. lock_guard lock(mPageMutex);
  54. while (first_free >= mNumObjectsAllocated)
  55. {
  56. uint32 next_page = mNumObjectsAllocated / mPageSize;
  57. if (next_page == mNumPages)
  58. return cInvalidObjectIndex; // Out of space!
  59. mPages[next_page] = reinterpret_cast<ObjectStorage *>(AlignedAllocate(mPageSize * sizeof(ObjectStorage), max<size_t>(alignof(ObjectStorage), JPH_CACHE_LINE_SIZE)));
  60. mNumObjectsAllocated += mPageSize;
  61. }
  62. }
  63. // Allocation successful
  64. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);)
  65. ObjectStorage &storage = GetStorage(first_free);
  66. ::new (&storage.mObject) Object(std::forward<Parameters>(inParameters)...);
  67. storage.mNextFreeObject.store(first_free, memory_order_release);
  68. return first_free;
  69. }
  70. else
  71. {
  72. // Load next pointer
  73. uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire);
  74. // Construct a new first free object tag
  75. uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
  76. // Compare and swap
  77. if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
  78. {
  79. // Allocation successful
  80. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);)
  81. ObjectStorage &storage = GetStorage(first_free);
  82. ::new (&storage.mObject) Object(std::forward<Parameters>(inParameters)...);
  83. storage.mNextFreeObject.store(first_free, memory_order_release);
  84. return first_free;
  85. }
  86. }
  87. }
  88. }
  89. template <typename Object>
  90. void FixedSizeFreeList<Object>::AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex)
  91. {
  92. JPH_ASSERT(GetStorage(inObjectIndex).mNextFreeObject.load(memory_order_relaxed) == inObjectIndex, "Trying to add a object to the batch that is already in a free list");
  93. JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
  94. // Link object in batch to free
  95. if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
  96. ioBatch.mFirstObjectIndex = inObjectIndex;
  97. else
  98. GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release);
  99. ioBatch.mLastObjectIndex = inObjectIndex;
  100. ioBatch.mNumObjects++;
  101. }
  102. template <typename Object>
  103. void FixedSizeFreeList<Object>::DestructObjectBatch(Batch &ioBatch)
  104. {
  105. if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
  106. {
  107. // Call destructors
  108. if constexpr (!is_trivially_destructible<Object>())
  109. {
  110. uint32 object_idx = ioBatch.mFirstObjectIndex;
  111. do
  112. {
  113. ObjectStorage &storage = GetStorage(object_idx);
  114. storage.mObject.~Object();
  115. object_idx = storage.mNextFreeObject.load(memory_order_relaxed);
  116. }
  117. while (object_idx != cInvalidObjectIndex);
  118. }
  119. // Add to objects free list
  120. ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex);
  121. for (;;)
  122. {
  123. // Get first object from the list
  124. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
  125. uint32 first_free = uint32(first_free_object_and_tag);
  126. // Make it the next pointer of the last object in the batch that is to be freed
  127. storage.mNextFreeObject.store(first_free, memory_order_release);
  128. // Construct a new first free object tag
  129. uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
  130. // Compare and swap
  131. if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
  132. {
  133. // Free successful
  134. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);)
  135. // Mark the batch as freed
  136. #ifdef JPH_ENABLE_ASSERTS
  137. ioBatch.mNumObjects = uint32(-1);
  138. #endif
  139. return;
  140. }
  141. }
  142. }
  143. }
  144. template <typename Object>
  145. void FixedSizeFreeList<Object>::DestructObject(uint32 inObjectIndex)
  146. {
  147. JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
  148. // Call destructor
  149. ObjectStorage &storage = GetStorage(inObjectIndex);
  150. storage.mObject.~Object();
  151. // Add to object free list
  152. for (;;)
  153. {
  154. // Get first object from the list
  155. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
  156. uint32 first_free = uint32(first_free_object_and_tag);
  157. // Make it the next pointer of the last object in the batch that is to be freed
  158. storage.mNextFreeObject.store(first_free, memory_order_release);
  159. // Construct a new first free object tag
  160. uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
  161. // Compare and swap
  162. if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
  163. {
  164. // Free successful
  165. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);)
  166. return;
  167. }
  168. }
  169. }
  170. template<typename Object>
  171. inline void FixedSizeFreeList<Object>::DestructObject(Object *inObject)
  172. {
  173. uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject.load(memory_order_relaxed);
  174. JPH_ASSERT(index < mNumObjectsAllocated);
  175. DestructObject(index);
  176. }
  177. JPH_NAMESPACE_END