FixedSizeFreeList.inl 7.3 KB

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