UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AsyncWork.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 AsyncWork.h: Definition of queued work classes
5=============================================================================*/
6
7#pragma once
8
9#include "CoreTypes.h"
11#include "Misc/Compression.h"
13#include "Stats/Stats.h"
14#include "HAL/Event.h"
15#include "HAL/PlatformProcess.h"
17#include "Misc/IQueuedWork.h"
20
59template<typename TTask>
62 private IQueuedWork
63{
65 TTask Task;
66
67 /* Generic start function, not called directly
68 * @param bForceSynchronous if true, this job will be started synchronously, now, on this thread
69 **/
71 {
74 FQueuedThreadPool* QueuedPool = InQueuedPool;
76 {
77 QueuedPool = 0;
78 }
79 if (QueuedPool)
80 {
81 QueuedPool->AddQueuedWork(this, InPriority);
82 }
83 else
84 {
85 // we aren't doing async stuff
86 DoWork();
87 }
88 }
89
93 void DoWork()
94 {
96 FScopeCycleCounter Scope(Task.GetStatId(), true);
97
98 Task.DoWork();
99 delete this;
100 }
101
105 virtual void DoThreadedWork()
106 {
107 DoWork();
108 }
109
114 virtual void Abandon(void)
115 {
116 if (Task.CanAbandon())
117 {
118 Task.Abandon();
119 delete this;
120 }
121 else
122 {
123 DoWork();
124 }
125 }
126
127public:
129 template<typename...T>
130 explicit FAutoDeleteAsyncTask(T&&... Args) : Task(Forward<T>(Args)...)
131 {
132 }
133
138 {
139 Start(true, nullptr);
140 }
141
149};
150
151
208 , private IQueuedWork
209{
211 FThreadSafeCounter WorkNotFinishedCounter;
213 FEvent* DoneEvent = nullptr;
215 FQueuedThreadPool* QueuedPool = nullptr;
221 int64 RequiredMemory = -1;
223 const TCHAR * DebugName = nullptr;
225 TStatId StatId;
226
227 /* Internal function to destroy the completion event
228 **/
229 void DestroyEvent()
230 {
232 DoneEvent = nullptr;
233 }
234
235 EQueuedWorkFlags GetQueuedWorkFlags() const final
236 {
237 return Flags;
238 }
239
240 /* Generic start function, not called directly
241 * @param bForceSynchronous if true, this job will be started synchronously, now, on this thread
242 **/
244 {
246
247 FScopeCycleCounter Scope(StatId, true);
249 // default arg has InRequiredMemory == -1
250 RequiredMemory = InRequiredMemory;
251 DebugName = InDebugName;
252
254 CheckIdle(); // can't start a job twice without it being completed first
255 WorkNotFinishedCounter.Increment();
256 QueuedPool = InQueuedPool;
258 Flags = InQueuedWorkFlags;
260 {
261 QueuedPool = 0;
262 }
263 if (QueuedPool)
264 {
265 if (!DoneEvent)
266 {
268 }
269 DoneEvent->Reset();
270 QueuedPool->AddQueuedWork(this, InQueuedWorkPriority);
271 }
272 else
273 {
274 // we aren't doing async stuff
275 DestroyEvent();
276 DoWork();
277 }
278 }
279
283 void DoWork()
284 {
286 FScopeCycleCounter Scope(StatId, true);
287
288 DoTaskWork();
289 check(WorkNotFinishedCounter.GetValue() == 1);
290 WorkNotFinishedCounter.Decrement();
291 }
292
296 void FinishThreadedWork()
297 {
298 check(QueuedPool);
299 if (DoneEvent)
300 {
301 FScopeCycleCounter Scope(StatId, true);
303 DoneEvent->Trigger();
304 }
305 }
306
310 void DoThreadedWork() final
311 {
312 DoWork();
313 FinishThreadedWork();
314 }
315
320 void Abandon() final
321 {
322 if (TryAbandonTask())
323 {
324 check(WorkNotFinishedCounter.GetValue() == 1);
325 WorkNotFinishedCounter.Decrement();
326 }
327 else
328 {
329 DoWork();
330 }
331 FinishThreadedWork();
332 }
333
338 void SyncCompletion(bool bIsLatencySensitive)
339 {
341 if (QueuedPool)
342 {
343 TRACE_CPUPROFILER_EVENT_SCOPE(FAsyncTask::SyncCompletion);
344 FScopeCycleCounter Scope(StatId);
346
347 check(DoneEvent); // if it is not done yet, we must have an event
348 DoneEvent->Wait();
349 QueuedPool = 0;
350 }
351 CheckIdle();
352 }
353
354protected:
356 {
357 StatId = InStatId;
358 }
359
363 void CheckIdle() const
364 {
365 check(WorkNotFinishedCounter.GetValue() == 0);
367 check(!QueuedPool);
368 }
369
371 virtual void DoTaskWork() = 0;
372
376 virtual bool TryAbandonTask() = 0;
377
381 virtual void MarkAsCanceled()
382 {
383 }
384
385public:
388 {
389 // destroying an unfinished task is a bug
390 CheckIdle();
391 DestroyEvent();
392 }
393
398 {
399 return RequiredMemory;
400 }
401
402 const TCHAR * GetDebugName() const final
403 {
404 return DebugName;
405 }
406
415
423
430 {
431 bool DoSyncCompletion = true;
433 {
434 if (QueuedPool)
435 {
436 if (QueuedPool->RetractQueuedWork(this))
437 {
438 // we got the job back, so do the work now and no need to synchronize
439 DoSyncCompletion = false;
440 DoWork();
441 FinishThreadedWork();
442 QueuedPool = 0;
443 }
444 }
445 else if (WorkNotFinishedCounter.GetValue()) // in the synchronous case, if we haven't done it yet, do it now
446 {
447 DoWork();
448 }
449 }
451 {
452 SyncCompletion(bIsLatencySensitive);
453 }
454 CheckIdle(); // Must have had bDoWorkOnThisThreadIfNotStarted == false and needed it to be true for a synchronous job
455 }
456
462 {
463 if (QueuedPool)
464 {
465 if (QueuedPool->RetractQueuedWork(this))
466 {
467 QueuedPool = InQueuedPool;
469 QueuedPool->AddQueuedWork(this, InQueuedWorkPriority);
470
471 return true;
472 }
473 }
474 return false;
475 }
476
482 bool Cancel()
483 {
484 if (QueuedPool)
485 {
486 if (QueuedPool->RetractQueuedWork(this))
487 {
488 check(WorkNotFinishedCounter.GetValue() == 1);
489 WorkNotFinishedCounter.Decrement();
490 FinishThreadedWork();
491 QueuedPool = 0;
492 return true;
493 }
494
495 if (!IsWorkDone())
496 {
498 }
499 }
500 return false;
501 }
502
508 bool WaitCompletionWithTimeout(float TimeLimitSeconds)
509 {
510 if (TimeLimitSeconds <= 0.0f)
511 {
512 return IsDone();
513 }
514
516 if (QueuedPool)
517 {
518 FScopeCycleCounter Scope(StatId);
520
521 uint32 Ms = uint32(TimeLimitSeconds * 1000.0f) + 1;
522 check(Ms);
523
524 check(DoneEvent); // if it is not done yet, we must have an event
525 if (DoneEvent->Wait(Ms))
526 {
527 QueuedPool = 0;
528 CheckIdle();
529 return true;
530 }
531 return false;
532 }
533 CheckIdle();
534 return true;
535 }
536
540 bool IsDone()
541 {
542 if (!IsWorkDone())
543 {
544 return false;
545 }
546 SyncCompletion(/*bIsLatencySensitive = */false);
547 return true;
548 }
549
554 bool IsWorkDone() const
555 {
556 if (WorkNotFinishedCounter.GetValue())
557 {
558 return false;
559 }
560 return true;
561 }
562
566 bool IsIdle() const
567 {
568 return WorkNotFinishedCounter.GetValue() == 0 && QueuedPool == 0;
569 }
570
575
577 {
578 return Priority;
579 }
580};
581
582template<typename TTask>
584 : public FAsyncTaskBase
585{
587 TTask Task;
588
589 struct CMarkAsCanceled
590 {
591 template <typename TaskType>
592 auto Requires(TaskType& Task) -> decltype(
593 Task.MarkAsCanceled()
594 );
595 };
596
600 void MarkAsCanceled() final
601 {
603 {
604 Task.MarkAsCanceled();
605 }
606 }
607public:
609 : Task()
610 {
611 // Cache the StatId to remain backward compatible with TTask that declare GetStatId as non-const.
612 Init(Task.GetStatId());
613 }
614
616 template <typename Arg0Type, typename... ArgTypes>
617 FAsyncTask(Arg0Type&& Arg0, ArgTypes&&... Args)
618 : Task(Forward<Arg0Type>(Arg0), Forward<ArgTypes>(Args)...)
619 {
620 // Cache the StatId to remain backward compatible with TTask that declare GetStatId as non-const.
621 Init(Task.GetStatId());
622 }
623
624 /* Retrieve embedded user job, not legal to call while a job is in process
625 * @return reference to embedded user job
626 **/
627 TTask& GetTask()
628 {
629 CheckIdle(); // can't modify a job without it being completed first
630 return Task;
631 }
632
633 /* Retrieve embedded user job, not legal to call while a job is in process
634 * @return reference to embedded user job
635 **/
636 const TTask& GetTask() const
637 {
638 CheckIdle(); // could be safe, but I won't allow it anyway because the data could be changed while it is being read
639 return Task;
640 }
641
642 bool TryAbandonTask() final
643 {
644 if (Task.CanAbandon())
645 {
646 Task.Abandon();
647 return true;
648 }
649
650 return false;
651 }
652
653 void DoTaskWork() final
654 {
655 Task.DoWork();
656 }
657};
658
663{
664public:
666 {
667 return false;
668 }
669 void Abandon()
670 {
671 }
672};
#define check(expr)
Definition AssertionMacros.h:314
#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::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
#define DECLARE_SCOPE_CYCLE_COUNTER(CounterName, StatId, GroupId)
Definition Stats.h:653
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
EQueuedWorkFlags
Definition IQueuedWork.h:10
void Init()
Definition LockFreeList.h:4
EQueuedWorkPriority
Definition QueuedThreadPool.h:14
FQueuedThreadPool * GThreadPool
Definition ThreadingBase.cpp:48
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition AsyncWork.h:209
void Init(TStatId InStatId)
Definition AsyncWork.h:355
int64 GetRequiredMemory() const final
Definition AsyncWork.h:397
bool IsWorkDone() const
Definition AsyncWork.h:554
const TCHAR * GetDebugName() const final
Definition AsyncWork.h:402
bool IsIdle() const
Definition AsyncWork.h:566
bool SetPriority(EQueuedWorkPriority QueuedWorkPriority)
Definition AsyncWork.h:571
void EnsureCompletion(bool bDoWorkOnThisThreadIfNotStarted=true, bool bIsLatencySensitive=false)
Definition AsyncWork.h:429
bool Reschedule(FQueuedThreadPool *InQueuedPool=GThreadPool, EQueuedWorkPriority InQueuedWorkPriority=EQueuedWorkPriority::Normal)
Definition AsyncWork.h:461
bool WaitCompletionWithTimeout(float TimeLimitSeconds)
Definition AsyncWork.h:508
EQueuedWorkPriority GetPriority() const
Definition AsyncWork.h:576
virtual void DoTaskWork()=0
void StartSynchronousTask(EQueuedWorkPriority InQueuedWorkPriority=EQueuedWorkPriority::Normal, EQueuedWorkFlags InQueuedWorkFlags=EQueuedWorkFlags::None, int64 InRequiredMemory=-1, const TCHAR *InDebugName=nullptr)
Definition AsyncWork.h:411
void StartBackgroundTask(FQueuedThreadPool *InQueuedPool=GThreadPool, EQueuedWorkPriority InQueuedWorkPriority=EQueuedWorkPriority::Normal, EQueuedWorkFlags InQueuedWorkFlags=EQueuedWorkFlags::None, int64 InRequiredMemory=-1, const TCHAR *InDebugName=nullptr)
Definition AsyncWork.h:419
bool IsDone()
Definition AsyncWork.h:540
bool Cancel()
Definition AsyncWork.h:482
virtual ~FAsyncTaskBase()
Definition AsyncWork.h:387
virtual void MarkAsCanceled()
Definition AsyncWork.h:381
virtual bool TryAbandonTask()=0
void CheckIdle() const
Definition AsyncWork.h:363
Definition AsyncWork.h:585
bool TryAbandonTask() final
Definition AsyncWork.h:642
FAsyncTask(Arg0Type &&Arg0, ArgTypes &&... Args)
Definition AsyncWork.h:617
const TTask & GetTask() const
Definition AsyncWork.h:636
void DoTaskWork() final
Definition AsyncWork.h:653
FAsyncTask()
Definition AsyncWork.h:608
TTask & GetTask()
Definition AsyncWork.h:627
Definition AsyncWork.h:63
FAutoDeleteAsyncTask(T &&... Args)
Definition AsyncWork.h:130
void StartBackgroundTask(FQueuedThreadPool *InQueuedPool=GThreadPool, EQueuedWorkPriority InPriority=EQueuedWorkPriority::Normal)
Definition AsyncWork.h:145
void StartSynchronousTask()
Definition AsyncWork.h:137
Definition Event.h:21
virtual bool Wait(uint32 WaitTime, const bool bIgnoreThreadIdleStats=false)=0
virtual void Trigger()=0
virtual void Reset()=0
Definition AsyncWork.h:663
void Abandon()
Definition AsyncWork.h:669
bool CanAbandon()
Definition AsyncWork.h:665
Definition QueuedThreadPool.h:105
virtual bool RetractQueuedWork(IQueuedWork *InQueuedWork)=0
virtual void AddQueuedWork(IQueuedWork *InQueuedWork, EQueuedWorkPriority InQueuedWorkPriority=EQueuedWorkPriority::Normal)=0
Definition LightweightStats.h:424
Definition ThreadSafeCounter.h:14
int32 Increment()
Definition ThreadSafeCounter.h:52
int32 Decrement()
Definition ThreadSafeCounter.h:75
int32 GetValue() const
Definition ThreadSafeCounter.h:120
Definition IQueuedWork.h:62
Definition InheritedContext.h:118
CORE_API FInheritedContextScope RestoreInheritedContext()
Definition InheritedContext.cpp:8
void CaptureInheritedContext()
Definition InheritedContext.h:121
Definition InheritedContext.h:54
@ Warning
Definition LogVerbosity.h:34
static UE_FORCEINLINE_HINT void MemoryBarrier()
Definition AndroidPlatformMisc.h:249
static CORE_API void DumpStackTraceToLog(const ELogVerbosity::Type LogVerbosity)
Definition AssertionMacros.cpp:817
static CORE_API class FEvent * GetSynchEventFromPool(bool bIsManualReset=false)
Definition GenericPlatformProcess.cpp:576
static CORE_API void ReturnSynchEventToPool(FEvent *Event)
Definition GenericPlatformProcess.cpp:589
Definition LightweightStats.h:416