UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AwaitableTask.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
7#include "Misc/Launder.h"
8
9template<typename>
10class TAwaitableTask;
11
13{
14 template<typename ReturnType>
15 inline ReturnType MakeDummyValue()
16 {
17 return *(reinterpret_cast<ReturnType*>(uintptr_t(1)));
18 }
19
20 template<>
22 {
23 return;
24 }
25
27 {
28 };
29
30 //the TPromiseVTableBase type is used to move the vtable pointer out of the promise, where it otherwise could interfere the alignment of the Callable/Lambda.
31 template<typename ReturnType>
33 {
34 protected:
38
39 public:
40 TPromiseVTableBase() = default;
41
42 virtual bool TryLaunch()
43 {
44 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
45 return false;
46 }
47
48 virtual void IncrementRefCount()
49 {
50 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
51 }
52
53 virtual ReturnType GetResult()
54 {
55 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
57 }
58
59 virtual bool IsLaunched() const
60 {
61 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
62 return false;
63 }
64
65 virtual bool IsValid() const
66 {
67 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
68 return false;
69 }
70
71 virtual bool IsCompleted() const
72 {
73 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
74 return true;
75 }
76
77 virtual void Finish()
78 {
79 checkNoEntry(); //if we get to this place than the compiler is optimizing our vtable lookup and ignores our undefined/shallow copy of the memory
80 }
81
84 };
85
86 //the TPromiseVTableDummy is used with empty handles, this allows us to always call the virtual interface without nullpointer checks
87 //validation for launching and getting the result of empty taskhandles is done as well.
88 template<typename ReturnType>
89 class TPromiseVTableDummy final : public TPromiseVTableBase<ReturnType>
90 {
91 public:
92 TPromiseVTableDummy() : TPromiseVTableBase<ReturnType>(nullptr)
93 {
94 static_assert(sizeof(TPromiseVTableDummy) == sizeof(TPromiseVTableBase<ReturnType>), "Sizes must match because we reinterpret the memory");
95 }
96
97 bool TryLaunch() override
98 {
99 checkf(false, TEXT("trying to Launch an empty AwaitableTask"));
100 return false;
101 }
102
103 void IncrementRefCount() override
104 {
105 }
106
107 ReturnType GetResult() override
108 {
109 checkf(false, TEXT("trying to get the Result of an empty AwaitableTask"));
111 }
112
113 bool IsLaunched() const override
114 {
115 return false;
116 }
117
118 bool IsValid() const override
119 {
120 return false;
121 }
122
123 bool IsCompleted() const override
124 {
125 return true;
126 }
127
128 void Finish() override
129 {
130 }
131 };
132
133 template<typename ReturnType>
139
140 template<typename ReturnType>
147
148 //the TPromiseVTable is used to type erase the PromiseType and therefore its lambda type
149 template<typename PromiseType>
150 class TPromiseVTable final : public TPromiseVTableBase<typename PromiseType::ReturnType>
151 {
152 using ReturnType = typename PromiseType::ReturnType;
153
154 public:
156 {
157 checkSlow(this->Promise);
158 static_assert(sizeof(TPromiseVTable) == sizeof(TPromiseVTableBase<ReturnType>), "Sizes must match because we reinterpret the memory");
159 }
160
161 bool TryLaunch() override
162 {
163 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
164 return LocalPromise->TryLaunch();
165 }
166
167 void IncrementRefCount() override
168 {
169 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
170 LocalPromise->IncrementRefCount();
171 }
172
173 ReturnType GetResult() override
174 {
175 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
176 return LocalPromise->GetResult();
177 }
178
179 bool IsLaunched() const override
180 {
181 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
182 return LocalPromise->IsLaunched();
183 }
184
185 bool IsValid() const override
186 {
187 return true;
188 }
189
190 bool IsCompleted() const override
191 {
192 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
193 return LocalPromise->IsCompleted();
194 }
195
196 void Finish() override
197 {
198 PromiseType* LocalPromise = static_cast<PromiseType*>(this->Promise);
201 }
202 };
203
204 template<typename TReturnType, typename CallableType>
205 class TPromise final : public FPromiseBase
206 {
207 public:
209
210 private:
212
213 private:
214 CallableType Callable;
216 ReturnType ReturnValue;
217 std::atomic_int ReferenceCounter{ 2 }; //starts at 2 because at construction the AwaitableTask will hold a Reference and the LowLevelTasks::FTask does as well.
218 UE::FManualResetEvent Completed;
219
220 inline void Execute()
221 {
222 ReturnValue = Invoke(Callable);
223 Completed.Notify();
224 }
225
226 TPromise(const TPromise&) = delete;
227 TPromise& operator= (const TPromise& Other) = delete;
228
229 public:
230 template<typename CallableT>
232 {
233 Task.Init(DebugName, Priority, [this, Deleter(LowLevelTasks::TDeleter<ThisType, &ThisType::Finish>(this))]()
234 {
235 Execute();
236 });
237 }
238
239 inline bool TryLaunch()
240 {
241 return LowLevelTasks::TryLaunch(Task);
242 }
243
244 inline bool IsLaunched() const
245 {
246 return !Task.IsReady();
247 }
248
249 inline void IncrementRefCount()
250 {
251 ReferenceCounter.fetch_add(1, std::memory_order_release);
252 }
253
254 inline bool IsCompleted() const
255 {
256 return Completed.IsNotified();
257 }
258
259 inline void Finish()
260 {
261 int LocalCounter = ReferenceCounter.fetch_sub(1, std::memory_order_release);
262 if (LocalCounter == 1) //this is 1 because fetch_sub returns the value before the decrement (value is actually 0)
263 {
264 delete this;
265 }
266 }
267
269 {
270 if (Task.TryCancel())
271 {
272 Execute();
273 return ReturnValue;
274 }
275 else
276 {
277 Completed.Wait();
278 return ReturnValue;
279 }
280 }
281 };
282
283 template<typename CallableType>
284 class TPromise<void, CallableType> final : public FPromiseBase
285 {
286 public:
288
289 private:
291
292 private:
293 CallableType Callable;
295 std::atomic_int ReferenceCounter{ 2 }; //starts at 2 because at construction the AwaitableTask will hold a Reference and the LowLevelTasks::FTask does as well.
296 UE::FManualResetEvent Completed;
297
298 inline void Execute()
299 {
300 Invoke(Callable);
301 Completed.Notify();
302 }
303
304 TPromise(const TPromise&) = delete;
305 TPromise& operator= (const TPromise& Other) = delete;
306
307 public:
308 template<typename CallableT>
310 {
311 Task.Init(DebugName, Priority, [this, Deleter(LowLevelTasks::TDeleter<ThisType, &ThisType::Finish>(this))]()
312 {
313 Execute();
314 });
315 }
316
317 inline bool TryLaunch()
318 {
319 return LowLevelTasks::TryLaunch(Task);
320 }
321
322 inline bool IsLaunched() const
323 {
324 return !Task.IsReady();
325 }
326
327 inline void IncrementRefCount()
328 {
329 ReferenceCounter.fetch_add(1, std::memory_order_release);
330 }
331
332 inline bool IsCompleted() const
333 {
334 return Completed.IsNotified();
335 }
336
337 inline void Finish()
338 {
339 int LocalCounter = ReferenceCounter.fetch_sub(1, std::memory_order_release);
340 if (LocalCounter == 2 && !IsLaunched()) //this is 2 because fetch_sub returns the value before the decrement (value is actually 1)
341 {
342 //Cancel the Task and Try to Launch the Continuation if we are the last reference
343 verify(Task.TryCancel());
344 }
345 else if (LocalCounter == 1) //this is 1 because fetch_sub returns the value before the decrement (value is actually 0)
346 {
347 delete this;
348 }
349 }
350
351 inline void GetResult()
352 {
353 if (Task.TryCancel())
354 {
355 Execute();
356 }
357 else
358 {
359 Completed.Wait();
360 }
361 }
362 };
363}
364
365/*
366 * Awaitable Tasks are very simple they only allow for Lauching and Awaiting a Task.
367 * They will try to run other work while awaiting the Result.
368 */
369template<typename ReturnType>
370class TAwaitableTask final
371{
372 template<typename R>
374
375 template<typename R>
377
378 template<typename R, typename C>
380
381 template<typename P>
383
385 //the PromiseVTable has enough storage for both the promise and vtable
386 TPromiseVTableBase<ReturnType> PromiseVTable;
387
388public:
390 {
391 //uninitizialized Handles start with a DummyVtable
392 new (&PromiseVTable) TPromiseVTableDummy<ReturnType>();
393 }
394
396 {
397 Reset();
398 }
399
401 {
402 PromiseVTable = Other.PromiseVTable;
403 GetVtable()->IncrementRefCount();
404 }
405
407 {
408 PromiseVTable = MoveTemp(Other.PromiseVTable);
409 }
410
412 {
413 if(this != &Other)
414 {
415 Reset();
416 PromiseVTable = Other.PromiseVTable;
417 GetVtable()->IncrementRefCount();
418 }
419 return *this;
420 }
421
423 {
424 if(this != &Other)
425 {
426 Reset();
427 PromiseVTable = MoveTemp(Other.PromiseVTable);
428 }
429 return *this;
430 }
431
432public:
433 //initialize a new Task with given Priority now and Abandon previous work (if any)
434 //initialized Tasks can be awaited on, but they will run synchronous in this case
435 template<typename CallableT>
436 void Init(const TCHAR* DebugName, LowLevelTasks::ETaskPriority Priority, CallableT&& Callable)
437 {
438 Reset();
439 using CallableType = std::decay_t<CallableT>;
440 static_assert(TIsInvocable<CallableType>::Value, "Callable is not invocable");
441 static_assert(std::is_convertible<ReturnType, decltype(Callable())>::value, "Callable has no matching Return Type");
443 new (&PromiseVTable) TPromiseVTable<PromiseType>(new PromiseType(DebugName, Priority, Forward<CallableT>(Callable)));
444 }
445
446 //initialize a new Task now and Abandon previous work (if any)
447 //initialized Tasks can be awaited on, but they will run synchronous in this case
448 template<typename CallableT>
449 inline void Init(const TCHAR* DebugName, CallableT&& Callable)
450 {
452 }
453
454 //Launches the Task for processing on a WorkerThread. The returntype will specify if the Task was Launched successfully (true) or if it might have been launched already (false)
456 {
457 return GetVtable()->TryLaunch();
458 }
459
460 //initialize and Launch a Task with given Priority
461 template<typename CallableT>
463 {
464 Init(DebugName, Priority, Forward<CallableT>(Callable));
465 using CallableType = std::decay_t<CallableT>;
468 }
469
470 //initialize and Launch a Task
471 template<typename CallableT>
472 inline bool InitAndLaunch(const TCHAR* DebugName, CallableT&& Callable)
473 {
475 }
476
477 //Abandon the Task and freeing its memory if this is the last reference to it.
478 void Reset()
479 {
480 GetVtable()->Finish();
481 }
482
483 //returns true if the Task has been Launched.
484 bool IsLaunched() const
485 {
486 return GetVtable()->IsLaunched();
487 }
488
489 //Does the TaskHandle hold a valid or Dummy Task?
490 bool IsValid() const
491 {
492 return GetVtable()->IsValid();
493 }
494
495 //Returns true if the Tasks completed.
496 bool IsCompleted() const
497 {
498 return GetVtable()->IsCompleted();
499 }
500
501 //Await the Task Completion and get the result, this will assert if this is a Dummy Handle.
502 //Awaiting an initizialized but not launched task will run ('launch') it synchronously in the current thread.
503 //use BusyWaitUntilLaunched if you want to wait for launching the task on another thread.
504 ReturnType Await()
505 {
507 return GetVtable()->GetResult();
508 }
509
510private:
511 template<typename PromiseType = TPromiseVTableBase<ReturnType>>
512 inline PromiseType* GetVtable()
513 {
514 return UE_LAUNDER(static_cast<PromiseType*>(&PromiseVTable));
515 }
516
517 template<typename PromiseType = TPromiseVTableBase<ReturnType>>
518 inline const PromiseType* GetVtable() const
519 {
520 return UE_LAUNDER(static_cast<const PromiseType*>(&PromiseVTable));
521 }
522};
OODEFFUNC typedef void(OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)(void *ptr)
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define checkNoEntry()
Definition AssertionMacros.h:316
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
#define verify(expr)
Definition AssertionMacros.h:319
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
AUTORTFM_INFER UE_FORCEINLINE_HINT constexpr auto Invoke(FuncType &&Func, ArgTypes &&... Args) -> decltype(((FuncType &&) Func)((ArgTypes &&) Args...))
Definition Invoke.h:44
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_LAUNDER(x)
Definition Launder.h:8
void Init()
Definition LockFreeList.h:4
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
memcpy(InputBufferBase, BinkBlocksData, BinkBlocksSize)
Definition AwaitableTask.h:27
Definition AwaitableTask.h:33
virtual bool IsLaunched() const
Definition AwaitableTask.h:59
virtual void Finish()
Definition AwaitableTask.h:77
TPromiseVTableBase(FPromiseBase *InPromise)
Definition AwaitableTask.h:36
virtual bool IsCompleted() const
Definition AwaitableTask.h:71
virtual bool TryLaunch()
Definition AwaitableTask.h:42
FPromiseBase * Promise
Definition AwaitableTask.h:35
TPromiseVTableBase & operator=(const TPromiseVTableBase &Other)
Definition AwaitableTask.h:134
virtual ReturnType GetResult()
Definition AwaitableTask.h:53
virtual bool IsValid() const
Definition AwaitableTask.h:65
virtual void IncrementRefCount()
Definition AwaitableTask.h:48
Definition AwaitableTask.h:90
void IncrementRefCount() override
Definition AwaitableTask.h:103
bool IsValid() const override
Definition AwaitableTask.h:118
bool IsCompleted() const override
Definition AwaitableTask.h:123
bool TryLaunch() override
Definition AwaitableTask.h:97
ReturnType GetResult() override
Definition AwaitableTask.h:107
void Finish() override
Definition AwaitableTask.h:128
bool IsLaunched() const override
Definition AwaitableTask.h:113
TPromiseVTableDummy()
Definition AwaitableTask.h:92
Definition AwaitableTask.h:151
ReturnType GetResult() override
Definition AwaitableTask.h:173
bool TryLaunch() override
Definition AwaitableTask.h:161
TPromiseVTable(FPromiseBase *InPromise)
Definition AwaitableTask.h:155
bool IsCompleted() const override
Definition AwaitableTask.h:190
bool IsLaunched() const override
Definition AwaitableTask.h:179
void Finish() override
Definition AwaitableTask.h:196
void IncrementRefCount() override
Definition AwaitableTask.h:167
bool IsValid() const override
Definition AwaitableTask.h:185
void GetResult()
Definition AwaitableTask.h:351
bool IsCompleted() const
Definition AwaitableTask.h:332
bool TryLaunch()
Definition AwaitableTask.h:317
void Finish()
Definition AwaitableTask.h:337
bool IsLaunched() const
Definition AwaitableTask.h:322
void ReturnType
Definition AwaitableTask.h:287
void IncrementRefCount()
Definition AwaitableTask.h:327
TPromise(const TCHAR *DebugName, LowLevelTasks::ETaskPriority Priority, CallableT &&InCallable)
Definition AwaitableTask.h:309
Definition AwaitableTask.h:206
bool TryLaunch()
Definition AwaitableTask.h:239
ReturnType GetResult()
Definition AwaitableTask.h:268
TReturnType ReturnType
Definition AwaitableTask.h:208
bool IsLaunched() const
Definition AwaitableTask.h:244
bool IsCompleted() const
Definition AwaitableTask.h:254
void IncrementRefCount()
Definition AwaitableTask.h:249
void Finish()
Definition AwaitableTask.h:259
TPromise(const TCHAR *DebugName, LowLevelTasks::ETaskPriority Priority, CallableT &&InCallable)
Definition AwaitableTask.h:231
Definition Task.h:310
Definition Task.h:153
Definition AwaitableTask.h:371
TAwaitableTask(ThisClass &&Other)
Definition AwaitableTask.h:406
bool IsCompleted() const
Definition AwaitableTask.h:496
ThisClass & operator=(const ThisClass &Other)
Definition AwaitableTask.h:411
void Init(const TCHAR *DebugName, LowLevelTasks::ETaskPriority Priority, CallableT &&Callable)
Definition AwaitableTask.h:436
void Init(const TCHAR *DebugName, CallableT &&Callable)
Definition AwaitableTask.h:449
bool InitAndLaunch(const TCHAR *DebugName, LowLevelTasks::ETaskPriority Priority, CallableT &&Callable)
Definition AwaitableTask.h:462
void Reset()
Definition AwaitableTask.h:478
TAwaitableTask(const ThisClass &Other)
Definition AwaitableTask.h:400
bool InitAndLaunch(const TCHAR *DebugName, CallableT &&Callable)
Definition AwaitableTask.h:472
~TAwaitableTask()
Definition AwaitableTask.h:395
TAwaitableTask()
Definition AwaitableTask.h:389
bool TryLaunch()
Definition AwaitableTask.h:455
ReturnType Await()
Definition AwaitableTask.h:504
bool IsLaunched() const
Definition AwaitableTask.h:484
bool IsValid() const
Definition AwaitableTask.h:490
Definition Future.h:541
Definition ManualResetEvent.h:15
Definition AwaitableTask.h:13
ReturnType MakeDummyValue()
Definition AwaitableTask.h:15
void MakeDummyValue< void >()
Definition AwaitableTask.h:21
ETaskPriority
Definition Task.h:18
UE_FORCEINLINE_HINT bool TryLaunch(FTask &Task, EQueuePreference QueuePreference=EQueuePreference::DefaultPreference, bool bWakeUpWorker=true)
Definition Scheduler.h:181
Definition IsInvocable.h:47