MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Threading.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14#include "CoreMinimal.h"
15#include "UObject/StrongObjectPtr.h"
16#include "Async/TaskGraphInterfaces.h"
17#include "Async/Async.h"
18#include "Mcro/FunctionTraits.h"
19#include "Mcro/SharedObjects.h"
20#include "RHICommandList.h"
21#include "RenderingThread.h"
22
24{
25 using namespace Mcro::FunctionTraits;
26 using namespace Mcro::SharedObjects;
27
28 /**
29 * @brief
30 * Returns true when called in the thread which is associated with the given ENamedThreads.
31 * If there's no such a thread or if this function is not called in that thread, return false.
32 */
33 MCRO_API bool IsInThread(ENamedThreads::Type threadName);
34
35 namespace Detail
36 {
37 MCRO_API auto GetThreadCheck(ENamedThreads::Type threadName) -> bool(*)();
38
39 template <CFunctionLike When>
40 requires (TFunction_ArgCount<When> == 0)
42 ENamedThreads::Type threadName,
43 TUniqueFunction<void()>&& func, When&& when
44 ) {
45 if (IsInThread(threadName)) func();
46 else AsyncTask(threadName, [when = MoveTemp(when), func = MoveTemp(func)]
47 {
48 if (auto keep = when()) func();
49 });
50 }
51
52 template <CFunctionLike When>
53 requires (TFunction_ArgCount<When> == 0)
54 void EnqueueRenderCommandBoilerplate(TUniqueFunction<void(FRHICommandListImmediate&)>&& func, When&& when)
55 {
56 if (IsInRenderingThread()) func(GetImmediateCommandList_ForRenderCommand());
57 else
58 {
59 ENQUEUE_RENDER_COMMAND(FMcroThreading)([when = MoveTemp(when), func = MoveTemp(func)](FRHICommandListImmediate& cmdList)
60 {
61 if (auto keep = when()) func(cmdList);
62 });
63 }
64 }
65
66 template <CFunctorObject Function, typename Result = TFunction_Return<Function>, CFunctionLike When>
67 requires (
68 TFunction_ArgCount<Function> == 0
69 && TFunction_ArgCount<When> == 0
70 )
72 ENamedThreads::Type threadName,
73 Function&& func,
74 When&& when
75 ) {
76 if (IsInThread(threadName))
77 return MakeFulfilledPromise<Result>(func()).GetFuture();
78
79 TPromise<Result> promise;
80 auto future = promise.GetFuture();
81
82 AsyncTask(threadName, [when = MoveTemp(when), func = MoveTemp(func), promise = MoveTemp(promise)]() mutable
83 {
84 if (auto keep = when()) promise.SetValue(func());
85 else promise.SetValue({});
86 });
87 return future;
88 }
89
90 template <CFunctorObject Function, typename Result = TFunction_Return<Function>, CFunctionLike When>
91 requires (
92 TFunction_ArgCount<Function> == 1
93 && CSameAs<FRHICommandListImmediate&, TFunction_Arg<Function, 0>>
94 && TFunction_ArgCount<When> == 0
95 )
97 ENamedThreads::Type threadName,
98 Function&& func,
99 When&& when
100 ) {
101 if (IsInRenderingThread())
102 return MakeFulfilledPromise<Result>(func(GetImmediateCommandList_ForRenderCommand())).GetFuture();
103
104 TPromise<Result> promise;
105 auto future = promise.GetFuture();
106
107 ENQUEUE_RENDER_COMMAND(FMcroThreading)([when = MoveTemp(when), func = MoveTemp(func), promise = MoveTemp(promise)](FRHICommandListImmediate& cmdList) mutable
108 {
109 if (auto keep = when())
110 promise.SetValue(func(cmdList));
111 else promise.SetValue({});
112 });
113 return future;
114 }
115 }
116
117 /**
118 * @brief
119 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
120 * already
121 */
122 MCRO_API void RunInThread(ENamedThreads::Type threadName, TUniqueFunction<void()>&& func);
123
124 /**
125 * @brief
126 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
127 * already check the validity of a target object first before running on the selected thread.
128 */
129 MCRO_API void RunInThread(ENamedThreads::Type threadName, const UObject* boundToObject, TUniqueFunction<void()>&& func);
130
131 /**
132 * @brief
133 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
134 * already check the validity of a target object first before running on the selected thread.
135 */
136 MCRO_API void RunInThread(ENamedThreads::Type threadName, const FWeakObjectPtr& boundToObject, TUniqueFunction<void()>&& func);
137
138 /**
139 * @brief
140 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
141 */
142 MCRO_API void RunInGameThread(TUniqueFunction<void()>&& func);
143
144 /**
145 * @brief
146 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
147 * Check the validity of a target object first before running on the game thread.
148 */
149 MCRO_API void RunInGameThread(const UObject* boundToObject, TUniqueFunction<void()>&& func);
150
151 /**
152 * @brief
153 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
154 * Check the validity of a target object first before running on the game thread.
155 */
156 MCRO_API void RunInGameThread(const FWeakObjectPtr& boundToObject, TUniqueFunction<void()>&& func);
157
158 /**
159 * @brief
160 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
161 */
162 MCRO_API void EnqueueRenderCommand(TUniqueFunction<void(FRHICommandListImmediate&)>&& func);
163
164 /**
165 * @brief
166 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
167 * Check the validity of a target object first before running on the render thread.
168 */
169 MCRO_API void EnqueueRenderCommand(const UObject* boundToObject, TUniqueFunction<void(FRHICommandListImmediate&)>&& func);
170
171 /**
172 * @brief
173 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
174 * Check the validity of a target object first before running on the render thread.
175 */
176 MCRO_API void EnqueueRenderCommand(const FWeakObjectPtr& boundToObject, TUniqueFunction<void(FRHICommandListImmediate&)>&& func);
177
178 /**
179 * @brief
180 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
181 * already Check the validity of a target object first before running on the selected thread.
182 */
183 template <CSharedOrWeak Object>
184 void RunInThread(ENamedThreads::Type threadName, const Object& boundToObject, TUniqueFunction<void()>&& func)
185 {
186 TWeakPtrFrom<Object> weakObject(boundToObject);
187 Detail::RunInThreadBoilerplate(threadName, MoveTemp(func), [weakObject = MoveTemp(weakObject)]
188 {
189 return weakObject.Pin();
190 });
191 }
192
193 /**
194 * @brief
195 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
196 * Check the validity of a target object first before running on the game thread.
197 */
198 template <CSharedOrWeak Object>
199 void RunInGameThread(const Object& boundToObject, TUniqueFunction<void()>&& func)
200 {
201 RunInThread<Object>(ENamedThreads::GameThread, boundToObject, MoveTemp(func));
202 }
203
204 /**
205 * @brief
206 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
207 * Check the validity of a target object first before running on the game thread.
208 */
209 template <CSharedOrWeak Object>
210 void EnqueueRenderCommand(const Object& boundToObject, TUniqueFunction<void(FRHICommandListImmediate&)>&& func)
211 {
212 TWeakPtrFrom<Object> weakObject(boundToObject);
213 Detail::EnqueueRenderCommandBoilerplate(MoveTemp(func), [weakObject = MoveTemp(weakObject)]
214 {
215 return weakObject.Pin();
216 });
217 }
218
219 /**
220 * @brief
221 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
222 * already This overload doesn't check object lifespans.
223 */
224 template <
225 CFunctorObject Function,
226 typename Result = TFunction_Return<Function>
227 >
228 requires (TFunction_ArgCount<Function> == 0)
229 TFuture<Result> PromiseInThread(ENamedThreads::Type threadName, Function&& func)
230 {
231 return Detail::PromiseInThreadBoilerplate(threadName, MoveTemp(func), []{ return true; });
232 }
233
234 /**
235 * @brief
236 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
237 * already Check the validity of a target object first before running on the selected thread.
238 */
239 template <
240 CSharedOrWeak Object,
241 CFunctorObject Function,
242 typename Result = TFunction_Return<Function>
243 >
244 requires (TFunction_ArgCount<Function> == 0)
245 TFuture<Result> PromiseInThread(ENamedThreads::Type threadName, const Object& boundToObject, Function&& func)
246 {
247 TWeakPtrFrom<Object> weakObject(boundToObject);
248 return Detail::PromiseInThreadBoilerplate(threadName, MoveTemp(func), [weakObject = MoveTemp(weakObject)]
249 {
250 return weakObject.Pin();
251 });
252 }
253
254 /**
255 * @brief
256 * Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selected thread
257 * already Check the validity of a target object first before running on the selected thread.
258 */
259 template <
260 CUObject Object,
261 CFunctorObject Function,
262 typename Result = TFunction_Return<Function>
263 >
264 requires (TFunction_ArgCount<Function> == 0)
265 TFuture<Result> PromiseInThread(ENamedThreads::Type threadName, const Object* boundToObject, Function&& func)
266 {
267 return Detail::PromiseInThreadBoilerplate(threadName, MoveTemp(func), [boundToObject]
268 {
269 return IsValid(boundToObject) ? TStrongObjectPtr(boundToObject) : nullptr;
270 });
271 }
272
273 /**
274 * @brief
275 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
276 * This overload doesn't check object lifespans.
277 */
278 template <
279 CFunctorObject Function,
280 typename Result = TFunction_Return<Function>
281 >
282 requires (TFunction_ArgCount<Function> == 0)
283 TFuture<Result> PromiseInGameThread(Function&& func)
284 {
285 return Detail::PromiseInThreadBoilerplate(ENamedThreads::GameThread, MoveTemp(func), []{ return true; });
286 }
287
288 /**
289 * @brief
290 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
291 * Check the validity of a target object first before running on the game thread.
292 */
293 template <
294 CSharedOrWeak Object,
295 CFunctorObject Function,
296 typename Result = TFunction_Return<Function>
297 >
298 requires (TFunction_ArgCount<Function> == 0)
299 TFuture<Result> PromiseInGameThread(const Object& boundToObject, Function&& func)
300 {
301 TWeakPtrFrom<Object> weakObject(boundToObject);
302 return Detail::PromiseInThreadBoilerplate(ENamedThreads::GameThread, MoveTemp(func), [weakObject = MoveTemp(weakObject)]
303 {
304 return weakObject.Pin();
305 });
306 }
307
308 /**
309 * @brief
310 * Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread already
311 * Check the validity of a target object first before running on the game thread.
312 */
313 template <
314 CUObject Object,
315 CFunctorObject Function,
316 typename Result = TFunction_Return<Function>
317 >
318 requires (TFunction_ArgCount<Function> == 0)
319 TFuture<Result> PromiseInGameThread(const Object* boundToObject, Function&& func)
320 {
321 return Detail::PromiseInThreadBoilerplate(ENamedThreads::GameThread, MoveTemp(func), [boundToObject]
322 {
323 return IsValid(boundToObject) ? TStrongObjectPtr(boundToObject) : nullptr;
324 });
325 }
326
327 /**
328 * @brief
329 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
330 * This overload doesn't check object lifespans.
331 */
332 template <
333 CFunctorObject Function,
334 typename Result = TFunction_Return<Function>
335 >
336 requires (
338 && CSameAs<FRHICommandListImmediate&, TFunction_Arg<Function, 0>>
339 )
340 TFuture<Result> EnqueueRenderPromise(Function&& func)
341 {
342 return Detail::EnqueueRenderPromiseBoilerplate(ENamedThreads::GameThread, MoveTemp(func), []{ return true; });
343 }
344
345 /**
346 * @brief
347 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
348 * Check the validity of a target object first before running on the render thread.
349 */
350 template <
351 CSharedOrWeak Object,
352 CFunctorObject Function,
353 typename Result = TFunction_Return<Function>
354 >
355 requires (
357 && CSameAs<FRHICommandListImmediate&, TFunction_Arg<Function, 0>>
358 )
359 TFuture<Result> EnqueueRenderPromise(const Object& boundToObject, Function&& func)
360 {
361 TWeakPtrFrom<Object> weakObject(boundToObject);
362 return Detail::EnqueueRenderPromiseBoilerplate(ENamedThreads::GameThread, MoveTemp(func), [weakObject = MoveTemp(weakObject)]
363 {
364 return weakObject.Pin();
365 });
366 }
367
368 /**
369 * @brief
370 * Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render thread already
371 * Check the validity of a target object first before running on the render thread.
372 */
373 template <
374 CUObject Object,
375 CFunctorObject Function,
376 typename Result = TFunction_Return<Function>
377 >
378 requires (
380 && CSameAs<FRHICommandListImmediate&, TFunction_Arg<Function, 0>>
381 )
382 TFuture<Result> EnqueueRenderPromise(const Object* boundToObject, Function&& func)
383 {
384 return Detail::EnqueueRenderPromiseBoilerplate(ENamedThreads::GameThread, MoveTemp(func), [boundToObject]
385 {
386 return IsValid(boundToObject) ? TStrongObjectPtr(boundToObject) : nullptr;
387 });
388 }
389}
typename TFunctionTraits< std::decay_t< T > >::Return TFunction_Return
Shorthand for getting a function return type.
constexpr size_t TFunction_ArgCount
Shorthand for getting a function argument count.
Utilities for TSharedPtr/Ref and related.
TWeakPtr< typename T::ElementType, T::Mode > TWeakPtrFrom
Copy thread-safety from other shared object type.
void RunInThreadBoilerplate(ENamedThreads::Type threadName, TUniqueFunction< void()> &&func, When &&when)
Definition Threading.h:41
TFuture< Result > EnqueueRenderPromiseBoilerplate(ENamedThreads::Type threadName, Function &&func, When &&when)
Definition Threading.h:96
void EnqueueRenderCommandBoilerplate(TUniqueFunction< void(FRHICommandListImmediate &)> &&func, When &&when)
Definition Threading.h:54
TFuture< Result > PromiseInThreadBoilerplate(ENamedThreads::Type threadName, Function &&func, When &&when)
Definition Threading.h:71
MCRO_API auto GetThreadCheck(ENamedThreads::Type threadName) -> bool(*)()
MCRO_API void RunInGameThread(TUniqueFunction< void()> &&func)
Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread...
MCRO_API bool IsInThread(ENamedThreads::Type threadName)
Returns true when called in the thread which is associated with the given ENamedThreads....
MCRO_API void EnqueueRenderCommand(TUniqueFunction< void(FRHICommandListImmediate &)> &&func)
Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render th...
TFuture< Result > EnqueueRenderPromise(Function &&func)
Simply run a lambda function on the render thread but only use AsyncTask if it's not on the render th...
Definition Threading.h:340
TFuture< Result > PromiseInGameThread(Function &&func)
Simply run a lambda function on the game thread but only use AsyncTask if it's not on the game thread...
Definition Threading.h:283
TFuture< Result > PromiseInThread(ENamedThreads::Type threadName, Function &&func)
Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selecte...
Definition Threading.h:229
MCRO_API void RunInThread(ENamedThreads::Type threadName, TUniqueFunction< void()> &&func)
Simply run a lambda function on the selected thread but only use AsyncTask if it's not on the selecte...