UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
LockFreeFixedSizeAllocator.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "AutoRTFM.h"
7#include "HAL/UnrealMemory.h"
8#include "Misc/NoopCounter.h"
10
17#ifndef USE_NAIVE_TLockFreeFixedSizeAllocator_TLSCacheBase
18#define USE_NAIVE_TLockFreeFixedSizeAllocator_TLSCacheBase (0) // this is useful for find who really leaked
19#endif
20
21template<int32 SIZE, typename TBundleRecycler, typename TTrackingCounter = FNoopCounter>
23{
24 enum
25 {
26 SIZE_PER_BUNDLE = 65536,
27 NUM_PER_BUNDLE = SIZE_PER_BUNDLE / SIZE
28 };
29
30public:
31
33 {
34 static_assert(SIZE >= sizeof(void*) && SIZE % sizeof(void*) == 0, "Blocks in TLockFreeFixedSizeAllocator must be at least the size of a pointer.");
37 }
44
51 inline void* Allocate()
52 {
53#if USE_NAIVE_TLockFreeFixedSizeAllocator_TLSCacheBase
54 return FMemory::Malloc(SIZE);
55#else
56 FThreadLocalCache& TLS = GetTLS();
57
58 if (!TLS.PartialBundle)
59 {
60 if (TLS.FullBundle)
61 {
62 TLS.PartialBundle = TLS.FullBundle;
63 TLS.FullBundle = nullptr;
64 }
65 else
66 {
67 TLS.PartialBundle = GlobalFreeListBundles.Pop();
68 if (!TLS.PartialBundle)
69 {
70 TLS.PartialBundle = (void**)FMemory::Malloc(SIZE_PER_BUNDLE);
71 void **Next = TLS.PartialBundle;
72 for (int32 Index = 0; Index < NUM_PER_BUNDLE - 1; Index++)
73 {
74 void* NextNext = (void*)(((uint8*)Next) + SIZE);
75 *Next = NextNext;
76 Next = (void**)NextNext;
77 }
78 *Next = nullptr;
79 NumFree.Add(NUM_PER_BUNDLE);
80 }
81 }
82 TLS.NumPartial = NUM_PER_BUNDLE;
83 }
84 NumUsed.Increment();
85 NumFree.Decrement();
86 void* Result = (void*)TLS.PartialBundle;
87 TLS.PartialBundle = (void**)*TLS.PartialBundle;
88 TLS.NumPartial--;
89 check(TLS.NumPartial >= 0 && ((!!TLS.NumPartial) == (!!TLS.PartialBundle)));
90 return Result;
91#endif
92 }
93
100 inline void Free(void *Item)
101 {
102#if USE_NAIVE_TLockFreeFixedSizeAllocator_TLSCacheBase
103 return FMemory::Free(Item);
104#else
105 NumUsed.Decrement();
106 NumFree.Increment();
107 FThreadLocalCache& TLS = GetTLS();
108 if (TLS.NumPartial >= NUM_PER_BUNDLE)
109 {
110 if (TLS.FullBundle)
111 {
112 GlobalFreeListBundles.Push(TLS.FullBundle);
113 //TLS.FullBundle = nullptr;
114 }
115 TLS.FullBundle = TLS.PartialBundle;
116 TLS.PartialBundle = nullptr;
117 TLS.NumPartial = 0;
118 }
119 *(void**)Item = (void*)TLS.PartialBundle;
120 TLS.PartialBundle = (void**)Item;
121 TLS.NumPartial++;
122#endif
123 }
124
132 {
133 return NumUsed;
134 }
135
143 {
144 return NumFree;
145 }
146
147private:
148
150 struct FThreadLocalCache
151 {
152 void **FullBundle;
153 void **PartialBundle;
154 int32 NumPartial;
155
156 FThreadLocalCache()
157 : FullBundle(nullptr)
158 , PartialBundle(nullptr)
159 , NumPartial(0)
160 {
161 }
162 };
163
164 FThreadLocalCache& GetTLS()
165 {
167 FThreadLocalCache* TLS = (FThreadLocalCache*)FPlatformTLS::GetTlsValue(TlsSlot);
168 if (!TLS)
169 {
170 TLS = new FThreadLocalCache();
172 }
173 return *TLS;
174 }
175
177 uint32 TlsSlot;
178
180 TBundleRecycler GlobalFreeListBundles;
181
183 TTrackingCounter NumUsed;
184
186 TTrackingCounter NumFree;
187};
188
194template<int32 SIZE, int TPaddingForCacheContention, typename TTrackingCounter = FNoopCounter>
196{
197public:
199 {
200 checkf(!AutoRTFM::IsClosed() || !AutoRTFM::IsOnCurrentTransactionStack(this), TEXT("Not allowed to construct a stack local within a transaction."));
201 }
202
205 {
206 AutoRTFM::PopAllOnAbortHandlers(this);
208 {
209 check(GetNumUsed() == 0);
210 Trim();
211 };
212 }
213
220 void* Allocate(int32 Alignment = MIN_ALIGNMENT)
221 {
222 void* Memory = nullptr;
223
224 // We need the allocation to happen (so we get a valid `Memory`)
225 // pointer to use in the transaction. So we do the actual meat
226 // of the allocation in the open.
228 {
229 NumUsed.Increment();
230 if (Alignment <= 4096)
231 {
232 // Pop from a free list only if Alignment is not large than a memory page size
233 Memory = FreeList.Pop();
234 if (Memory)
235 {
236 NumFree.Decrement();
237 }
238 }
239 if (!Memory)
240 {
241 Memory = FMemory::Malloc(SIZE, Alignment);
242 }
243 };
244
245 // But if we abort the transaction, we need to return the memory
246 // region to the allocator (otherwise we'll leak).
248 {
249 this->Free(Memory);
250 };
251
252 return Memory;
253 }
254
261 void Free(void* Item)
262 {
263 // We defer actually returning `Item` until on-commit time.
264 // If we didn't do this an aborting transaction would not
265 // be able to be undone.
266 UE_AUTORTFM_ONCOMMIT(this, Item)
267 {
268 NumUsed.Decrement();
269 FreeList.Push(Item);
270 NumFree.Increment();
271 };
272 }
273
277 void Trim()
278 {
279 // Similar to `Free`, we need to only do the destructive
280 // trim operation at commit time - otherwise we'll wouldn't
281 // be able to undo it.
283 {
284 while (void* Mem = FreeList.Pop())
285 {
286 FMemory::Free(Mem);
287 NumFree.Decrement();
288 }
289 };
290 }
291
298 typename TTrackingCounter::IntegerType GetNumUsed() const
299 {
300 return AutoRTFM::Open([&] { return NumUsed.GetValue(); });
301 }
302
309 typename TTrackingCounter::IntegerType GetNumFree() const
310 {
311 return AutoRTFM::Open([&] { return NumFree.GetValue(); });
312 }
313
314private:
316
317
319
321 TTrackingCounter NumUsed;
322
324 TTrackingCounter NumFree;
325};
326
332template<int32 SIZE, int TPaddingForCacheContention, typename TTrackingCounter = FNoopCounter>
333class TLockFreeFixedSizeAllocator_TLSCache : public TLockFreeFixedSizeAllocator_TLSCacheBase<SIZE, TLockFreePointerListUnordered<void*, TPaddingForCacheContention>, TTrackingCounter>
334{
335};
336
342template<class T, int TPaddingForCacheContention>
343class TLockFreeClassAllocator : private TLockFreeFixedSizeAllocator<sizeof(T), TPaddingForCacheContention, FNoopCounter>
344{
345public:
352 void* Allocate()
353 {
355 }
356
363 T* New()
364 {
365 return ::new (Allocate()) T();
366 }
367
374 void Free(T *Item)
375 {
376 Item->~T();
378 }
379};
380
386template<class T, int TPaddingForCacheContention>
387class TLockFreeClassAllocator_TLSCache : private TLockFreeFixedSizeAllocator_TLSCache<sizeof(T), TPaddingForCacheContention, FNoopCounter>
388{
389public:
400
407 T* New()
408 {
409 return ::new (Allocate()) T();
410 }
411
418 void Free(T *Item)
419 {
420 Item->~T();
422 }
423};
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
#define UE_NONCOPYABLE(TypeName)
Definition CoreMiscDefines.h:457
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
@ MIN_ALIGNMENT
Definition MemoryBase.h:27
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition UnrealTemplate.h:321
Definition LockFreeFixedSizeAllocator.h:388
T * New()
Definition LockFreeFixedSizeAllocator.h:407
void * Allocate()
Definition LockFreeFixedSizeAllocator.h:396
void Free(T *Item)
Definition LockFreeFixedSizeAllocator.h:418
Definition LockFreeFixedSizeAllocator.h:344
void * Allocate()
Definition LockFreeFixedSizeAllocator.h:352
T * New()
Definition LockFreeFixedSizeAllocator.h:363
void Free(T *Item)
Definition LockFreeFixedSizeAllocator.h:374
Definition LockFreeFixedSizeAllocator.h:23
const TTrackingCounter & GetNumFree() const
Definition LockFreeFixedSizeAllocator.h:142
~TLockFreeFixedSizeAllocator_TLSCacheBase()
Definition LockFreeFixedSizeAllocator.h:39
void * Allocate()
Definition LockFreeFixedSizeAllocator.h:51
const TTrackingCounter & GetNumUsed() const
Definition LockFreeFixedSizeAllocator.h:131
void Free(void *Item)
Definition LockFreeFixedSizeAllocator.h:100
TLockFreeFixedSizeAllocator_TLSCacheBase()
Definition LockFreeFixedSizeAllocator.h:32
Definition LockFreeFixedSizeAllocator.h:334
Definition LockFreeFixedSizeAllocator.h:196
TLockFreeFixedSizeAllocator()
Definition LockFreeFixedSizeAllocator.h:198
TTrackingCounter::IntegerType GetNumUsed() const
Definition LockFreeFixedSizeAllocator.h:298
~TLockFreeFixedSizeAllocator()
Definition LockFreeFixedSizeAllocator.h:204
void Free(void *Item)
Definition LockFreeFixedSizeAllocator.h:261
void * Allocate(int32 Alignment=MIN_ALIGNMENT)
Definition LockFreeFixedSizeAllocator.h:220
void Trim()
Definition LockFreeFixedSizeAllocator.h:277
TTrackingCounter::IntegerType GetNumFree() const
Definition LockFreeFixedSizeAllocator.h:309
void Push(T *NewItem)
Definition LockFreeList.h:849
T * Pop()
Definition LockFreeList.h:858
Definition LockFreeList.h:904
U16 Index
Definition radfft.cpp:71
static uint32 AllocTlsSlot(void)
Definition AndroidPlatformTLS.h:30
static UE_FORCEINLINE_HINT void FreeTlsSlot(uint32 SlotIndex)
Definition AndroidPlatformTLS.h:67
static UE_FORCEINLINE_HINT void * GetTlsValue(uint32 SlotIndex)
Definition AndroidPlatformTLS.h:57
static UE_FORCEINLINE_HINT void SetTlsValue(uint32 SlotIndex, void *Value)
Definition AndroidPlatformTLS.h:47
static const uint32 InvalidTlsSlot
Definition GenericPlatformTLS.h:13
static UE_FORCEINLINE_HINT bool IsValidTlsSlot(uint32 SlotIndex)
Definition GenericPlatformTLS.h:20
static FORCENOINLINE CORE_API void Free(void *Original)
Definition UnrealMemory.cpp:685