UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SpscQueue.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"
9#include "Misc/Optional.h"
10#include <atomic>
11
16template<typename T, typename AllocatorType = FMemory>
17class TSpscQueue final
18{
19public:
20 using ElementType = T;
21
23
25 {
26 FNode* Node = ::new(AllocatorType::Malloc(sizeof(FNode), alignof(FNode))) FNode;
27 Tail.store(Node, std::memory_order_relaxed);
28 Head = First = TailCopy = Node;
29 NumElems = 0;
30 }
31
33 {
34 FNode* Node = First;
35 FNode* LocalTail = Tail.load(std::memory_order_relaxed);
36
37 // Delete all nodes which are the sentinel or unoccupied
38 bool bContinue = false;
39 do
40 {
41 FNode* Next = Node->Next.load(std::memory_order_relaxed);
42 bContinue = Node != LocalTail;
43 AllocatorType::Free(Node);
44 Node = Next;
45 } while (bContinue);
46
47 // Delete all nodes which are occupied, destroying the element first
48 while (Node != nullptr)
49 {
50 FNode* Next = Node->Next.load(std::memory_order_relaxed);
51 DestructItem((ElementType*)&Node->Value);
52 AllocatorType::Free(Node);
53 Node = Next;
54 }
55 }
56
57 template <typename... ArgTypes>
58 void Enqueue(ArgTypes&&... Args)
59 {
60 FNode* Node = AllocNode();
61 ::new((void*)&Node->Value) ElementType(Forward<ArgTypes>(Args)...);
62
63 Head->Next.store(Node, std::memory_order_release);
64 Head = Node;
65
66 NumElems++;
67 }
68
69 // returns empty TOptional if queue is empty
71 {
72 FNode* LocalTail = Tail.load(std::memory_order_relaxed);
73 FNode* LocalTailNext = LocalTail->Next.load(std::memory_order_acquire);
74 if (LocalTailNext == nullptr)
75 {
76 return {};
77 }
78
82
83 Tail.store(LocalTailNext, std::memory_order_release);
84
85 if (Value.IsSet())
86 {
87 NumElems--;
88 }
89 return Value;
90 }
91
93 {
95 if (LocalElement.IsSet())
96 {
98 return true;
99 }
100
101 return false;
102 }
103
104 [[nodiscard]] bool IsEmpty() const
105 {
106 return !NumElems;
107 }
108
109 [[nodiscard]] int32 Num() const
110 {
111 return NumElems;
112 }
113
114 // as there can be only one consumer, a consumer can safely "peek" the tail of the queue.
115 // returns a pointer to the tail if the queue is not empty, nullptr otherwise
116 // there's no overload with TOptional as it doesn't support references
118 {
119 FNode* LocalTail = Tail.load(std::memory_order_relaxed);
120 FNode* LocalTailNext = LocalTail->Next.load(std::memory_order_acquire);
121
122 if (LocalTailNext == nullptr)
123 {
124 return nullptr;
125 }
126
127 return (ElementType*)&LocalTailNext->Value;
128 }
129
130private:
131 struct FNode
132 {
133 std::atomic<FNode*> Next{ nullptr };
135 };
136
137public:
138 //
139 // Allows the single consumer to iterate the contents of the queue without popping from it.
140 //
141 // The single producer may continue to insert items in the queue while the consumer is iterating.
142 // These new items may or may not be seen by the consumer, since the consumer might have finished
143 // iterating before reaching those new elements.
144 //
146 {
147 TSpscQueue::FNode* Current;
148
149 FIterator(const TSpscQueue& Queue)
150 : Current(Queue.Tail.load(std::memory_order_relaxed))
151 {
152 ++(*this);
153 }
154
156 {
157 Current = Current->Next.load(std::memory_order_acquire);
158 return *this;
159 }
160
161 bool operator== (std::nullptr_t) const
162 {
163 return Current == nullptr;
164 }
165
166 const ElementType& operator* () const
167 {
168 return Current->Value.GetUnchecked();
169 }
170 };
171
173 {
174 return FIterator(*this);
175 }
176
177 std::nullptr_t end() const
178 {
179 return nullptr;
180 }
181
182private:
183 FNode* AllocNode()
184 {
185 // first tries to allocate node from internal node cache,
186 // if attempt fails, allocates node via ::operator new()
187
188 auto AllocFromCache = [this]()
189 {
190 FNode* Node = First;
191 First = First->Next;
192 Node->Next.store(nullptr, std::memory_order_relaxed);
193 return Node;
194 };
195
196 if (First != TailCopy)
197 {
198 return AllocFromCache();
199 }
200
201 TailCopy = Tail.load(std::memory_order_acquire);
202 if (First != TailCopy)
203 {
204 return AllocFromCache();
205 }
206
207 return ::new(AllocatorType::Malloc(sizeof(FNode), alignof(FNode))) FNode();
208 }
209
210private:
211 // consumer part
212 // accessed mainly by consumer, infrequently by producer
213 std::atomic<FNode*> Tail; // tail of the queue
214
215 // producer part
216 // accessed only by producer
217 FNode* Head; // head of the queue
218
219 FNode* First; // last unused node (tail of node cache)
220 FNode* TailCopy; // helper (points somewhere between First and Tail)
221
222 std::atomic<int32> NumElems;
223};
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
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
Definition SpscQueue.h:18
UE_NONCOPYABLE(TSpscQueue)
ElementType * Peek() const
Definition SpscQueue.h:117
bool Dequeue(ElementType &OutElem)
Definition SpscQueue.h:92
TSpscQueue()
Definition SpscQueue.h:24
FIterator begin() const
Definition SpscQueue.h:172
std::nullptr_t end() const
Definition SpscQueue.h:177
~TSpscQueue()
Definition SpscQueue.h:32
bool IsEmpty() const
Definition SpscQueue.h:104
int32 Num() const
Definition SpscQueue.h:109
void Enqueue(ArgTypes &&... Args)
Definition SpscQueue.h:58
T ElementType
Definition SpscQueue.h:20
TOptional< ElementType > Dequeue()
Definition SpscQueue.h:70
Definition Optional.h:131
Definition SpscQueue.h:146
FIterator(const TSpscQueue &Queue)
Definition SpscQueue.h:149
const ElementType & operator*() const
Definition SpscQueue.h:166
TSpscQueue::FNode * Current
Definition SpscQueue.h:147
FIterator & operator++()
Definition SpscQueue.h:155
bool operator==(std::nullptr_t) const
Definition SpscQueue.h:161
Definition TypeCompatibleBytes.h:24