FixedSizeFreeList.inl 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. namespace JPH {
  4. template <typename Object>
  5. FixedSizeFreeList<Object>::~FixedSizeFreeList()
  6. {
  7. // Ensure everything is freed before the freelist is destructed
  8. JPH_ASSERT(mNumFreeObjects == 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. delete [] 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 = new ObjectStorage * [mNumPages];
  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;
  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++;
  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 *>(AlignedAlloc(mPageSize * sizeof(ObjectStorage), JPH_CACHE_LINE_SIZE));
  60. mNumObjectsAllocated += mPageSize;
  61. }
  62. }
  63. // Allocation successful
  64. JPH_IF_ENABLE_ASSERTS(--mNumFreeObjects;)
  65. ObjectStorage &storage = GetStorage(first_free);
  66. new (&storage.mData) Object(forward<Parameters>(inParameters)...);
  67. storage.mNextFreeObject = first_free;
  68. return first_free;
  69. }
  70. else
  71. {
  72. // Load next pointer
  73. uint32 new_first_free = GetStorage(first_free).mNextFreeObject;
  74. // Construct a new first free object tag
  75. uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag++) << 32);
  76. // Compare and swap
  77. if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
  78. {
  79. // Allocation successful
  80. JPH_IF_ENABLE_ASSERTS(--mNumFreeObjects;)
  81. ObjectStorage &storage = GetStorage(first_free);
  82. new (&storage.mData) Object(forward<Parameters>(inParameters)...);
  83. storage.mNextFreeObject = first_free;
  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 == 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 = inObjectIndex;
  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 (!is_trivially_destructible<Object>())
  109. {
  110. uint32 object_idx = ioBatch.mFirstObjectIndex;
  111. do
  112. {
  113. ObjectStorage &storage = GetStorage(object_idx);
  114. reinterpret_cast<Object &>(storage.mData).~Object();
  115. object_idx = storage.mNextFreeObject;
  116. }
  117. while (object_idx != cInvalidObjectIndex);
  118. }
  119. // Add to objects free list
  120. for (;;)
  121. {
  122. // Get first object from the list
  123. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag;
  124. uint32 first_free = uint32(first_free_object_and_tag);
  125. // Make it the next pointer of the last object in the batch that is to be freed
  126. GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject = first_free;
  127. // Construct a new first free object tag
  128. uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag++) << 32);
  129. // Compare and swap
  130. if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
  131. {
  132. // Free successful
  133. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects += ioBatch.mNumObjects;)
  134. // Mark the batch as freed
  135. #ifdef JPH_ENABLE_ASSERTS
  136. ioBatch.mNumObjects = uint32(-1);
  137. #endif
  138. return;
  139. }
  140. }
  141. }
  142. }
  143. template <typename Object>
  144. void FixedSizeFreeList<Object>::DestructObject(uint32 inObjectIndex)
  145. {
  146. JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
  147. // Call destructor
  148. ObjectStorage &storage = GetStorage(inObjectIndex);
  149. reinterpret_cast<Object &>(storage.mData).~Object();
  150. // Add to object free list
  151. for (;;)
  152. {
  153. // Get first object from the list
  154. uint64 first_free_object_and_tag = mFirstFreeObjectAndTag;
  155. uint32 first_free = uint32(first_free_object_and_tag);
  156. // Make it the next pointer of the last object in the batch that is to be freed
  157. storage.mNextFreeObject = first_free;
  158. // Construct a new first free object tag
  159. uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag++) << 32);
  160. // Compare and swap
  161. if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
  162. {
  163. // Free successful
  164. JPH_IF_ENABLE_ASSERTS(mNumFreeObjects++;)
  165. return;
  166. }
  167. }
  168. }
  169. template<typename Object>
  170. inline void FixedSizeFreeList<Object>::DestructObject(Object *inObject)
  171. {
  172. uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject;
  173. JPH_ASSERT(index < mNumObjectsAllocated);
  174. DestructObject(index);
  175. }
  176. } // JPH