UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
EventCount.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Async/ParkingLot.h"
6#include <atomic>
7#include <type_traits>
8
9namespace UE
10{
11
12template <typename CounterType>
13class TEventCount;
14
16template <typename CounterType>
18{
19 static_assert(std::is_unsigned_v<CounterType>);
20
21public:
23 inline explicit operator bool() const { return !(Value & 1); }
24
25private:
27 CounterType Value = 1;
28
30};
31
59template <typename CounterType>
61{
62 static_assert(std::is_unsigned_v<CounterType>);
63
64public:
65 constexpr TEventCount() = default;
66
67 TEventCount(const TEventCount&) = delete;
69
78 {
80
81#if PLATFORM_CPU_X86_FAMILY
82 // Doing a relaxed load here because fetch_or on x86 cannot return the previous value
83 // so when we use the return value of fetch_or, it gets compiled into a compare_exchange.
84 // The worst that can happen here is that we get a stale token value and end up
85 // not waiting for an iteration. We would then get the proper value on the next iteration.
86 Token.Value = Count.load(std::memory_order_relaxed) & ~CounterType(1);
87 Count.fetch_or(1, std::memory_order_acq_rel);
88#else
89 Token.Value = Count.fetch_or(1, std::memory_order_acq_rel) & ~CounterType(1);
90#endif
91 return Token;
92 }
93
100 {
101 if ((Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value)
102 {
103 ParkingLot::Wait(&Count, [this, Compare] { return (Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value; }, nullptr);
104 }
105 }
106
115 {
116 if ((Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value)
117 {
118 const ParkingLot::FWaitState WaitState = ParkingLot::WaitFor(&Count, [this, Compare] { return (Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value; }, nullptr, WaitTime);
119
120 // Make sure we return true if we did wait but also if the wait was skipped because the value actually changed before we had the opportunity to wait.
121 return WaitState.bDidWake || !WaitState.bDidWait;
122 }
123 return true;
124 }
125
134 {
135 if ((Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value)
136 {
137 const ParkingLot::FWaitState WaitState = ParkingLot::WaitUntil(&Count, [this, Compare] { return (Count.load(std::memory_order_acquire) & ~CounterType(1)) == Compare.Value; }, nullptr, WaitTime);
138
139 // Make sure we return true if we did wait but also if the wait was skipped because the value actually changed before we had the opportunity to wait.
140 return WaitState.bDidWake || !WaitState.bDidWait;
141 }
142 return true;
143 }
144
151 inline void Notify()
152 {
153 //
154 // .fetch_add(0, acq_rel) is used to have a StoreLoad barrier,
155 // which we can't express in C++. That works by making the load
156 // also be store (via RMW) and relying on a StoreStore barrier to
157 // get the desired ordering.
158 //
159 // Previously, this code was:
160 // CounterType Value = Count.load(std::memory_order_relaxed);
161 //
162 // which had a memory re-ordering and stale values being read,
163 // leading to a missed Wake and dead-locked waiter, as a result.
164 //
165 CounterType Value = Count.fetch_add(0, std::memory_order_acq_rel);
166 if ((Value & 1) && Count.compare_exchange_strong(Value, Value + 1, std::memory_order_release))
167 {
168 ParkingLot::WakeAll(&Count);
169 }
170 }
171
180 inline void NotifyWeak()
181 {
182 // We probably don't need the weakly consistent case anymore
183 // for this weak version of notify, but will need more tests before removing.
184#if PLATFORM_WEAKLY_CONSISTENT_MEMORY
185 //
186 // .fetch_add(0, acq_rel) is used to have a StoreLoad barrier,
187 // which we can't express in C++. That works by making the load
188 // also be store (via RMW) and relying on a StoreStore barrier to
189 // get the desired ordering.
190 //
191 // Previously, this code was:
192 // CounterType Value = Count.load(std::memory_order_relaxed);
193 //
194 // which had a memory re-ordering and stale values being read,
195 // leading to a missed Wake and dead-locked waiter, as a result.
196 //
197 CounterType Value = Count.fetch_add(0, std::memory_order_acq_rel);
198#else
199 // On x86 and other non weak memory model, the fetch_or inside PrepareWait
200 // is a serializing instruction that will flush the store buffer.
201 // We can omit the expensive locked op here and just do a relaxed read.
202 CounterType Value = Count.load(std::memory_order_relaxed);
203#endif
204 if ((Value & 1) && Count.compare_exchange_strong(Value, Value + 1, std::memory_order_release))
205 {
206 ParkingLot::WakeAll(&Count);
207 }
208 }
209
210private:
211 std::atomic<CounterType> Count = 0;
212};
213
216
217} // UE
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
const bool
Definition NetworkReplayStreaming.h:178
Definition EventCount.h:18
Definition EventCount.h:61
TEventCountToken< CounterType > PrepareWait()
Definition EventCount.h:77
void Wait(TEventCountToken< CounterType > Compare)
Definition EventCount.h:99
TEventCount(const TEventCount &)=delete
bool WaitFor(TEventCountToken< CounterType > Compare, FMonotonicTimeSpan WaitTime)
Definition EventCount.h:114
constexpr TEventCount()=default
TEventCount & operator=(const TEventCount &)=delete
bool WaitUntil(TEventCountToken< CounterType > Compare, FMonotonicTimePoint WaitTime)
Definition EventCount.h:133
void NotifyWeak()
Definition EventCount.h:180
void Notify()
Definition EventCount.h:151
FWaitState Wait(const void *Address, TFunctionWithContext< bool()> CanWait, TFunctionWithContext< void()> BeforeWait)
Definition ParkingLot.h:52
FWaitState WaitFor(const void *Address, TFunctionWithContext< bool()> CanWait, TFunctionWithContext< void()> BeforeWait, FMonotonicTimeSpan WaitTime)
Definition ParkingLot.h:65
FWaitState WaitUntil(const void *Address, TFunctionWithContext< bool()> CanWait, TFunctionWithContext< void()> BeforeWait, FMonotonicTimePoint WaitTime)
Definition ParkingLot.h:78
Definition AdvancedWidgetsModule.cpp:13
Definition MonotonicTime.h:74
Definition MonotonicTime.h:20
Definition ParkingLot.h:20
bool bDidWake
Definition ParkingLot.h:24