Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
FixedSizeFreeList.inl
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
6
7template <typename Object>
9{
10 // Check if we got our Init call
11 if (mPages != nullptr)
12 {
13 // Ensure everything is freed before the freelist is destructed
14 JPH_ASSERT(mNumFreeObjects.load(memory_order_relaxed) == mNumPages * mPageSize);
15
16 // Free memory for pages
17 uint32 num_pages = mNumObjectsAllocated / mPageSize;
18 for (uint32 page = 0; page < num_pages; ++page)
19 AlignedFree(mPages[page]);
20 Free(mPages);
21 }
22}
23
24template <typename Object>
25void FixedSizeFreeList<Object>::Init(uint inMaxObjects, uint inPageSize)
26{
27 // Check sanity
28 JPH_ASSERT(inPageSize > 0 && IsPowerOf2(inPageSize));
29 JPH_ASSERT(mPages == nullptr);
30
31 // Store configuration parameters
32 mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize;
33 mPageSize = inPageSize;
34 mPageShift = CountTrailingZeros(inPageSize);
35 mObjectMask = inPageSize - 1;
36 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects = mNumPages * inPageSize;)
37
38 // Allocate page table
39 mPages = reinterpret_cast<ObjectStorage **>(Allocate(mNumPages * sizeof(ObjectStorage *)));
40
41 // We didn't yet use any objects of any page
42 mNumObjectsAllocated = 0;
43 mFirstFreeObjectInNewPage = 0;
44
45 // Start with 1 as the first tag
46 mAllocationTag = 1;
47
48 // Set first free object (with tag 0)
49 mFirstFreeObjectAndTag = cInvalidObjectIndex;
50}
51
52template <typename Object>
53template <typename... Parameters>
55{
56 for (;;)
57 {
58 // Get first object from the linked list
59 uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
60 uint32 first_free = uint32(first_free_object_and_tag);
61 if (first_free == cInvalidObjectIndex)
62 {
63 // The free list is empty, we take an object from the page that has never been used before
64 first_free = mFirstFreeObjectInNewPage.fetch_add(1, memory_order_relaxed);
65 if (first_free >= mNumObjectsAllocated)
66 {
67 // Allocate new page
68 lock_guard lock(mPageMutex);
69 while (first_free >= mNumObjectsAllocated)
70 {
71 uint32 next_page = mNumObjectsAllocated / mPageSize;
72 if (next_page == mNumPages)
73 return cInvalidObjectIndex; // Out of space!
74 mPages[next_page] = reinterpret_cast<ObjectStorage *>(AlignedAllocate(mPageSize * sizeof(ObjectStorage), max<size_t>(alignof(ObjectStorage), JPH_CACHE_LINE_SIZE)));
75 mNumObjectsAllocated += mPageSize;
76 }
77 }
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 else
87 {
88 // Load next pointer
89 uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire);
90
91 // Construct a new first free object tag
92 uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
93
94 // Compare and swap
95 if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
96 {
97 // Allocation successful
98 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);)
99 ObjectStorage &storage = GetStorage(first_free);
100 new (&storage.mObject) Object(std::forward<Parameters>(inParameters)...);
101 storage.mNextFreeObject.store(first_free, memory_order_release);
102 return first_free;
103 }
104 }
105 }
106}
107
108template <typename Object>
110{
111 JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
112
113 // Reset next index
114 atomic<uint32> &next_free_object = GetStorage(inObjectIndex).mNextFreeObject;
115 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");
116 next_free_object.store(cInvalidObjectIndex, memory_order_release);
117
118 // Link object in batch to free
119 if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
120 ioBatch.mFirstObjectIndex = inObjectIndex;
121 else
122 GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release);
123 ioBatch.mLastObjectIndex = inObjectIndex;
124 ioBatch.mNumObjects++;
125}
126
127template <typename Object>
129{
130 if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
131 {
132 // Call destructors
133 if constexpr (!std::is_trivially_destructible<Object>())
134 {
135 uint32 object_idx = ioBatch.mFirstObjectIndex;
136 do
137 {
138 ObjectStorage &storage = GetStorage(object_idx);
139 storage.mObject.~Object();
140 object_idx = storage.mNextFreeObject.load(memory_order_relaxed);
141 }
142 while (object_idx != cInvalidObjectIndex);
143 }
144
145 // Add to objects free list
146 ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex);
147 for (;;)
148 {
149 // Get first object from the list
150 uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
151 uint32 first_free = uint32(first_free_object_and_tag);
152
153 // Make it the next pointer of the last object in the batch that is to be freed
154 storage.mNextFreeObject.store(first_free, memory_order_release);
155
156 // Construct a new first free object tag
157 uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
158
159 // Compare and swap
160 if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
161 {
162 // Free successful
163 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);)
164
165 // Mark the batch as freed
166#ifdef JPH_ENABLE_ASSERTS
167 ioBatch.mNumObjects = uint32(-1);
168#endif
169 return;
170 }
171 }
172 }
173}
174
175template <typename Object>
177{
178 JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
179
180 // Call destructor
181 ObjectStorage &storage = GetStorage(inObjectIndex);
182 storage.mObject.~Object();
183
184 // Add to object free list
185 for (;;)
186 {
187 // Get first object from the list
188 uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
189 uint32 first_free = uint32(first_free_object_and_tag);
190
191 // Make it the next pointer of the last object in the batch that is to be freed
192 storage.mNextFreeObject.store(first_free, memory_order_release);
193
194 // Construct a new first free object tag
195 uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
196
197 // Compare and swap
198 if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
199 {
200 // Free successful
201 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);)
202 return;
203 }
204 }
205}
206
207template<typename Object>
209{
210 uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject.load(memory_order_relaxed);
211 JPH_ASSERT(index < mNumObjectsAllocated);
212 DestructObject(index);
213}
214
#define JPH_CACHE_LINE_SIZE
Definition Core.h:521
std::uint64_t uint64
Definition Core.h:485
unsigned int uint
Definition Core.h:481
#define JPH_NAMESPACE_END
Definition Core.h:414
std::uint32_t uint32
Definition Core.h:484
#define JPH_NAMESPACE_BEGIN
Definition Core.h:408
#define JPH_IF_ENABLE_ASSERTS(...)
Definition IssueReporting.h:35
#define JPH_ASSERT(...)
Definition IssueReporting.h:33
constexpr bool IsPowerOf2(T inV)
Check if inV is a power of 2.
Definition Math.h:76
uint CountTrailingZeros(uint32 inValue)
Compute number of trailing zero bits (how many low bits are zero)
Definition Math.h:98
AllocateFunction Allocate
Definition Memory.cpp:68
FreeFunction Free
Definition Memory.cpp:70
AlignedFreeFunction AlignedFree
Definition Memory.cpp:72
AlignedAllocateFunction AlignedAllocate
Definition Memory.cpp:71
@ Object
Start of a new object.
void Init(uint inMaxObjects, uint inPageSize)
Initialize the free list, up to inMaxObjects can be allocated.
Definition FixedSizeFreeList.inl:25
void DestructObjectBatch(Batch &ioBatch)
Lockless destruct batch of objects.
Definition FixedSizeFreeList.inl:128
uint32 ConstructObject(Parameters &&... inParameters)
Lockless construct a new object, inParameters are passed on to the constructor.
Definition FixedSizeFreeList.inl:54
~FixedSizeFreeList()
Destructor.
Definition FixedSizeFreeList.inl:8
void DestructObject(uint32 inObjectIndex)
Lockless destruct an object and return it to the free pool.
Definition FixedSizeFreeList.inl:176
void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex)
Definition FixedSizeFreeList.inl:109
A batch of objects that can be destructed.
Definition FixedSizeFreeList.h:99
uint32 mFirstObjectIndex
Definition FixedSizeFreeList.h:100
uint32 mNumObjects
Definition FixedSizeFreeList.h:102
uint32 mLastObjectIndex
Definition FixedSizeFreeList.h:101