Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
MTAccessDetector.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"
6#include "HAL/PlatformTLS.h"
7#include "HAL/PreprocessorHelpers.h"
8#include "Misc/AssertionMacros.h"
9#include "Misc/Build.h"
10#include <atomic>
11
12
13#define ENABLE_MT_DETECTOR DO_CHECK
14
16
17extern bool GIsAutomationTesting;
18
19/**
20 * Read write multithread access detector, will check on concurrent write/write and read/write access, but will not on concurrent read access.
21 * Note this detector is not re-entrant, see FRWRecursiveAccessDetector and FRWFullyRecursiveAccessDetector.
22 * But FRWAccessDetector should be the default one to start with.
23 */
24struct FRWAccessDetector
25{
26public:
27
28 FRWAccessDetector()
29 : AtomicValue(0)
30 {}
31
32 ~FRWAccessDetector()
33 {
34 checkf(AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be destroyed while other threads access it"))
35 }
36
37 FRWAccessDetector(FRWAccessDetector&& Other)
38 {
39 checkf(Other.AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be \"moved out\" while other threads access it"));
40 }
41
42 FRWAccessDetector& operator=(FRWAccessDetector&& Other)
43 {
44 checkf(AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be modified while other threads access it"));
45 checkf(Other.AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be \"moved out\" while other threads access it"));
46 return *this;
47 }
48
49 FRWAccessDetector(const FRWAccessDetector& Other)
50 {
51 checkf(Other.AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be copied while other threads access it"));
52 }
53
54 FRWAccessDetector& operator=(const FRWAccessDetector& Other)
55 {
56 checkf(AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be modified while other threads access it"));
57 checkf(Other.AtomicValue.load(std::memory_order_relaxed) == 0, TEXT("Detector cannot be copied while other threads access it"));
58 return *this;
59 }
60
61 /**
62 * Acquires read access, will check if there are any writers
63 * @return true if no errors were detected
64 */
65 FORCEINLINE bool AcquireReadAccess() const
66 {
67 const bool ErrorDetected = (AtomicValue.fetch_add(1, std::memory_order_relaxed) & WriterBits) != 0;
68 checkf(!ErrorDetected || GIsAutomationTesting, TEXT("Aquiring a read access while there is already a write access"));
69 return !ErrorDetected;
70 }
71
72 /**
73 * Releases read access, will check if there are any writers
74 * @return true if no errors were detected
75 */
76 FORCEINLINE bool ReleaseReadAccess() const
77 {
78 const bool ErrorDetected = (AtomicValue.fetch_sub(1, std::memory_order_relaxed) & WriterBits) != 0;
79 checkf(!ErrorDetected || GIsAutomationTesting, TEXT("Another thread asked to have a write access during this read access"));
80 return !ErrorDetected;
81 }
82
83 /**
84 * Acquires write access, will check if there are readers or other writers
85 * @return true if no errors were detected
86 */
87 FORCEINLINE bool AcquireWriteAccess() const
88 {
89 const bool ErrorDetected = AtomicValue.fetch_add(WriterIncrementValue, std::memory_order_relaxed) != 0;
90 checkf(!ErrorDetected || GIsAutomationTesting, TEXT("Acquiring a write access while there are ongoing read or write access"));
91 return !ErrorDetected;
92 }
93
94 /**
95 * Releases write access, will check if there are readers or other writers
96 * @return true if no errors were detected
97 */
98 FORCEINLINE bool ReleaseWriteAccess() const
99 {
100 const bool ErrorDetected = AtomicValue.fetch_sub(WriterIncrementValue, std::memory_order_relaxed) != WriterIncrementValue;
101 checkf(!ErrorDetected || GIsAutomationTesting, TEXT("Another thread asked to have a read or write access during this write access"));
102 return !ErrorDetected;
103 }
104
105protected:
106
107 // We need to do an atomic operation to know there are multiple writers, this is why we reserve more than one bit for them.
108 // While firing the check upon acquire write access, the other writer thread could continue and hopefully fire a check upon releasing access so we get both faulty callstacks.
109 static constexpr uint32 WriterBits = 0xfff00000;
110 static constexpr uint32 WriterIncrementValue = 0x100000;
111
112 mutable std::atomic<uint32> AtomicValue;
113};
114
115/**
116 * Same as FRWAccessDetector, but support re-entrance on the write access
117 * See FRWFullyRecursiveAccessDetector for read access re-entrance when holding a write access
118 */
119struct FRWRecursiveAccessDetector : public FRWAccessDetector
120{
121public:
122 /**
123 * Acquires write access, will check if there are readers or other writers
124 * @return true if no errors were detected
125 */
126 FORCEINLINE bool AcquireWriteAccess() const
127 {
128 uint32 CurThreadID = FPlatformTLS::GetCurrentThreadId();
129
130 if (WriterThreadID == CurThreadID)
131 {
132 RecursiveDepth++;
133 return true;
134 }
135 else if (FRWAccessDetector::AcquireWriteAccess())
136 {
137 check(RecursiveDepth == 0);
138 WriterThreadID = CurThreadID;
139 RecursiveDepth++;
140 return true;
141 }
142 return false;
143 }
144
145 /**
146 * Releases write access, will check if there are readers or other writers
147 * @return true if no errors were detected
148 */
149 FORCEINLINE bool ReleaseWriteAccess() const
150 {
151 uint32 CurThreadID = FPlatformTLS::GetCurrentThreadId();
152 if (WriterThreadID == CurThreadID)
153 {
154 check(RecursiveDepth > 0);
155 RecursiveDepth--;
156
157 if (RecursiveDepth == 0)
158 {
159 WriterThreadID = (uint32)-1;
160 return FRWAccessDetector::ReleaseWriteAccess();
161 }
162 return true;
163 }
164 else
165 {
166 // This can happen when a user continues pass a reported error,
167 // just trying to keep things going as best as possible.
168 return FRWAccessDetector::ReleaseWriteAccess();
169 }
170 }
171
172protected:
173 mutable uint32 WriterThreadID = (uint32)-1;
174 mutable int32 RecursiveDepth = 0;
175};
176
177/**
178 * Same as FRWRecursiveAccessDetector, but support re-entrance on read access when holding a write access.
179 */
180struct FRWFullyRecursiveAccessDetector : public FRWRecursiveAccessDetector
181{
182public:
183 /**
184 * Acquires read access, will check if there are any writers
185 * @return true if no errors were detected
186 */
187 FORCEINLINE bool AcquireReadAccess() const
188 {
189 uint32 CurThreadID = FPlatformTLS::GetCurrentThreadId();
190 if (WriterThreadID == CurThreadID)
191 {
192 return true;
193 }
194 return FRWAccessDetector::AcquireReadAccess();
195 }
196
197 /**
198 * Releases read access, will check if there are any writers
199 * @return true if no errors were detected
200 */
201 FORCEINLINE bool ReleaseReadAccess() const
202 {
203 uint32 CurThreadID = FPlatformTLS::GetCurrentThreadId();
204 if (WriterThreadID == CurThreadID)
205 {
206 return true;
207 }
208 return FRWAccessDetector::ReleaseReadAccess();
209 }
210};
211
212struct FBaseScopedAccessDetector
213{
214};
215
216template<typename RWAccessDetector>
217struct TScopedReaderAccessDetector : public FBaseScopedAccessDetector
218{
219public:
220
221 FORCEINLINE TScopedReaderAccessDetector(const RWAccessDetector& InAccessDetector)
222 : AccessDetector(InAccessDetector)
223 {
224 AccessDetector.AcquireReadAccess();
225 }
226
227 FORCEINLINE ~TScopedReaderAccessDetector()
228 {
229 AccessDetector.ReleaseReadAccess();
230 }
231private:
232 const RWAccessDetector& AccessDetector;
233};
234
235template<typename RWAccessDetector>
236FORCEINLINE TScopedReaderAccessDetector<RWAccessDetector> MakeScopedReaderAccessDetector(RWAccessDetector& InAccessDetector)
237{
238 return TScopedReaderAccessDetector<RWAccessDetector>(InAccessDetector);
239}
240
241template<typename RWAccessDetector>
242struct TScopedWriterDetector : public FBaseScopedAccessDetector
243{
244public:
245
246 FORCEINLINE TScopedWriterDetector(const RWAccessDetector& InAccessDetector)
247 : AccessDetector(InAccessDetector)
248 {
249 AccessDetector.AcquireWriteAccess();
250 }
251
252 FORCEINLINE ~TScopedWriterDetector()
253 {
254 AccessDetector.ReleaseWriteAccess();
255 }
256private:
257 const RWAccessDetector& AccessDetector;
258};
259
260template<typename RWAccessDetector>
261FORCEINLINE TScopedWriterDetector<RWAccessDetector> MakeScopedWriterAccessDetector(RWAccessDetector& InAccessDetector)
262{
263 return TScopedWriterDetector<RWAccessDetector>(InAccessDetector);
264}
265
266#define UE_MT_DECLARE_RW_ACCESS_DETECTOR(AccessDetector) FRWAccessDetector AccessDetector;
267#define UE_MT_DECLARE_RW_RECURSIVE_ACCESS_DETECTOR(AccessDetector) FRWRecursiveAccessDetector AccessDetector;
268#define UE_MT_DECLARE_RW_FULLY_RECURSIVE_ACCESS_DETECTOR(AccessDetector) FRWFullyRecursiveAccessDetector AccessDetector;
269
270#define UE_MT_SCOPED_READ_ACCESS(AccessDetector) const FBaseScopedAccessDetector& PREPROCESSOR_JOIN(ScopedMTAccessDetector_,__LINE__) = MakeScopedReaderAccessDetector(AccessDetector);
271#define UE_MT_SCOPED_WRITE_ACCESS(AccessDetector) const FBaseScopedAccessDetector& PREPROCESSOR_JOIN(ScopedMTAccessDetector_,__LINE__) = MakeScopedWriterAccessDetector(AccessDetector);
272
273#define UE_MT_ACQUIRE_READ_ACCESS(AccessDetector) (AccessDetector).AcquireReadAccess();
274#define UE_MT_RELEASE_READ_ACCESS(AccessDetector) (AccessDetector).ReleaseReadAccess();
275#define UE_MT_ACQUIRE_WRITE_ACCESS(AccessDetector) (AccessDetector).AcquireWriteAccess();
276#define UE_MT_RELEASE_WRITE_ACCESS(AccessDetector) (AccessDetector).ReleaseWriteAccess();
277
278#else // ENABLE_MT_DETECTOR
279
280#define UE_MT_DECLARE_RW_ACCESS_DETECTOR(AccessDetector)
281#define UE_MT_DECLARE_RW_RECURSIVE_ACCESS_DETECTOR(AccessDetector)
282#define UE_MT_DECLARE_RW_FULLY_RECURSIVE_ACCESS_DETECTOR(AccessDetector)
283
284#define UE_MT_SCOPED_READ_ACCESS(AccessDetector)
285#define UE_MT_SCOPED_WRITE_ACCESS(AccessDetector)
286
287#define UE_MT_ACQUIRE_READ_ACCESS(AccessDetector)
288#define UE_MT_RELEASE_READ_ACCESS(AccessDetector)
289#define UE_MT_ACQUIRE_WRITE_ACCESS(AccessDetector)
290#define UE_MT_RELEASE_WRITE_ACCESS(AccessDetector)
291
292#endif // ENABLE_MT_DETECTOR
#define DO_CHECK
Definition Build.h:311
#define ENABLE_MT_DETECTOR