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>
26{
27 // Check sanity
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;
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);
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);
83 storage.mNextFreeObject.store(first_free, memory_order_release);
85 }
86 else
87 {
88 // Load next pointer
89 uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire);
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);
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 }
107
108template <typename Object>
110{
111 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");
112 JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
113
114 // Link object in batch to free
115 if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
116 ioBatch.mFirstObjectIndex = inObjectIndex;
117 else
118 GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release);
119 ioBatch.mLastObjectIndex = inObjectIndex;
120 ioBatch.mNumObjects++;
121}
122
123template <typename Object>
125{
126 if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
127 {
128 // Call destructors
129 if constexpr (!is_trivially_destructible<Object>())
130 {
131 uint32 object_idx = ioBatch.mFirstObjectIndex;
132 do
133 {
134 ObjectStorage &storage = GetStorage(object_idx);
135 storage.mObject.~Object();
136 object_idx = storage.mNextFreeObject.load(memory_order_relaxed);
137 }
138 while (object_idx != cInvalidObjectIndex);
139 }
140
141 // Add to objects free list
142 ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex);
143 for (;;)
144 {
145 // Get first object from the list
146 uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
148
149 // Make it the next pointer of the last object in the batch that is to be freed
150 storage.mNextFreeObject.store(first_free, memory_order_release);
151
152 // Construct a new first free object tag
153 uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
154
155 // Compare and swap
156 if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
157 {
158 // Free successful
159 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);)
160
161 // Mark the batch as freed
163 ioBatch.mNumObjects = uint32(-1);
164#endif
165 return;
166 }
167 }
168 }
169}
170
171template <typename Object>
173{
174 JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
175
176 // Call destructor
177 ObjectStorage &storage = GetStorage(inObjectIndex);
178 storage.mObject.~Object();
179
180 // Add to object free list
181 for (;;)
182 {
183 // Get first object from the list
184 uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire);
186
187 // Make it the next pointer of the last object in the batch that is to be freed
188 storage.mNextFreeObject.store(first_free, memory_order_release);
189
190 // Construct a new first free object tag
191 uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32);
192
193 // Compare and swap
194 if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release))
195 {
196 // Free successful
197 JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);)
198 return;
199 }
200 }
201}
202
203template<typename Object>
205{
206 uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject.load(memory_order_relaxed);
207 JPH_ASSERT(index < mNumObjectsAllocated);
208 DestructObject(index);
209}
210
#define JPH_CACHE_LINE_SIZE
Definition Core.h:466
std::uint64_t uint64
Definition Core.h:443
unsigned int uint
Definition Core.h:439
#define JPH_NAMESPACE_END
Definition Core.h:367
std::uint32_t uint32
Definition Core.h:442
#define JPH_NAMESPACE_BEGIN
Definition Core.h:361
#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:73
uint CountTrailingZeros(uint32 inValue)
Compute number of trailing zero bits (how many low bits are zero)
Definition Math.h:95
AllocateFunction Allocate
Definition Memory.cpp:59
FreeFunction Free
Definition Memory.cpp:60
AlignedFreeFunction AlignedFree
Definition Memory.cpp:62
AlignedAllocateFunction AlignedAllocate
Definition Memory.cpp:61
@ 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:124
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:172
void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex)
Definition FixedSizeFreeList.inl:109
Definition Reference.h:204
A batch of objects that can be destructed.
Definition FixedSizeFreeList.h:97