FixedSizeFreeList.inl 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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(GetStorage(inObjectIndex).mNextFreeObject.load(memory_order_relaxed) == inObjectIndex, "Trying to add a object to the batch that is already in a free list");
  98. JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
  99. // Link object in batch to free
  100. if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
  101. ioBatch.mFirstObjectIndex = inObjectIndex;
  102. else
  103. GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release);
  104. ioBatch.mLastObjectIndex = inObjectIndex;
  105. ioBatch.mNumObjects++;
  106. }
  107. template <typename Object>
  108. void FixedSizeFreeList<Object>::DestructObjectBatch(Batch &ioBatch)
  109. {
  110. if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
  111. {
  112. // Call destructors
  113. if constexpr (!is_trivially_destructible<Object>())
  114. {
  115. uint32 object_idx = ioBatch.mFirstObjectIndex;
  116. do
  117. {
  118. ObjectStorage &storage = GetStorage(object_idx);
  119. storage.mObject.~Object();
  120. object_idx = storage.mNextFreeObject.load(memory_order_relaxed);
  121. }
  122. while (object_idx != cInvalidObjectIndex);
  123. }
  124. // Add to objects free list
  125. ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex);
  126. for (;;)
  127. {
  128. // Get first object from the list
  129. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
  130. uint32 first_free = uint32(first_free_object_and_tag);
  131. // Make it the next pointer of the last object in the batch that is to be freed
  132. storage.mNextFreeObject.store(first_free, memory_order_release);
  133. // Construct a new first free object tag
  134. uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
  135. // Compare and swap
  136. if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
  137. {
  138. // Free successful
  139. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);)
  140. // Mark the batch as freed
  141. #ifdef JPH_ENABLE_ASSERTS
  142. ioBatch.mNumObjects = uint32(-1);
  143. #endif
  144. return;
  145. }
  146. }
  147. }
  148. }
  149. template <typename Object>
  150. void FixedSizeFreeList<Object>::DestructObject(uint32 inObjectIndex)
  151. {
  152. JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
  153. // Call destructor
  154. ObjectStorage &storage = GetStorage(inObjectIndex);
  155. storage.mObject.~Object();
  156. // Add to object free list
  157. for (;;)
  158. {
  159. // Get first object from the list
  160. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
  161. uint32 first_free = uint32(first_free_object_and_tag);
  162. // Make it the next pointer of the last object in the batch that is to be freed
  163. storage.mNextFreeObject.store(first_free, memory_order_release);
  164. // Construct a new first free object tag
  165. uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
  166. // Compare and swap
  167. if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
  168. {
  169. // Free successful
  170. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);)
  171. return;
  172. }
  173. }
  174. }
  175. template<typename Object>
  176. inline void FixedSizeFreeList<Object>::DestructObject(Object *inObject)
  177. {
  178. uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject.load(memory_order_relaxed);
  179. JPH_ASSERT(index < mNumObjectsAllocated);
  180. DestructObject(index);
  181. }
  182. JPH_NAMESPACE_END