UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Task.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Tasks/TaskPrivate.h"
9#include "HAL/Event.h"
10#include "HAL/IConsoleManager.h"
11#include "CoreTypes.h"
12
13namespace UE::Tasks
14{
15 template<typename ResultType>
16 class TTask;
17
18 template<typename TaskCollectionType>
20
21 template<typename TaskType> void AddNested(const TaskType& Nested);
22
23 namespace Private
24 {
25 template<typename TaskCollectionType>
27
28 // a common part of the generic `TTask<ResultType>` and its `TTask<void>` specialisation
30 {
31 // friends to get access to `Pimpl`
32 friend FTaskBase;
33
34 template<int32 Index, typename ArrayType, typename FirstTaskType, typename... OtherTasksTypes>
36
37 template<int32 Index, typename ArrayType, typename TaskType>
38 friend void PrerequisitesUnpacker(ArrayType& Array, TaskType& FirstTask);
39
40 template<typename TaskCollectionType>
42
43 template<typename TaskCollectionType>
45
46 template<typename TaskCollectionType>
48
49 template<typename TaskType>
50 friend void UE::Tasks::AddNested(const TaskType& Nested);
51
52 protected:
54 : Pimpl(Other, /*bAddRef = */false)
55 {}
56
57 public:
59
60 FTaskHandle() = default;
61
62 bool IsValid() const
63 {
64 return Pimpl.IsValid();
65 }
66
67 // checks if task's execution is done
68 bool IsCompleted() const
69 {
70 return !IsValid() || Pimpl->IsCompleted();
71 }
72
73 // waits for task's completion with timeout. Tries to retract the task and execute it in-place, if failed - blocks until the task
74 // is completed by another thread. If timeout is zero, tries to retract the task and returns immedially after that.
75 // @return true if the task is completed
77 {
78 return !IsValid() || Pimpl->Wait(FTimeout{ Timeout });
79 }
80
81 // waits for task's completion without timeout. Tries to retract the task and execute it in-place, if failed - blocks until the task
82 // is completed by another thread.
83 // @return true if the task is completed
84 bool Wait() const
85 {
86 if (IsValid())
87 {
88 Pimpl->Wait();
89 }
90
91 // we keep the return type as boolean even if always returning true
92 // as we could merge some versions of the Wait API back at some point.
93 return true;
94 }
95
96 // try to finish the task on the current thread if it's not already started.
97 // @return true if the task is completed
99 {
100 Pimpl->TryRetractAndExecute(FTimeout::Never());
101 return IsCompleted();
102 }
103
104 // launches a task for asynchronous execution
105 // @param DebugName - a unique name for task identification in debugger and profiler, is compiled out in test/shipping builds
106 // @param TaskBody - a functor that will be executed asynchronously
107 // @param Priority - task priority that affects when the task will be executed
108 // @param TaskFlags - task config options
109 // @return a trivially relocatable instance that can be used to wait for task completion or to obtain task execution result
110 template<typename TaskBodyType>
111 void Launch(
112 const TCHAR* DebugName,
114 ETaskPriority Priority = ETaskPriority::Normal,
117 )
118 {
119 check(!IsValid());
120
122 FExecutableTask* Task = FExecutableTask::Create(DebugName, Forward<TaskBodyType>(TaskBody), Priority, ExtendedPriority, Flags);
123 // this must happen before launching, to support an ability to access the task itself from inside it
124 *Pimpl.GetInitReference() = Task;
125 Task->TryLaunch(sizeof(*Task));
126 }
127
128 // launches a task for asynchronous execution, with prerequisites that must be completed before the task is scheduled
129 // @param DebugName - a unique name for task identification in debugger and profiler, is compiled out in test/shipping builds
130 // @param TaskBody - a functor that will be executed asynchronously
131 // @param Prerequisites - tasks or task events that must be completed before the task being launched can be scheduled, accepts any
132 // iterable collection (.begin()/.end()), `Tasks::Prerequisites()` helper is recommended to create such collection on the fly
133 // @param Priority - task priority that affects when the task will be executed
134 // @param TaskFlags - task config options
135 // @return a trivially relocatable instance that can be used to wait for task completion or to obtain task execution result
136 template<typename TaskBodyType, typename PrerequisitesCollectionType>
137 void Launch(
138 const TCHAR* DebugName,
141 ETaskPriority Priority = ETaskPriority::Normal,
144 )
145 {
146 check(!IsValid());
147
149 FExecutableTask* Task = FExecutableTask::Create(DebugName, Forward<TaskBodyType>(TaskBody), Priority, ExtendedPriority, Flags);
151 // this must happen before launching, to support an ability to access the task itself from inside it
152 *Pimpl.GetInitReference() = Task;
153 Task->TryLaunch(sizeof(*Task));
154 }
155
156 bool IsAwaitable() const
157 {
158 return IsValid() && Pimpl->IsAwaitable();
159 }
160
161 bool operator==(const FTaskHandle& Other) const
162 {
163 return Pimpl == Other.Pimpl;
164 }
165
166 bool operator!=(const FTaskHandle& Other) const
167 {
168 return Pimpl != Other.Pimpl;
169 }
170
171 ETaskPriority GetPriority() const
172 {
173 return Pimpl->GetPriority();
174 }
175
177 {
178 return Pimpl->GetExtendedPriority();
179 }
180
181
182 protected:
184 };
185 }
186
187 // a movable/copyable handle of `Private::TTaskWithResult*` with the API adopted for public usage.
188 // implements Pimpl idiom
189 template<typename ResultType>
191 {
192 public:
193 TTask() = default;
194
195 // waits until the task is completed and returns task's result
196 ResultType& GetResult()
197 {
198 check(IsValid());
199 FTaskHandle::Wait();
200 return static_cast<Private::TTaskWithResult<ResultType>*>(Pimpl.GetReference())->GetResult();
201 }
202
203 private:
204 friend FPipe;
205
206 // private constructor, valid instances can be created only by launching (see friends)
209 {}
210 };
211
212 template<>
214 {
215 public:
216 TTask() = default;
217
219 {
220 check(IsValid()); // to be consistent with a generic `TTask<ResultType>::GetResult()`
221 Wait();
222 }
223
224 private:
225 friend FPipe;
226
227 // private constructor, valid instances can be created only by launching (see friends)
230 {}
231 };
232
233 // A synchronisation primitive, a recommended substitution of `FEvent` for signalling between tasks. If used as a task prerequisite or
234 // a nested task, it doesn't block a worker thread. Optionally can use "busy waiting" - executing tasks while waiting.
236 {
237 public:
238 explicit FTaskEvent(const TCHAR* DebugName)
239 : Private::FTaskHandle(Private::FTaskEventBase::Create(DebugName))
240 {
241 }
242
243 // all prerequisites must be added before triggering the event
244 template<typename PrerequisitesType>
246 {
247 Pimpl->AddPrerequisites(Prerequisites);
248 }
249
250 void Trigger()
251 {
252 if (!IsCompleted()) // event can be triggered multiple times
253 {
254 Pimpl->Trigger(sizeof(*Pimpl));
255 }
256 }
257 };
258
259 // launches a task for asynchronous execution
260 // @param DebugName - a unique name for task identification in debugger and profiler, is compiled out in test/shipping builds
261 // @param TaskBody - a functor that will be executed asynchronously
262 // @param Priority - task priority that affects when the task will be executed
263 // @param TaskFlags - task config options
264 // @return a trivially relocatable instance that can be used to wait for task completion or to obtain task execution result
265 template<typename TaskBodyType>
267 const TCHAR* DebugName,
269 ETaskPriority Priority = ETaskPriority::Normal,
272 )
273 {
274 using FResult = TInvokeResult_T<TaskBodyType>;
276 Task.Launch(DebugName, Forward<TaskBodyType>(TaskBody), Priority, ExtendedPriority, Flags);
277 return Task;
278 }
279
280 // launches a task for asynchronous execution, with prerequisites that must be completed before the task is scheduled
281 // @param DebugName - a unique name for task identification in debugger and profiler, is compiled out in test/shipping builds
282 // @param TaskBody - a functor that will be executed asynchronously
283 // @param Prerequisites - tasks or task events that must be completed before the task being launched can be scheduled, accepts any
284 // iterable collection (.begin()/.end()), `Tasks::Prerequisites()` helper is recommended to create such collection on the fly
285 // @param Priority - task priority that affects when the task will be executed
286 // @param TaskFlags - task config options
287 // @return a trivially relocatable instance that can be used to wait for task completion or to obtain task execution result
288 template<typename TaskBodyType, typename PrerequisitesCollectionType>
290 const TCHAR* DebugName,
293 ETaskPriority Priority = ETaskPriority::Normal,
296 )
297 {
298 using FResult = TInvokeResult_T<TaskBodyType>;
301 return Task;
302 }
303
304 namespace Private
305 {
306 template<typename TaskCollectionType>
308 {
309#if UE_TASK_TRACE_ENABLED
311 TasksIds.Reserve(Tasks.Num());
312
313 for (auto& Task : Tasks)
314 {
315 if (Task.IsValid())
316 {
317 TasksIds.Add(Task.Pimpl->GetTraceId());
318 }
319 }
320
321 return TasksIds;
322#else
323 return {};
324#endif
325 }
326 }
327
329 {
330 Task.Wait();
331 }
332
334
335 namespace Private
336 {
337 template<int32 Index, typename ArrayType, typename FirstTaskType, typename... OtherTasksTypes>
343
344 template<int32 Index, typename ArrayType, typename TaskType>
345 void PrerequisitesUnpacker(ArrayType& Array, TaskType& Task)
346 {
347 Array[Index] = Task.Pimpl.GetReference();
348 }
349
350 template<typename HigherLevelTaskType, std::enable_if_t<std::is_same_v<HigherLevelTaskType, FTask>>* = nullptr>
351 bool IsCompleted(const HigherLevelTaskType& Prerequisite)
352 {
353 return Prerequisite.IsCompleted();
354 }
355
356 template<typename HigherLevelTaskType, std::enable_if_t<std::is_same_v<HigherLevelTaskType, FGraphEventRef>>* = nullptr>
357 bool IsCompleted(const HigherLevelTaskType& Prerequisite)
358 {
359 return Prerequisite.IsValid() ? Prerequisite->IsCompleted() : false;
360 }
361 }
362
363 template<typename... TaskTypes,
364 typename std::decay_t<decltype(std::declval<TTuple<TaskTypes...>>().template Get<0>())>::FTaskHandleId* = nullptr>
366 {
367 TStaticArray<Private::FTaskBase*, sizeof...(TaskTypes)> Res;
368 Private::PrerequisitesUnpacker<0>(Res, Tasks...);
369 return Res;
370 }
371
372 template<typename TaskCollectionType>
374 {
375 return Tasks;
376 }
377
378 // wait for multiple tasks, with timeout
379 // @param TaskCollectionType - an iterable collection of `TTask<T>`, e.g. `TArray<FTask>`
380 template<typename TaskCollectionType>
381 bool Wait(const TaskCollectionType& Tasks, FTimespan InTimeout/* = FTimespan::MaxValue()*/)
382 {
385
386 return UE::Tasks::Launch(
387 TEXT("Waiting Task"),
388 [](){},
390 ETaskPriority::Default /* doesn't matter */,
392 ).Wait(InTimeout);
393 }
394
396 // "any task" support. these functions allocate excessively (per input task plus more).
397 // can be reduced to a single alloc if this is a perf issue
398
399 // Blocks the current thread until any of the given tasks is completed.
400 // Is slightly more efficient than `Any().Wait()`.
401 // Returns the index of the first completed task, or `INDEX_NONE` on timeout
402 template<typename TaskCollectionType>
404 {
405 if (UNLIKELY(Tasks.Num() == 0))
406 {
407 return INDEX_NONE;
408 }
409
410 // Avoid memory allocations if any of the events are already completed
411 for (int32 Index = 0; Index < Tasks.Num(); ++Index)
412 {
414 {
415 return Index;
416 }
417 }
418
419 struct FSharedData
420 {
422 std::atomic<int32> CompletedTaskIndex{ 0 };
423 };
424
425 // Shared data usage is important to avoid the variable to go out of scope
426 // before all the task have been run even if we exit after the first event
427 // is triggered.
429
430 for (int32 Index = 0; Index < Tasks.Num(); ++Index)
431 {
433 [SharedData, Index]
434 {
435 SharedData->CompletedTaskIndex.store(Index, std::memory_order_relaxed);
436 SharedData->Event.Notify();
437 },
439 ETaskPriority::Default,
441 );
442 }
443
444 if (SharedData->Event.WaitFor(UE::FMonotonicTimeSpan::FromMilliseconds(Timeout.GetTotalMilliseconds())))
445 {
446 return SharedData->CompletedTaskIndex.load(std::memory_order_relaxed);
447 }
448
449 return INDEX_NONE;
450 }
451
452 // Returns a task that gets completed as soon as any of the given tasks gets completed
453 template<typename TaskCollectionType>
455 {
456 if (UNLIKELY(Tasks.Num() == 0))
457 {
458 return FTask{};
459 }
460
461 struct FSharedData
462 {
464 : RefCount(InitRefCount)
465 {
466 }
467
469 std::atomic<uint32> RefCount;
470 };
471
472 FSharedData* SharedData = new FSharedData(Tasks.Num());
473 // `SharedData` can be destroyed before leaving the scope, cache the result locally
474 FTaskEvent Result = SharedData->Event;
475
476 for (const FTask& Task : Tasks)
477 {
479 [SharedData, Num = Tasks.Num()]
480 {
481 // cache the local copy as `SharedData` can be concurrently deleted right after decrementing the ref counter
482 FTaskEvent Event = SharedData->Event;
483 uint32 PrevRefCount = SharedData->RefCount.fetch_sub(1, std::memory_order_acq_rel); // acq_rel to sync between tasks
484
485 if (UNLIKELY(PrevRefCount == Num))
486 { // the first completed task
487 Event.Trigger();
488 }
489
490 if (UNLIKELY(PrevRefCount == 1))
491 { // the last completed task
492 delete SharedData;
493 }
494 },
496 ETaskPriority::Default,
498 );
499 }
500
501 return Result;
502 }
503
504 // Adds the nested task to the task that is being currently executed by the current thread. A parent task is not flagged completed
505 // until all nested tasks are completed. It's similar to explicitly waiting for a sub-task at the end of its parent task, except explicit waiting
506 // blocks the worker executing the parent task until the sub-task is completed. With nested tasks, the worker won't be blocked.
507 template<typename TaskType>
508 void AddNested(const TaskType& Nested)
509 {
510 Private::FTaskBase* Parent = Private::GetCurrentTask();
511 check(Parent != nullptr);
512 Parent->AddNested(*Nested.Pimpl);
513 }
514
515 // Console variable for configuring task priorities
516 // Example:
517 // FTaskPriorityCVar CVar{ TEXT("CVarName"), TEXT("CVarHelp"), ETaskPriority::Normal, EExtendedTaskPriority::None };
518 // Launch(UE_SOURCE_LOCATION, [] {}, CVar.GetTaskPriority(), CVar.GetExtendedTaskPriority()).Wait();
520 {
521 public:
522 CORE_API FTaskPriorityCVar(const TCHAR* Name, const TCHAR* Help, ETaskPriority DefaultPriority, EExtendedTaskPriority DefaultExtendedPriority);
523
524 ETaskPriority GetTaskPriority() const
525 {
526 return Priority;
527 }
528
530 {
531 return ExtendedPriority;
532 }
533
534 private:
535 static FString CreateFullHelpText(const TCHAR* Name, const TCHAR* OriginalHelp);
536 static FString ConfigStringFromPriorities(ETaskPriority InPriority, EExtendedTaskPriority InExtendedPriority);
537 void OnSettingChanged(IConsoleVariable* Variable);
538
539 private:
540 FString RawSetting;
541 FString FullHelpText;
543 ETaskPriority Priority;
544 EExtendedTaskPriority ExtendedPriority;
545 };
546
547 // creates and returns an already completed task holding a result value constructed from given args
548 template<typename ResultType, typename... ArgTypes>
550 {
551 return Launch(
553 [&] { return ResultType(Forward<ArgTypes>(Args)...); },
554 ETaskPriority::Default, // doesn't matter
555 EExtendedTaskPriority::Inline);
556 }
557
558 // support for canceling tasks mid-execution
559 // usage:
560 // FCancellationToken Token;
561 // Launch(UE_SOURCE_LOCATION, [&Token] { ... if (Token.IsCanceled()) return; ... });
562 // Token.Cancel();
563 // * it's user's decision and responsibility to manage cancellation token lifetime, to check cancellation token, return early,
564 // to do any required cleanup, or to do nothing at all
565 // * no way to cancel a task to skip its execution completely
566 // * waiting for a canceled task is blocking until its prerequisites are completed and the task is executed and completed,
567 // basically same as for not canceled tasks except a canceled one can quit execution early
568 // * canceling a task doesn't affect its subsequents (unless they use the same cancellation token instance)
570 {
571 public:
574
575 void Cancel()
576 {
577 bCanceled.store(true, std::memory_order_relaxed);
578 }
579
580 bool IsCanceled() const
581 {
582 return bCanceled.load(std::memory_order_relaxed);
583 }
584
585 private:
586 std::atomic<bool> bCanceled{ false };
587 };
588
590 {
591 public:
595
596 CORE_API static FCancellationToken* GetCurrentCancellationToken();
597
599 {
600 if (FCancellationToken* CurrentCancellationToken = GetCurrentCancellationToken())
601 {
602 return CurrentCancellationToken->IsCanceled();
603 }
604
605 return false;
606 }
607 private:
608 bool bHasActiveScope = false;
609 void SetToken(FCancellationToken* CancellationToken);
610 };
611}
OODEFFUNC typedef void(OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)(void *ptr)
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#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
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UNLIKELY(x)
Definition Platform.h:857
typename TInvokeResult< FuncType, ArgTypes... >::Type TInvokeResult_T
Definition Invoke.h:135
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define TRACE_CPUPROFILER_EVENT_SCOPE(Name)
Definition CpuProfilerTrace.h:528
@ Num
Definition MetalRHIPrivate.h:234
#define UE_SOURCE_LOCATION
Definition PreprocessorHelpers.h:71
if(Failed) console_printf("Failed.\n")
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition IConsoleManager.h:1580
Definition IConsoleManager.h:558
Definition Launch.Build.cs:10
Definition Array.h:670
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition RefCounting.h:454
Definition SharedPointer.h:153
Definition StaticArray.h:26
Definition ManualResetEvent.h:15
Definition Timeout.h:21
static FTimeout Never()
Definition Timeout.h:49
static bool IsCurrentWorkCanceled()
Definition Task.h:598
Definition Task.h:570
UE_NONCOPYABLE(FCancellationToken)
void Cancel()
Definition Task.h:575
bool IsCanceled() const
Definition Task.h:580
Definition Pipe.h:29
Definition Task.h:236
void AddPrerequisites(const PrerequisitesType &Prerequisites)
Definition Task.h:245
void Trigger()
Definition Task.h:250
FTaskEvent(const TCHAR *DebugName)
Definition Task.h:238
Definition Task.h:520
EExtendedTaskPriority GetExtendedTaskPriority() const
Definition Task.h:529
ETaskPriority GetTaskPriority() const
Definition Task.h:524
Definition TaskPrivate.h:120
bool IsValid() const
Definition Task.h:62
EExtendedTaskPriority GetExtendedPriority() const
Definition Task.h:176
bool operator==(const FTaskHandle &Other) const
Definition Task.h:161
void FTaskHandleId
Definition Task.h:58
bool IsAwaitable() const
Definition Task.h:156
void Launch(const TCHAR *DebugName, TaskBodyType &&TaskBody, PrerequisitesCollectionType &&Prerequisites, ETaskPriority Priority=ETaskPriority::Normal, EExtendedTaskPriority ExtendedPriority=EExtendedTaskPriority::None, ETaskFlags Flags=ETaskFlags::None)
Definition Task.h:137
TRefCountPtr< FTaskBase > Pimpl
Definition Task.h:183
bool TryRetractAndExecute()
Definition Task.h:98
void Launch(const TCHAR *DebugName, TaskBodyType &&TaskBody, ETaskPriority Priority=ETaskPriority::Normal, EExtendedTaskPriority ExtendedPriority=EExtendedTaskPriority::None, ETaskFlags Flags=ETaskFlags::None)
Definition Task.h:111
friend bool TryRetractAndExecute(const TaskCollectionType &Tasks, FTimeout Timeout)
Definition TaskPrivate.h:1007
friend bool UE::Tasks::Wait(const TaskCollectionType &Tasks, FTimespan InTimeout)
bool Wait() const
Definition Task.h:84
ETaskPriority GetPriority() const
Definition Task.h:171
friend void PrerequisitesUnpacker(ArrayType &Array, FirstTaskType &FirstTask, OtherTasksTypes &... OtherTasks)
Definition Task.h:338
bool IsCompleted() const
Definition Task.h:68
bool Wait(FTimespan Timeout) const
Definition Task.h:76
friend TArray< TaskTrace::FId > GetTraceIds(const TaskCollectionType &Tasks)
Definition Task.h:307
FTaskHandle(FTaskBase *Other)
Definition Task.h:53
bool operator!=(const FTaskHandle &Other) const
Definition Task.h:166
Definition TaskPrivate.h:925
Definition TaskPrivate.h:835
void GetResult()
Definition Task.h:218
Definition Task.h:191
ResultType & GetResult()
Definition Task.h:196
Definition OverriddenPropertySet.cpp:45
TArray< TaskTrace::FId > GetTraceIds(const TaskCollectionType &Tasks)
Definition Task.h:307
void PrerequisitesUnpacker(ArrayType &Array, FirstTaskType &FirstTask, OtherTasksTypes &... OtherTasks)
Definition Task.h:338
bool IsCompleted(const HigherLevelTaskType &Prerequisite)
Definition Task.h:351
Definition AnalyticsProviderLog.h:8
void AddNested(const TaskType &Nested)
Definition Task.h:508
TStaticArray< Private::FTaskBase *, sizeof...(TaskTypes)> Prerequisites(TaskTypes &... Tasks)
Definition Task.h:365
ETaskFlags
Definition TaskPrivate.h:89
EExtendedTaskPriority
Definition TaskPrivate.h:60
TTask< TInvokeResult_T< TaskBodyType > > Launch(const TCHAR *DebugName, TaskBodyType &&TaskBody, ETaskPriority Priority=ETaskPriority::Normal, EExtendedTaskPriority ExtendedPriority=EExtendedTaskPriority::None, ETaskFlags Flags=ETaskFlags::None)
Definition Task.h:266
bool Wait(const TaskCollectionType &Tasks, FTimespan InTimeout=FTimespan::MaxValue())
Definition Task.h:381
int32 WaitAny(const TaskCollectionType &Tasks, FTimespan Timeout=FTimespan::MaxValue())
Definition Task.h:403
TTask< ResultType > MakeCompletedTask(ArgTypes &&... Args)
Definition Task.h:549
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
Definition Timespan.h:76
static FTimespan MaxValue()
Definition Timespan.h:686
Definition Tuple.h:652
Definition TaskTrace.h:56
static constexpr FMonotonicTimeSpan FromMilliseconds(double Milliseconds)
Definition MonotonicTime.h:35