UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
TransactionallySafeSpscQueue.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
7#include "Misc/Optional.h"
8#include "Misc/ScopeLock.h"
12
28template<typename T, typename AllocatorType = FMemory>
30{
31public:
32 using ElementType = T;
33
35
37 {
38 FNode* Node = ::new(AllocatorType::Malloc(sizeof(FNode), alignof(FNode))) FNode;
39 Head = First = Tail = TailCopy = Node;
40 }
41
43 {
44 // Nobody should have a reference to this class anymore, but we still take the mutex to
45 // guarantee that the queue isn't modified by another thread while a transaction is underway.
46 UE::TScopeLock Lock(Mutex);
47 FNode* Node = First;
48
49 // Delete all sentinel or unoccupied nodes.
50 bool bContinue = false;
51 do
52 {
53 FNode* Next = Node->Next;
54 bContinue = Node != Tail;
55 AllocatorType::Free(Node);
56 Node = Next;
57 }
58 while (bContinue);
59
60 // Destroy and free all occupied nodes.
61 while (Node != nullptr)
62 {
63 FNode* Next = Node->Next;
64 DestructItem((ElementType*)&Node->Value);
65 AllocatorType::Free(Node);
66 Node = Next;
67 }
68 }
69
70 template <typename... ArgTypes>
71 void Enqueue(ArgTypes&&... Args)
72 {
73 FNode* Node = AllocNode();
74 ::new((void*)&Node->Value) ElementType(Forward<ArgTypes>(Args)...);
75
76 UE::TScopeLock Lock(Mutex);
77 Head->Next = Node;
78 Head = Node;
79 }
80
81 // Returns NullOpt if the queue is empty.
82 // Spin-waiting on Dequeue from within an AutoRTFM transaction is likely to deadlock,
83 // as the matching Enqueue will be waiting on Mutex.
85 {
86 Mutex.Lock();
87 FNode* LocalTail = Tail;
88 FNode* LocalTailNext = LocalTail->Next;
89 Mutex.Unlock();
90
91 if (LocalTailNext == nullptr)
92 {
93 return NullOpt;
94 }
95
99
100 Mutex.Lock();
101 Tail = LocalTailNext;
102 Mutex.Unlock();
103
104 return Value;
105 }
106
108 {
110 if (LocalElement.IsSet())
111 {
113 return true;
114 }
115
116 return false;
117 }
118
119 [[nodiscard]] bool IsEmpty() const
120 {
121 UE::TScopeLock Lock(Mutex);
122 FNode* LocalTail = Tail;
123 FNode* LocalTailNext = LocalTail->Next;
124
125 return LocalTailNext == nullptr;
126 }
127
128 // As there can be only one consumer, a consumer can safely "peek" the tail of the queue.
129 // Returns a pointer to the tail if the queue is not empty, nullptr otherwise.
130 // There's no overload with TOptional as it doesn't support references.
132 {
133 UE::TScopeLock Lock(Mutex);
134 FNode* LocalTail = Tail;
135 FNode* LocalTailNext = LocalTail->Next;
136
137 return LocalTailNext
138 ? (ElementType*)&LocalTailNext->Value
139 : nullptr;
140 }
141
142private:
143 struct FNode
144 {
145 FNode* Next = nullptr;
147 };
148
149private:
150 FNode* AllocFromCache()
151 {
152 // The mutex must _already be held_ when calling this function.
153 FNode* Node = First;
154 First = First->Next;
155 Node->Next = nullptr;
156 return Node;
157 }
158
159 FNode* AllocNode()
160 {
161 // First, we attempt to allocate a node from internal node cache.
162 {
163 UE::TScopeLock Lock(Mutex);
164 if (First != TailCopy)
165 {
166 return AllocFromCache();
167 }
168
169 TailCopy = Tail;
170 if (First != TailCopy)
171 {
172 return AllocFromCache();
173 }
174 }
175
176 // Our cache is empty; allocate via ::operator new() instead.
177 return ::new(AllocatorType::Malloc(sizeof(FNode), alignof(FNode))) FNode();
178 }
179
180private:
181 // This mutex guards all accesses to this structure or to FNode::Next.
182 mutable UE::FTransactionallySafeMutex Mutex;
183
184 // consumer part
185 // accessed mainly by consumer, infrequently by producer
186 FNode* Tail; // tail of the queue
187 // producer part
188 // accessed only by producer
189 FNode* Head; // head of the queue
190 FNode* First; // last unused node (tail of node cache)
191 FNode* TailCopy; // points to the initially-created tail
192};
constexpr FNullOpt NullOpt
Definition Optional.h:15
FORCEINLINE constexpr void DestructItem(ElementType *Element)
Definition MemoryOps.h:56
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTempIfPossible(T &&Obj) noexcept
Definition UnrealTemplate.h:538
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
Definition TransactionallySafeSpscQueue.h:30
TTransactionallySafeSpscQueue()
Definition TransactionallySafeSpscQueue.h:36
T ElementType
Definition TransactionallySafeSpscQueue.h:32
TOptional< ElementType > Dequeue()
Definition TransactionallySafeSpscQueue.h:84
UE_NONCOPYABLE(TTransactionallySafeSpscQueue)
bool IsEmpty() const
Definition TransactionallySafeSpscQueue.h:119
void Enqueue(ArgTypes &&... Args)
Definition TransactionallySafeSpscQueue.h:71
bool Dequeue(ElementType &OutElem)
Definition TransactionallySafeSpscQueue.h:107
ElementType * Peek() const
Definition TransactionallySafeSpscQueue.h:131
~TTransactionallySafeSpscQueue()
Definition TransactionallySafeSpscQueue.h:42
Definition Mutex.h:18
void Lock()
Definition Mutex.h:43
void Unlock()
Definition Mutex.h:53
Definition ScopeLock.h:21
Definition Optional.h:131
Definition TypeCompatibleBytes.h:24