UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Transaction.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#if (defined(__AUTORTFM) && __AUTORTFM)
6
7#include "AutoRTFM.h"
8#include "HitSet.h"
9#include "IntervalTree.h"
10#include "IntrusivePool.h"
11#include "Stack.h"
12#include "StackRange.h"
13#include "Stats.h"
14#include "TaskArray.h"
15#include "Utils.h"
16#include "WriteLog.h"
17
18#include <optional>
19
20namespace AutoRTFM
21{
22class FContext;
23
24class AUTORTFM_DISABLE FTransaction final
25{
26 friend class TIntrusivePool<FTransaction, 16>;
27
28 void Resurrect(FContext* const InContext);
29 void Suppress();
30 FTransaction** GetIntrusiveAddress();
31
32 // Constructor.
33 // The transaction is constructed in an initial Closed state.
34 FTransaction(FContext* const Context);
35
36 // Destructor.
37 // The transaction must we in a Done state.
39
40public:
41 // State flow diagram for a transaction:
42 // ┌──────────────────────────────────────┐
43 // │ Uninitialized │
44 // └──────────────────────────────────────┘
45 // │ │
46 // ▼ ▼
47 // ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐
48 // │ OpenInactive │ ←→ │ OpenActive │ ←→ │ ClosedActive │ ←→ │ ClosedInactive │
49 // └────────────────┘ └────────────────┘ └────────────────┘ └────────────────┘
50 // │ │
51 // ▼ ▼
52 // ┌──────────────────────────────────────┐
53 // │ Done │
54 // └──────────────────────────────────────┘
55 enum class EState
56 {
57 // The initial state for the transaction.
58 // Can only transition to OpenActive or ClosedActive.
60 // The transaction is open (not recording writes) and is the current transaction.
61 // Can only transition to OpenInactive, ClosedActive or Done.
63 // The transaction is closed (recording writes) and is the current transaction.
64 // Can only transition to ClosedInactive, OpenActive or Done.
66 // The transaction is open and the current transaction is a descendant.
67 // Can only transition to OpenActive.
69 // The transaction is closed and the current transaction is a descendant.
70 // Can only transition to ClosedActive.
72 // The transaction is committed or aborted.
73 // Once in this state the transaction must be reset with Reset() or destructed.
74 Done,
75 };
76
78
79 // Clears the tracked transaction state and resets back to the default Uninitialized state.
80 void Reset();
81
82 bool IsNested() const { return !!Parent; }
83 FTransaction* GetParent() const { return Parent; }
84
85 // This should just use type displays or ranges. Maybe ranges could even work out great.
86 bool IsNestedWithin(const FTransaction* Other) const
87 {
88 for (const FTransaction* Current = this; ; Current = Current->Parent)
89 {
90 if (!Current)
91 {
92 return false;
93 }
94 else if (Current == Other)
95 {
96 return true;
97 }
98 }
99 }
100
101 inline bool IsScopedTransaction() const { return bIsStackScoped; }
102
103 void DeferUntilCommit(TTask<void()>&&);
104 void DeferUntilPreAbort(TTask<void()>&&);
105 void DeferUntilAbort(TTask<void()>&&);
106 void DeferUntilComplete(TTask<void()>&&);
107 void PushDeferUntilCommitHandler(const void* Key, TTask<void()>&&);
108 void PopDeferUntilCommitHandler(const void* Key);
109 void PopAllDeferUntilCommitHandlers(const void* Key);
110 void PushDeferUntilAbortHandler(const void* Key, TTask<void()>&&);
111 void PopDeferUntilAbortHandler(const void* Key);
112 void PopAllDeferUntilAbortHandlers(const void* Key);
113
114 [[noreturn]] void AbortAndThrow();
116 bool AttemptToCommit();
117
118 // Record that a write is about to occur at the given LogicalAddress of Size bytes.
119 void RecordWrite(void* LogicalAddress, size_t Size, bool bNoMemoryValidation = false);
120 template<unsigned SIZE> void RecordWrite(void* LogicalAddress);
121 template<unsigned SIZE> void RecordWriteInsertedSlow(void* LogicalAddress);
122 template<unsigned SIZE> void RecordWriteNotInsertedSlow(void* LogicalAddress);
123
124 void DidAllocate(void* LogicalAddress, size_t Size);
125 void DidFree(void* LogicalAddress);
126
128 void SetClosedActive();
129 void SetOpenInactive();
130 void SetClosedInactive();
131 void SetActive();
132 void SetInactive();
133 void SetDone();
134
135 // A debug helper that will break to the debugger if the memory validation
136 // hash no longer matches. Useful for isolating where the open write happened.
138
139 // State querying
140 EState State() const { return CurrentState; }
141 bool IsFresh() const;
142 bool IsOpenActive() const { return CurrentState == EState::OpenActive; }
143 bool IsClosedActive() const { return CurrentState == EState::ClosedActive; }
144 bool IsOpenInactive() const { return CurrentState == EState::OpenInactive; }
145 bool IsClosedInactive() const { return CurrentState == EState::ClosedInactive; }
146 bool IsOpen() const { return IsOpenActive() || IsOpenInactive(); }
147 bool IsClosed() const { return IsClosedActive() || IsClosedInactive(); }
148 bool IsActive() const { return IsOpenActive() || IsClosedActive(); }
149 bool IsInactive() const { return IsOpenInactive() || IsClosedInactive(); }
150 bool IsDone() const { return CurrentState == EState::Done; }
151
153 const void* OpenReturnAddress() const { return CurrentOpenReturnAddress; }
154
155 // The stack range represents all stack memory inside the transaction scope
156 inline FStackRange GetStackRange() const { return StackRange; }
157
158 // Returns true if the LogicalAddress is within the stack of the transaction.
159 inline bool IsOnStack(const void* LogicalAddress) const;
160
161private:
162 using FWriteHash = FWriteLog::FHash;
163 using FTaskArray = TTaskArray<TTask<void()>>;
164
165 void Undo();
166
167 void CommitNested();
169
172
173 // Calculates the hash of all the memory written during the transaction and stores it to
174 // RecordedWriteHash.
175 void RecordWriteHash();
176
177 // Compares the hash of all the memory that will be written on abort against the
178 // RecordedWriteHash, raising a warning or error if the hashes are not equal.
179 // See CalculateNestedWriteHash().
180 void ValidateWriteHash() const;
181
182 // Warns / errors when a validation hash mismatch is detected.
184
185 // Returns a hash of all the memory written during this transaction and all parent transactions.
186 // Can be used to ensure that no transaction written memory is modified during an open.
187 // FunctionAddress is the address of the function that triggered this hash.
188 FWriteHash CalculateNestedWriteHash(const void *FunctionAddress) const;
189
190 // Returns a hash of all the memory written during this transaction and all parent transactions.
191 // Can be used to ensure that no transaction written memory is modified during an open.
192 // NumWriteEntries is the number of write entries to hash before stopping.
193 // FunctionAddress is the address of the function that triggered this hash.
194 FWriteHash CalculateNestedWriteHashWithLimit(size_t NumWriteEntries, const void *FunctionAddress) const;
195
196 void CollectStats() const;
197
198 bool ShouldRecordWrite(void* LogicalAddress) const;
199
201
202 template<EState NewState>
203 void SetState();
204
205 FContext* Context;
206
207 // This field is used for two purposes:
208 // - When the transaction is active, the Parent field is used to support transaction nesting.
209 // This field points to our immediate outer nest; it's null when this is the top level.
210 // - When the transaction is "freed"--sitting in the FTransactionPool free list--this field is
211 // used to point to the next free item, forming a linked list of reusable FTransactions.
212 // (See GetIntrusiveAddress.)
213 FTransaction* Parent;
214
215 // Commit tasks run on commit in forward order.
217
218 // Abort tasks run on abort (pre memory rollback) in reverse order.
220
221 // Abort tasks run on abort (post memory rollback) in reverse order.
223
224 // Completion tasks run at the end of the transaction, in forward order.
225 std::optional<FTaskArray> CompletionTasks;
226
227 // If a call to `PopOnCommitHandler` could not find a commit to pop, it is deferred
228 // and tried again on the parent transaction.
230
231 // If a call to `PopOnAbortHandler` could not find an abort to pop, it is deferred
232 // and tried again on the parent transaction.
234
235 // If a call to `PopAllOnCommitHandlers` was used and our transaction successfully
236 // commits, we need to propagate this to the parent too.
238
239 // If a call to `PopAllOnAbortHandlers` was used and our transaction successfully
240 // commits, we need to propagate this to the parent too.
242
248 size_t NumWriteLogsHashed;
250 const void* CurrentOpenReturnAddress;
251 EState CurrentState;
253 bool bIsStackScoped;
254 bool bIsInAllocateFn;
255};
256
257} // namespace AutoRTFM
258
259#endif // (defined(__AUTORTFM) && __AUTORTFM)
OODEFFUNC typedef void(OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)(void *ptr)
#define AUTORTFM_DISABLE
Definition AutoRTFMDefines.h:116
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
void SetState(uint64 Value)
Definition LockFreeList.h:52
uint32 Size
Definition VulkanMemory.cpp:4034
UE_AUTORTFM_ALWAYS_OPEN void DebugBreakIfMemoryValidationFails()
Definition API.cpp:400
Definition API.cpp:57
State
Definition PacketHandler.h:88