UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
FMemory.inl
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#if !defined(FMEMORY_INLINE_GMalloc)
4# error "FMEMORY_INLINE_GMalloc should be defined before including this file. Possibly FMemory.inl is included directly instead of including Memory.h"
5#endif
6
8#include "AutoRTFM.h"
9
10struct FMemory;
12
15
16
18{
19 // AutoRTFM: For non-transactional code, all of these calls optimize away and the
20 // behavior is the same as it always has been.
21 // For transactional code, we call the allocator in the 'open' as an optimization, so that
22 // we don't end up keeping track of the writes to the allocator's internal data structures.
23 // This is because allocators are already transactional - malloc can be rolled back by
24 // calling free.
25 void* Ptr = AutoRTFM::Open([Count, Alignment]
26 {
27 void* Alloc = nullptr;
29 {
30 Alloc = FMemory::MallocExternal(Count, Alignment);
31 }
32 else
33 {
35 FScopedMallocTimer Timer(0);
36 Alloc = FMEMORY_INLINE_GMalloc->Malloc(Count, Alignment);
37 }
38 // optional tracking of every allocation
39 LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, Alloc, Count, ELLMTag::Untagged, ELLMAllocType::FMalloc));
40 return Alloc;
41 });
42
43 // AutoRTFM: This is a no-op for non-transactional code.
44 // For transactional code, this defers a call to Free if the transaction aborts,
45 // so that rolling back this allocation will end up freeing the memory.
46 AutoRTFM::OnAbort([Ptr]
47 {
48 // Disable the code analysis warning that complains that Free is being passed
49 // a pointer that may be null. Free explicitly handles this case already.
50 FMemory::Free(Ptr); //-V575
51 });
52
53 return AutoRTFM::DidAllocate(Ptr, Count);
54}
55
57{
58 if (AutoRTFM::IsClosed())
59 {
60 // AutoRTFM: For transactional code, we have to do a little dance to handle Realloc
61 // properly. We turn realloc into Malloc + Memcpy + Free and never call into the
62 // underlying allocator's realloc implementation. That's required, because if we
63 // were to call into actual realloc, it could end up freeing the old memory. And
64 // if we then aborted our transaction, we would end up rolling back pointers to
65 // point to that old allocation, and it wouldn't be possible to get back that original
66 // allocation at the same address.
67 //
68 // There is an opportunity here, in that if the original pointer was allocated within
69 // this transaction, then the above doesn't apply since rolling back the transaction
70 // would free that memory and the resulting heap wouldn't point at that memory. So
71 // if we new that the Original pointer was allocated in this transaction, we could
72 // call into the underlying realloc - however we would also have to account for the
73 // malloc deferring a call to free, so we would also have to erase that call to free.
74
75 void* Ptr = nullptr;
76
77 // Depending on the underlying implementation `Malloc` here, even if `Count` is zero,
78 // could do an actual allocation (it is implementation-defined what occurs). So
79 // instead, since we are fine to return null with a `Count` of zero, we check for
80 // that case and skip the `Malloc` call entirely.
81 if (Count > 0)
82 {
83 Ptr = FMemory::Malloc(Count, Alignment);
84
85 if (!Ptr)
86 {
87 return nullptr;
88 }
89 }
90
91 if (Original)
92 {
93 if (Ptr)
94 {
96 SIZE_T CopyCount = FGenericPlatformMath::Min(Count, OriginalCount); // handle the case where the new size is smaller
97
99 }
100
102 }
103
104 return Ptr;
105 }
106
107 // optional tracking -- a realloc with an Original pointer of null is equivalent
108 // to malloc() so there's nothing to free
110 LLM_IF_ENABLED(if (Original != nullptr) FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default, Original, ELLMAllocType::FMalloc));
111
112 void* Ptr;
114 {
115 Ptr = FMemory::ReallocExternal(Original, Count, Alignment);
116 }
117 else
118 {
120 FScopedMallocTimer Timer(1);
121 Ptr = FMEMORY_INLINE_GMalloc->Realloc(Original, Count, Alignment);
122 }
123
124 if (Ptr != Original)
125 {
126 // If the pointer we've got back from realloc is new memory, we are
127 // assuming the old memory was free'd.
128 AutoRTFM::DidFree(Original);
129 }
130
131 // optional tracking of every allocation - a realloc with a Count of zero is equivalent to a call
132 // to free() and will return a null pointer which does not require tracking. If realloc returns null
133 // for some other reason (like failure to allocate) there's also no reason to track it
134 LLM_IF_ENABLED(if (Ptr != nullptr) FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, Ptr, Count, ELLMTag::Untagged, ELLMAllocType::FMalloc));
135
136 return Ptr;
137}
138
140{
141 if (!Original)
142 {
143 FScopedMallocTimer Timer(3);
144 return;
145 }
146
147 // AutoRTFM: For transactional code, in order to support the transaction
148 // aborting and needing to 'roll back' the Free, we defer the actual
149 // free until commit time.
151 {
152 // optional tracking of every allocation
153 LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default, Original, ELLMAllocType::FMalloc));
154
156 {
158 return;
159 }
161 FScopedMallocTimer Timer(2);
163
164 AutoRTFM::DidFree(Original);
165 };
166}
167
169{
170 SIZE_T Result;
172 {
174 {
176 }
177 else
178 {
179 SIZE_T Size = 0;
180 const bool bGotSize = FMEMORY_INLINE_GMalloc->GetAllocationSize(Original, Size);
181 Result = bGotSize ? Size : 0;
182
183 // This folds away at compile time so that the check is only ever performed inside transactional
184 // code paths. The check is to ensure that the allocator used will return the correct allocation
185 // size, which is a cornerstone requirement for AutoRTFM to function.
186 if (AutoRTFM::IsClosed())
187 {
188 checkf(bGotSize, TEXT("For AutoRTFM to function it must be able to get the size of an allocation"));
189 }
190 }
191 };
192
193 return Result;
194}
195
197{
198 void* Ptr = nullptr; // Silence bogus static analysis warnings.
199
200 // AutoRTFM: For non-transactional code, all of these calls optimize away and the
201 // behavior is the same as it always has been.
202 // For transactional code, we call the allocator in the 'open' as an optimization, so that
203 // we don't end up keeping track of the writes to the allocator's internal data structures.
204 // This is because allocators are already transactional - malloc can be rolled back by
205 // calling free.
207 {
209 {
210 Ptr = FMemory::MallocZeroedExternal(Count, Alignment);
211 }
212 else
213 {
215 FScopedMallocTimer Timer(0);
216 Ptr = FMEMORY_INLINE_GMalloc->MallocZeroed(Count, Alignment);
217 }
218 // optional tracking of every allocation
219 LLM_IF_ENABLED(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, Ptr, Count, ELLMTag::Untagged, ELLMAllocType::FMalloc));
220 };
221
222 // AutoRTFM: This is a no-op for non-transactional code.
223 // For transactional code, this defers a call to Free if the transaction aborts,
224 // so that rolling back this allocation will end up freeing the memory.
225 AutoRTFM::OnAbort([Ptr]
226 {
227 // Disable the code analysis warning that complains that Free is being passed
228 // a pointer that may be null. Free explicitly handles this case already.
229 FMemory::Free(Ptr); //-V575
230 });
231
232 return AutoRTFM::DidAllocate(Ptr, Count);
233}
234
236{
237 SIZE_T Result;
239 {
241 {
242 Result = Count;
243 }
244 else
245 {
246 Result = FMEMORY_INLINE_GMalloc->QuantizeSize(Count, Alignment);
247 }
248 };
249
250 return Result;
251}
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
#define UNLIKELY(x)
Definition Platform.h:857
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
FORCEINLINE SIZE_T FMemory_QuantizeSizeInline(SIZE_T Count, uint32 Alignment)
Definition FMemory.inl:235
void FMemory_FreeInline(void *Original)
Definition FMemory.inl:139
FORCEINLINE void * FMemory_MallocInline(SIZE_T Count, uint32 Alignment)
Definition FMemory.inl:17
SIZE_T FMemory_GetAllocSizeInline(void *Original)
Definition FMemory.inl:168
FORCEINLINE void * FMemory_MallocZeroedInline(SIZE_T Count, uint32 Alignment)
Definition FMemory.inl:196
FORCEINLINE void * FMemory_ReallocInline(void *Original, SIZE_T Count, uint32 Alignment)
Definition FMemory.inl:56
#define LLM_IF_ENABLED(...)
Definition LowLevelMemTracker.h:1093
#define LLM_REALLOC_SCOPE(...)
Definition LowLevelMemTracker.h:1106
#define FMEMORY_INLINE_GMalloc
Definition Memory.h:17
UE_FORCEINLINE_HINT void DoGamethreadHook(int32 Index)
Definition UnrealMemory.h:32
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
static constexpr UE_FORCEINLINE_HINT T Min(T A, T B)
Definition GenericPlatformMath.h:985
Definition UnrealMemory.h:94
static FORCENOINLINE CORE_API void Free(void *Original)
Definition UnrealMemory.cpp:685
static CORE_API SIZE_T GetAllocSizeExternal(void *Original)
Definition UnrealMemory.cpp:524
static CORE_API void * ReallocExternal(void *Original, SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT)
Definition UnrealMemory.cpp:501
static UE_FORCEINLINE_HINT void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:160
static CORE_API void * MallocZeroedExternal(SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT)
Definition UnrealMemory.cpp:535
static CORE_API void * MallocExternal(SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT)
Definition UnrealMemory.cpp:491
static CORE_API void FreeExternal(void *Original)
Definition UnrealMemory.cpp:511
static FORCENOINLINE CORE_API SIZE_T GetAllocSize(void *Original)
Definition UnrealMemory.cpp:690
Definition UnrealMemory.h:75