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#include "Logging/LogMacros.h"
6#include "TaskDelegate.h"
7#include "HAL/Event.h"
8#include "CoreTypes.h"
9#include <atomic>
10
11#define LOWLEVEL_TASK_SIZE PLATFORM_CACHE_LINE_SIZE
12
13namespace LowLevelTasks
14{
16
17 enum class ETaskPriority : int8
18 {
19 High,
20 Normal,
26 Count,
27 Inherit, //Inherit the TaskPriority from the launching Task or the Default Priority if not launched from a Task.
28 };
29
30 inline const TCHAR* ToString(ETaskPriority Priority)
31 {
33 {
34 return nullptr;
35 }
36
37 const TCHAR* TaskPriorityToStr[] =
38 {
39 TEXT("High"),
40 TEXT("Normal"),
41 TEXT("BackgroundHigh"),
42 TEXT("BackgroundNormal"),
43 TEXT("BackgroundLow")
44 };
46 }
47
49 {
51 {
53 return true;
54 }
55
57 {
59 return true;
60 }
61
63 {
65 return true;
66 }
67
69 {
71 return true;
72 }
73
75 {
77 return true;
78 }
79
80 return false;
81 }
82
84 {
85 None = 0 << 0,
86 TryLaunchOnSuccess = 1 << 0, // try to launch and the continuation immediately if it was not launched yet (requires PrelaunchCancellation to work)
87 PrelaunchCancellation = 1 << 1, // allow cancellation before a task has been launched (this also allows the optimization of TryLaunchOnSuccess)
89 };
91
101
102 /*
103 * (I)nitThread: STORE(I)----------------------CAS(C)----------------------
104 * (C)ancelingThread: --->| Ready |<-->| CanceledAndReady |
105 * ---------------------- ----------------------
106 * |OR(L) |OR(L)
107 * V V
108 * (L)aunchingThread: --------------------------------------------------CAS(E)----------------------CAS(C)----------------------
109 * (C)ancelingThread: | Running |<---| Scheduled |<-->| Canceled |
110 * (E)xpeditingThread: -------------------------------------------------- ---------------------- ----------------------
111 * |OR(E) |OR(W) |OR(W) |OR(W)
112 * V V V V
113 * (W)orkerThread: ---------------------- OR(E)---------------------- ---------------------- ----------------------
114 * (E)xpeditingThread: | Expedited |<---| Expediting | | Running | | CanceledAndRunning |
115 * ---------------------- ---------------------- ---------------------- ----------------------
116 * |OR(W,E) |OR(W) |OR(W)
117 * V V V
118 * (W)orkerThread: ---------------------- ---------------------- ----------------------
119 * (E)xpeditingThread: |ExpeditedAndCompleted | | Completed | | CanceledAndCompleted |
120 * ---------------------- ---------------------- ----------------------
121 */
123 {
124 ReadyState = 0,
125 CanceledFlag = 1 << 0,
126 ScheduledFlag = 1 << 1,
127 RunningFlag = 1 << 2,
128 ExpeditingFlag = 1 << 3,
129 ExpeditedFlag = 1 << 4,
130 CompletedFlag = 1 << 5, //the default state when we create a handle
131 Count = (1 << 6) - 1,
132
133 Ready = ReadyState, //means the Task is ready to be launched
134 CanceledAndReady = Ready | CanceledFlag, //means the task was canceled and is ready to be launched (it still is required to be launched)
135 Scheduled = Ready | ScheduledFlag, //means the task is launched and therefore queued for execution by a worker
136 Canceled = CanceledAndReady | ScheduledFlag, //means the task was canceled and launched and therefore queued for execution by a worker (which already might be executing it's continuation)
137 Running = Scheduled | RunningFlag, //means the task is executing it's runnable and continuation by a worker
138 CanceledAndRunning = Canceled | RunningFlag, //means the task is executing it's continuation but the runnable was cancelled
139 Expediting = Running | ExpeditingFlag, //means the task is expediting and the scheduler has released it's reference to the expediting thread before that was finished
140 Expedited = Expediting | ExpeditedFlag, //means the task was expedited
141 Completed = Running | CompletedFlag, //means the task is completed with execution
142 ExpeditedAndCompleted = Expedited | CompletedFlag, //means the task is completed with execution and the runnable was expedited
143 CanceledAndCompleted = CanceledAndRunning | CompletedFlag, //means the task is completed with execution of it's continuation but the runnable was cancelled
144 };
146
147 /*
148 * Generic implementation of a Deleter, it often comes up that one has to call a function to cleanup after a Task finished
149 * this can be done by capturing a TDeleter like so: [Deleter(LowLevelTasks::TDeleter<Type, &Type::DeleteFunction>(value))](){}
150 */
151 template<typename Type, void (Type::*DeleteFunction)()>
153 {
154 Type* Value;
155
156 public:
157 inline TDeleter(Type* InValue) : Value(InValue)
158 {
159 }
160
161 inline TDeleter(const TDeleter&) = delete;
162 inline TDeleter(TDeleter&& Other) : Value(Other.Value)
163 {
164 Other.Value = nullptr;
165 }
166
167 inline Type* operator->() const
168 {
169 return Value;
170 }
171
172 inline ~TDeleter()
173 {
174 if(Value)
175 {
176 (Value->*DeleteFunction)();
177 }
178 }
179 };
180
181 /*
182 * this class is just here to hide some variables away
183 * because we don't want to become too close friends with the FScheduler
184 */
185 class FTask;
186 namespace Tasks_Impl
187 {
189 {
190 class FPackedDataAtomic;
191
192 friend class ::LowLevelTasks::FTask;
193 UE_NONCOPYABLE(FTaskBase); //means non movable
194
195 union FPackedData
196 {
197 uintptr_t PackedData;
198 struct
199 {
200 uintptr_t State : 6;
201 uintptr_t DebugName : 53;
203 uintptr_t Flags : 2;
204 };
205
206 private:
207 friend class FTaskBase::FPackedDataAtomic;
208 FPackedData(uintptr_t InPackedData) : PackedData(InPackedData)
209 {}
210
211 constexpr FPackedData()
213 , DebugName(0ull)
216 {
217 static_assert(!PLATFORM_32BITS, "32bit Platforms are not supported");
218 static_assert(uintptr_t(ETaskPriority::Count) <= (1ull << 3), "Not enough bits to store ETaskPriority");
219 static_assert(uintptr_t(ETaskState::Count) <= (1ull << 6), "Not enough bits to store ETaskState");
220 static_assert(uintptr_t(ETaskFlags::AllowEverything) < (1ull << 2), "Not enough bits to store ETaskFlags");
221 }
222
223 public:
225 : State((uintptr_t)InState)
229
230 {
231 checkSlow(reinterpret_cast<uintptr_t>(InDebugName) < (1ull << 53));
232 checkSlow((uintptr_t)InPriority < (1ull << 3));
233 checkSlow((uintptr_t)InState < (1ull << 6));
234 checkSlow((uintptr_t)InFlags < (1ull << 2));
235 static_assert(sizeof(FPackedData) == sizeof(uintptr_t), "Packed data needs to be pointer size");
236 }
237
238 FPackedData(const FPackedData& Other, ETaskState State)
239 : FPackedData(Other.GetDebugName(), Other.GetPriority(), State, Other.GetFlags())
240 {
241 }
242
243 inline const TCHAR* GetDebugName() const
244 {
245 return reinterpret_cast<const TCHAR*>(DebugName);
246 }
247
248 inline ETaskPriority GetPriority() const
249 {
250 return ETaskPriority(Priority);
251 }
252
253 inline ETaskState GetState() const
254 {
255 return ETaskState(State);
256 }
257
258 inline ETaskFlags GetFlags() const
259 {
260 return ETaskFlags(Flags);
261 }
262 };
263
264 class FPackedDataAtomic
265 {
266 std::atomic<uintptr_t> PackedData { FPackedData().PackedData };
267
268 public:
269 ETaskState fetch_or(ETaskState State, std::memory_order Order)
270 {
271 return ETaskState(FPackedData(PackedData.fetch_or(uintptr_t(State), Order)).State);
272 }
273
274 bool compare_exchange_strong(FPackedData& Expected, FPackedData Desired, std::memory_order Success, std::memory_order Failure)
275 {
276 return PackedData.compare_exchange_strong(Expected.PackedData, Desired.PackedData, Success, Failure);
277 }
278
279 bool compare_exchange_strong(FPackedData& Expected, FPackedData Desired, std::memory_order Order)
280 {
281 return PackedData.compare_exchange_strong(Expected.PackedData, Desired.PackedData, Order);
282 }
283
284 FPackedData load(std::memory_order Order) const
285 {
286 return PackedData.load(Order);
287 }
288
289 void store(const FPackedData& Expected, std::memory_order Order)
290 {
291 PackedData.store(Expected.PackedData, Order);
292 }
293 };
294
295 private:
296 using FTaskDelegate = TTaskDelegate<FTask*(bool), LOWLEVEL_TASK_SIZE - sizeof(FPackedData) - sizeof(void*)>;
297 FTaskDelegate Runnable;
298 mutable void* UserData = nullptr;
299 FPackedDataAtomic PackedData;
300
301 private:
302 FTaskBase() = default;
303 };
304 }
305
306 /*
307 * minimal low level task interface
308 */
309 class FTask final : private Tasks_Impl::FTaskBase
310 {
311 friend class FScheduler;
312 UE_NONCOPYABLE(FTask); //means non movable
313
314 static thread_local FTask* ActiveTask;
315
316 public:
317
318 /*
319 * means the task is completed and this taskhandle can be recycled
320 */
321 inline bool IsCompleted(std::memory_order MemoryOrder = std::memory_order_seq_cst) const
322 {
323 ETaskState State = PackedData.load(MemoryOrder).GetState();
325 }
326
327 /*
328 * means the task was canceled but might still need to be launched
329 */
330 inline bool WasCanceled() const
331 {
332 ETaskState State = PackedData.load(std::memory_order_relaxed).GetState();
334 }
335
336 /*
337 * means the task was expedited or that it already completed
338 */
339 inline bool WasExpedited() const
340 {
341 ETaskState State = PackedData.load(std::memory_order_acquire).GetState();
343 }
344
345 private:
346 //Scheduler internal interface to speed things up
347 inline bool WasCanceledOrIsExpediting() const
348 {
349 ETaskState State = PackedData.load(std::memory_order_relaxed).GetState();
351 }
352
353 public:
354 /*
355 * means the task is ready to be launched but might already been canceled
356 */
357 inline bool IsReady() const
358 {
359 ETaskState State = PackedData.load(std::memory_order_relaxed).GetState();
361 }
362
363#if PLATFORM_DESKTOP || !IS_MONOLITHIC
364 /*
365 * get the currently active task if any
366 */
367 CORE_API static const FTask* GetActiveTask();
368#else
370 {
371 return ActiveTask;
372 }
373#endif
374
375 /*
376 * try to cancel the task if it has not been launched yet.
377 */
379
380 /*
381 * try to revive a canceled task (as in reverting the cancellation as if it never happened).
382 * if it had been canceled and the scheduler has not run it yet it succeeds.
383 */
384 inline bool TryRevive();
385
386 /*
387 * try to expedite the task if succeded it will run immediately
388 * but it will not set the completed state until the scheduler has executed it, because the scheduler still holds a reference.
389 * to check for completion in the context of expediting use WasExpedited. The TaskHandle canot be reused until IsCompleted returns true.
390 * @param Continuation: optional Continuation that needs to be executed or scheduled by the caller (can only be non null if the operation returned true)
391 */
392 inline bool TryExpedite();
393 inline bool TryExpedite(FTask*& Continuation);
394
395 /*
396 * try to execute the task if it has not been launched yet the task will execute immediately.
397 * @param Continuation: optional Continuation that needs to be executed or scheduled by the caller (can only be non null if the operation returned true)
398 */
399 inline bool TryExecute();
400 inline bool TryExecute(FTask*& Continuation);
401
402 template<typename TRunnable>
404
405 template<typename TRunnable>
407
408 inline const TCHAR* GetDebugName() const;
409 inline ETaskPriority GetPriority() const;
410 inline bool IsBackgroundTask() const;
411 inline bool AllowBusyWaiting() const;
412 inline bool AllowCancellation() const;
413
420 inline FInitData GetInitData() const;
421
422 void* GetUserData() const { return UserData; }
423 void SetUserData(void* NewUserData) const { UserData = NewUserData; }
424
425 public:
426 FTask() = default;
427 inline ~FTask();
428
429 private: //Interface of the Scheduler
430 inline static bool PermitBackgroundWork()
431 {
432 return ActiveTask && ActiveTask->IsBackgroundTask();
433 }
434
435 inline bool TryPrepareLaunch();
436 //after calling this function the task can be considered dead
437 template<bool bIsExpeditingThread>
438 inline void TryFinish();
439
440 inline FTask* ExecuteTask();
441 inline void InheritParentData(ETaskPriority& Priority);
442 };
443
444 /******************
445 * IMPLEMENTATION *
446 ******************/
447
449 {
450 return PackedData.load(std::memory_order_relaxed).GetPriority();
451 }
452
453 inline void FTask::InheritParentData(ETaskPriority& Priority)
454 {
456 if (LocalActiveTask != nullptr)
457 {
459 {
460 Priority = LocalActiveTask->GetPriority();
461 }
462 UserData = LocalActiveTask->GetUserData();
463 }
464 else
465 {
467 {
469 }
470 UserData = nullptr;
471 }
472 }
473
474 template<typename TRunnable>
476 {
477 checkf(IsCompleted(), TEXT("State: %d"), PackedData.load(std::memory_order_relaxed).GetState());
478 checkSlow(!Runnable.IsSet());
479
480 //if the Runnable returns an FTask* than enable symetric switching
481 if constexpr (std::is_same_v<FTask*, decltype(UE::Core::Private::IsInvocable::DeclVal<TRunnable>()())>)
482 {
483 Runnable = [LocalRunnable = Forward<TRunnable>(InRunnable)](const bool bNotCanceled) mutable -> FTask*
484 {
485 if (bNotCanceled)
486 {
488 return Task;
489 }
490 return nullptr;
491 };
492 }
493 else
494 {
495 Runnable = [LocalRunnable = Forward<TRunnable>(InRunnable)](const bool bNotCanceled) mutable -> FTask*
496 {
497 if (bNotCanceled)
498 {
500 }
501 return nullptr;
502 };
503 }
504 InheritParentData(InPriority);
505 PackedData.store(FPackedData(InDebugName, InPriority, ETaskState::Ready, Flags), std::memory_order_release);
506 }
507
508 template<typename TRunnable>
513
515 {
516 checkf(IsCompleted(), TEXT("State: %d"), PackedData.load(std::memory_order_relaxed).GetState());
517 }
518
519 inline bool FTask::TryPrepareLaunch()
520 {
521 return !EnumHasAnyFlags(PackedData.fetch_or(ETaskState::ScheduledFlag, std::memory_order_release), ETaskState::ScheduledFlag);
522 }
523
525 {
528
529 FPackedData LocalPackedData = PackedData.load(std::memory_order_relaxed);
532 //to launch a canceled task it has to go though TryPrepareLaunch which is doing the memory_order_release
534 && ((bPrelaunchCancellation && PackedData.compare_exchange_strong(ReadyState, FPackedData(LocalPackedData, ETaskState::CanceledAndReady), std::memory_order_acquire))
535 || PackedData.compare_exchange_strong(ScheduledState, FPackedData(LocalPackedData, ETaskState::Canceled), std::memory_order_acquire));
536
537 if(bTryLaunchOnSuccess && WasCanceled && TryPrepareLaunch())
538 {
539 verifySlow(ExecuteTask() == nullptr);
540 return true;
541 }
542 return WasCanceled;
543 }
544
545 inline bool FTask::TryRevive()
546 {
547 FPackedData LocalPackedData = PackedData.load(std::memory_order_relaxed);
550 {
551 return false;
552 }
553
556 return PackedData.compare_exchange_strong(CanceledReadyState, FPackedData(LocalPackedData, ETaskState::Ready), std::memory_order_release)
557 || PackedData.compare_exchange_strong(CanceledState, FPackedData(LocalPackedData, ETaskState::Scheduled), std::memory_order_release);
558 }
559
561 {
562 if(TryPrepareLaunch())
563 {
564 OutContinuation = ExecuteTask();
565 return true;
566 }
567 return false;
568 }
569
570 inline bool FTask::TryExecute()
571 {
572 FTask* Continuation = nullptr;
573 bool Result = TryExecute(Continuation);
574 checkSlow(Continuation == nullptr);
575 return Result;
576 }
577
578 template<bool bIsExpeditingThread>
579 inline void FTask::TryFinish()
580 {
582 ETaskState PreviousState = PackedData.fetch_or(NextState, std::memory_order_acq_rel);
583 if constexpr (bIsExpeditingThread)
584 {
585 checkSlow(PreviousState == ETaskState::Running || PreviousState == ETaskState::Expediting);
586 }
588 {
589 FTaskDelegate LocalRunnable = MoveTemp(Runnable);
590 //do not access the task again after this call
591 //as by defitition the task can be considered dead
592 PreviousState = PackedData.fetch_or(ETaskState::CompletedFlag, std::memory_order_seq_cst);
593 checkSlow(PreviousState == ETaskState::Expedited);
594 }
595 }
596
598 {
599 FPackedData LocalPackedData = PackedData.load(std::memory_order_relaxed);
601 if(PackedData.compare_exchange_strong(ScheduledState, FPackedData(LocalPackedData, ETaskState::Running), std::memory_order_acquire))
602 {
603 OutContinuation = Runnable(true);
605 return true;
606 }
607 return false;
608 }
609
610 inline bool FTask::TryExpedite()
611 {
612 FTask* Continuation = nullptr;
613 bool Result = TryExpedite(Continuation);
614 checkSlow(Continuation == nullptr);
615 return Result;
616 }
617
618 inline FTask* FTask::ExecuteTask()
619 {
620 ETaskState PreviousState = PackedData.fetch_or(ETaskState::RunningFlag, std::memory_order_acquire);
622
623 FTask* Continuation = nullptr;
624 if(!EnumHasAnyFlags(PreviousState, ETaskState::RunningFlag)) //we are running or canceled
625 {
626 FTaskDelegate LocalRunnable;
627 Continuation = Runnable.CallAndMove(LocalRunnable, !EnumHasAnyFlags(PreviousState, ETaskState::CanceledFlag));
628 //do not access the task again after this call
629 //as by defitition the task can be considered dead
630 PreviousState = PackedData.fetch_or(ETaskState::CompletedFlag, std::memory_order_seq_cst);
631 checkSlow(PreviousState == ETaskState::Running || PreviousState == ETaskState::CanceledAndRunning);
632 }
633 else // we are expedited
634 {
635 checkSlow(PreviousState == ETaskState::Running || PreviousState == ETaskState::Expediting || PreviousState == ETaskState::Expedited);
637 }
638
639 return Continuation;
640 }
641
642 inline const TCHAR* FTask::GetDebugName() const
643 {
644 return PackedData.load(std::memory_order_relaxed).GetDebugName();
645 }
646
647 inline bool FTask::IsBackgroundTask() const
648 {
649 return PackedData.load(std::memory_order_relaxed).GetPriority() >= ETaskPriority::ForegroundCount;
650 }
651
652 inline bool FTask::AllowBusyWaiting() const
653 {
654 return EnumHasAnyFlags(PackedData.load(std::memory_order_relaxed).GetFlags(), ETaskFlags::AllowBusyWaiting);
655 }
656
657 inline bool FTask::AllowCancellation() const
658 {
659 return EnumHasAnyFlags(PackedData.load(std::memory_order_relaxed).GetFlags(), ETaskFlags::AllowCancellation);
660 }
661
663 {
664 FPackedData LocalPackedData = PackedData.load(std::memory_order_relaxed);
665 return { LocalPackedData.GetDebugName(), LocalPackedData.GetPriority(), LocalPackedData.GetFlags() };
666 }
667}
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define verifySlow(expr)
Definition AssertionMacros.h:334
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
#define LOWLEVEL_TASK_SIZE
Definition Task.h:11
#define UE_NONCOPYABLE(TypeName)
Definition CoreMiscDefines.h:457
FPlatformTypes::int8 int8
An 8-bit signed integer.
Definition Platform.h:1121
#define TEXT(x)
Definition Platform.h:1272
#define PLATFORM_32BITS
Definition Platform.h:682
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 UE_FORCEINLINE_HINT
Definition Platform.h:723
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
constexpr bool EnumHasAllFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:28
#define ENUM_CLASS_FLAGS(Enum)
Definition EnumClassFlags.h:6
UE_FORCEINLINE_HINT uint64 GetState() const
Definition LockFreeList.h:46
void Init()
Definition LockFreeList.h:4
#define DECLARE_LOG_CATEGORY_EXTERN(CategoryName, DefaultVerbosity, CompileTimeVerbosity)
Definition LogMacros.h:361
const bool
Definition NetworkReplayStreaming.h:178
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
Definition Scheduler.h:103
Definition Task.h:310
void * GetUserData() const
Definition Task.h:422
void Init(const TCHAR *InDebugName, ETaskPriority InPriority, TRunnable &&InRunnable, ETaskFlags Flags=ETaskFlags::DefaultFlags)
Definition Task.h:475
bool AllowBusyWaiting() const
Definition Task.h:652
FInitData GetInitData() const
Definition Task.h:662
void SetUserData(void *NewUserData) const
Definition Task.h:423
bool WasExpedited() const
Definition Task.h:339
bool AllowCancellation() const
Definition Task.h:657
~FTask()
Definition Task.h:514
static CORE_API const FTask * GetActiveTask()
Definition Scheduler.cpp:656
bool TryExpedite()
Definition Task.h:610
bool TryCancel(ECancellationFlags CancellationFlags=ECancellationFlags::DefaultFlags)
Definition Task.h:524
bool IsCompleted(std::memory_order MemoryOrder=std::memory_order_seq_cst) const
Definition Task.h:321
ETaskPriority GetPriority() const
Definition Task.h:448
const TCHAR * GetDebugName() const
Definition Task.h:642
bool WasCanceled() const
Definition Task.h:330
bool IsReady() const
Definition Task.h:357
bool TryExecute()
Definition Task.h:570
bool TryRevive()
Definition Task.h:545
bool IsBackgroundTask() const
Definition Task.h:647
Definition Task.h:153
~TDeleter()
Definition Task.h:172
Type * operator->() const
Definition Task.h:167
TDeleter(const TDeleter &)=delete
TDeleter(Type *InValue)
Definition Task.h:157
TDeleter(TDeleter &&Other)
Definition Task.h:162
friend class ::LowLevelTasks::FTask
Definition Task.h:192
uint32 GetFlags(uint32 Word3)
Definition CollisionFilterData.cpp:21
Definition Scheduler.cpp:25
ECancellationFlags
Definition Task.h:84
ETaskPriority
Definition Task.h:18
bool ToTaskPriority(const TCHAR *PriorityStr, ETaskPriority &OutPriority)
Definition Task.h:48
ETaskState
Definition Task.h:123
ETaskFlags
Definition Task.h:93
State
Definition PacketHandler.h:88
Definition Task.h:415
ETaskPriority Priority
Definition Task.h:417
ETaskFlags Flags
Definition Task.h:418
const TCHAR * DebugName
Definition Task.h:416
static UE_FORCEINLINE_HINT int32 Stricmp(const CharType *String1, const CharType *String2)
Definition CString.h:1030