ezEngine  Milestone 7
ezTaskSystem Class Reference

This system allows to automatically distribute tasks onto a number of worker threads. More...

#include <TaskSystem.h>

Classes

struct  TaskData
 

Static Public Member Functions

static void SetWorkThreadCount (ezInt8 iShortTasks=-1, ezInt8 iLongTasks=-1)
 Sets the number of threads to use for the different task categories. More...
 
static ezUInt32 GetWorkerThreadCount (ezWorkerThreadType::Enum Type)
 Returns the number of threads that are allocated to work on the given type of task.
 
static ezTaskGroupID StartSingleTask (ezTask *pTask, ezTaskPriority::Enum Priority)
 A helper function to insert a single task into the system and start it right away. Returns ID of the Group into which the task has been put.
 
static ezTaskGroupID StartSingleTask (ezTask *pTask, ezTaskPriority::Enum Priority, ezTaskGroupID Dependency)
 A helper function to insert a single task into the system and start it right away. Returns ID of the Group into which the task has been put. This overload allows to additionally specify a single dependency.
 
static ezTaskGroupID CreateTaskGroup (ezTaskPriority::Enum Priority, ezTaskGroup::OnTaskGroupFinished Callback=ezTaskGroup::OnTaskGroupFinished())
 Creates a new task group for one-time use. Groups need to be recreated every time a task is supposed to be inserted into the system. More...
 
static void AddTaskToGroup (ezTaskGroupID Group, ezTask *pTask)
 Adds a task to the given task group. The group must not yet have been started.
 
static void AddTaskGroupDependency (ezTaskGroupID Group, ezTaskGroupID DependsOn)
 Adds a dependency on another group to Group. This means Group will not be execute before DependsOn has finished. More...
 
static void StartTaskGroup (ezTaskGroupID Group)
 Starts the task group. After this no further modifications on the group (new tasks or dependencies) are allowed.
 
static bool IsTaskGroupFinished (ezTaskGroupID Group)
 Returns whether the given Group id refers to a task group that has been finished already. More...
 
static void SetTargetFrameTime (double fSmoothFrameMS=1000.0/40.0)
 Sets the target frame time that is supposed to not be exceeded. More...
 
static void FinishFrameTasks ()
 Call this function once at the end of a frame. It will ensure that all tasks for 'this frame' get finished properly. More...
 
static void WaitForTask (ezTask *pTask)
 This function will block until the given task has finished. More...
 
static void WaitForGroup (ezTaskGroupID Group)
 Blocks until all tasks in the given group have finished. Similar to 'WaitForTask'.
 
static ezResult CancelTask (ezTask *pTask, ezOnTaskRunning::Enum OnTaskRunning=ezOnTaskRunning::WaitTillFinished)
 This function will try to remove the given task from the work queue, to prevent it from being executed. More...
 
static ezResult CancelGroup (ezTaskGroupID Group, ezOnTaskRunning::Enum OnTaskRunning=ezOnTaskRunning::WaitTillFinished)
 Cancels all the tasks in the given group. More...
 
static bool IsLoadingThread ()
 Returns true when the thread that this function is executed on is the file loading thread.
 
static double GetThreadUtilization (ezWorkerThreadType::Enum Type, ezUInt32 iThread, ezUInt32 *pNumTasksExecuted=nullptr)
 Returns the utilization (0.0 to 1.0) of the given thread. Note: This will only be valid, if FinishFrameTasks() is called once per frame. More...
 

Private Member Functions

 EZ_MAKE_SUBSYSTEM_STARTUP_FRIEND (Foundation, TaskSystem)
 

Static Private Member Functions

static void Startup ()
 
static void Shutdown ()
 
static void DebugCheckTaskGroup (ezTaskGroupID Group)
 
static void ScheduleGroupTasks (ezTaskGroup *pGroup)
 
static void TaskHasFinished (ezTask *pTask, ezTaskGroup *pGroup)
 
static void DependencyHasFinished (ezTaskGroup *pGroup)
 
static void StopWorkerThreads ()
 
static TaskData GetNextTask (ezTaskPriority::Enum FirstPriority, ezTaskPriority::Enum LastPriority, ezTask *pPrioritizeThis=nullptr)
 
static bool ExecuteTask (ezTaskPriority::Enum FirstPriority, ezTaskPriority::Enum LastPriority, ezTask *pPrioritizeThis=nullptr)
 
static void FinishMainThreadTasks ()
 
static void ReprioritizeFrameTasks ()
 
static void ExecuteSomeFrameTasks (ezUInt32 uiSomeFrameTasks, double fSmoothFrameMS)
 

Static Private Attributes

static ezDynamicArray
< ezTaskWorkerThread * > 
s_WorkerThreads [ezWorkerThreadType::ENUM_COUNT]
 
static ezMutex s_TaskSystemMutex
 
static ezDeque< ezTaskGroups_TaskGroups
 
static ezList< TaskDatas_Tasks [ezTaskPriority::ENUM_COUNT]
 
static ezThreadSignal s_TasksAvailableSignal [ezWorkerThreadType::ENUM_COUNT]
 
static double s_fSmoothFrameMS = 1000.0 / 40.0
 
static ezProfilingId s_ProfileWaitForTask
 
static ezProfilingId s_ProfileWaitForGroup
 
static ezProfilingId s_ProfileCancelTask
 
static ezProfilingId s_ProfileCancelGroup
 
static ezProfilingId s_ProfileMainThreadTasks
 
static ezProfilingId s_ProfileSomeFrameTasks
 

Friends

class ezTaskWorkerThread
 

Detailed Description

This system allows to automatically distribute tasks onto a number of worker threads.

By deriving from ezTask you can create your own task types. These can be executed through this task system. You can run a single task using the 'StartSingleTask' function. For more complex setups, it is possible to create groups of tasks, which can have interdependencies. This should be used to group all tasks that belong to one system and need to be done before another system runs. For example you could group all tasks to update particle systems, and then have another group for all tasks to update sound, which depends on the first group, such that sound is only updated after all particle systems are done with.

Although it is possible to wait for tasks or to cancel them, it is generally advised to try to minimize their use. Tasks that might need to be canceled regularly (e.g. path searches) should be implemented in a way that they are aware of being canceled and will stop their work prematurely, instead of running through to the end.

Note that it is crucial to call 'FinishFrameTasks' once per frame, otherwise tasks that need to be executed on the main thread are never executed.


Class Documentation

struct ezTaskSystem::TaskData
Class Members
ezTaskGroup * m_pBelongsToGroup
ezTask * m_pTask

Member Function Documentation

void ezTaskSystem::AddTaskGroupDependency ( ezTaskGroupID  Group,
ezTaskGroupID  DependsOn 
)
static

Adds a dependency on another group to Group. This means Group will not be execute before DependsOn has finished.

Note
Be careful with dependencies and task priorities. A task that has to execute 'this frame' should never depend on a task that needs only finish 'next frame', this might introduce very long and unnecessary waits. A task that has priority 'this frame' or 'next frame' will actually not be executed in 'this frame' or 'next frame' until all its dependencies are fulfilled. So you might add a long running task and a short task which depends on it, but the system will not block at the end of the frame, to wait for the long running task (to finish the short task thereafter), as that short task won't get scheduled for execution, at all, until all its dependencies are actually finished.
ezResult ezTaskSystem::CancelGroup ( ezTaskGroupID  Group,
ezOnTaskRunning::Enum  OnTaskRunning = ezOnTaskRunning::WaitTillFinished 
)
static

Cancels all the tasks in the given group.

EZ_SUCCESS is returned, if all tasks were already finished or could be removed without waiting for any of them. EZ_FAILURE is returned, if at least one task was being processed by another thread and could not be removed without waiting. If bWaitForIt is false, the function cancels all tasks, but returns without blocking, even if not all tasks have been finished. If bWaitForIt is true, the function returns only after it is guaranteed that all tasks are properly terminated.

ezResult ezTaskSystem::CancelTask ( ezTask pTask,
ezOnTaskRunning::Enum  OnTaskRunning = ezOnTaskRunning::WaitTillFinished 
)
static

This function will try to remove the given task from the work queue, to prevent it from being executed.

The function will return EZ_SUCCESS, if the task could be removed and thus its execution could be prevented. It will also return EZ_SUCCESS, if the task was already finished and nothing needed to be done. Tasks that are removed without execution will still be marked as 'finished' and dependent tasks will be scheduled.

EZ_FAILURE is returned, if the task had already been started and thus could not be prevented from running.

In case of failure, bWaitForIt determines whether 'WaitForTask' is called (with all its consequences), or whether the function will return immediately.

The cancel flag is set on the task, such that tasks that support canceling might terminate earlier. However, there is no guarantee how long it takes for already running tasks to actually finish. Therefore when bWaitForIt is true, this function might block for a very long time. It is advised to implement tasks that need to be canceled regularly (e.g. path searches for units that might die) in a way that allows for quick canceling.

ezTaskGroupID ezTaskSystem::CreateTaskGroup ( ezTaskPriority::Enum  Priority,
ezTaskGroup::OnTaskGroupFinished  Callback = ezTaskGroup::OnTaskGroupFinished() 
)
static

Creates a new task group for one-time use. Groups need to be recreated every time a task is supposed to be inserted into the system.

All tasks that are added to this group will be run with the same given Priority. Once all tasks in the group are finished and thus the group is finished, an optional Callback can be executed.

void ezTaskSystem::FinishFrameTasks ( )
static

Call this function once at the end of a frame. It will ensure that all tasks for 'this frame' get finished properly.

Calling this function is crucial for several reasons. It is the central function to execute 'main thread' tasks. Otherwise these tasks might never get executed. It also changes the priority of all 'next frame' tasks to 'this frame', so that those tasks are guaranteed to get finished when 'FinishFrameTasks' is called the next time.

Finally this function executes tasks with the priority 'SomeFrameMainThread' as long as the target frame time is not exceeded. You can configure this with SetTargetFrameTime(), which defines how long (in milliseconds) the frame is allowed to be. As long as that time is not exceeded, additional 'SomeFrameMainThread' tasks will be executed. If the frame time spikes for a few frames, no such tasks will be executed, to prevent making it worse. However, if the frame time stays high over a longer period, 'FinishFrameTasks' will execute 'SomeFrameMainThread' tasks every once in a while, to guarantee some progress.

Note
After this function returns all tasks of priority 'ThisFrameMainThread' are finished. All tasks of priority 'EarlyThisFrame' up to 'LateThisFrame' are either finished or currently running on some thread, so they will be finished soon. There is however no guarantee that they are indeed all finished, as that would introduce unnecessary stalls.
static double ezTaskSystem::GetThreadUtilization ( ezWorkerThreadType::Enum  Type,
ezUInt32  iThread,
ezUInt32 *  pNumTasksExecuted = nullptr 
)
inlinestatic

Returns the utilization (0.0 to 1.0) of the given thread. Note: This will only be valid, if FinishFrameTasks() is called once per frame.

Also optionally returns the number of tasks that were finished during the last frame.

bool ezTaskSystem::IsTaskGroupFinished ( ezTaskGroupID  Group)
static

Returns whether the given Group id refers to a task group that has been finished already.

There is no time frame in which group IDs are valid. You may call this function at any time, even 10 minutes later, and it will correctly determine the results.

void ezTaskSystem::SetTargetFrameTime ( double  fSmoothFrameMS = 1000.0 / 40.0)
static

Sets the target frame time that is supposed to not be exceeded.

See Also
FinishFrameTasks() for more details.
void ezTaskSystem::SetWorkThreadCount ( ezInt8  iShortTasks = -1,
ezInt8  iLongTasks = -1 
)
static

Sets the number of threads to use for the different task categories.

uiShortTasks and uiLongTasks must be at least 1 and should not exceed the number of available CPU cores. There will always be exactly one additional thread for file access tasks (ezTaskPriority::FileAccess).

If uiShortTasks or uiLongTasks is smaller than 1, a default number of threads will be used for that type of work. This number of threads depends on the number of available CPU cores. If SetWorkThreadCount is never called, at all, the first time any task is started the number of worker threads is set to this default configuration. Unless you have a good idea how to set up the number of worker threads to make good use of the available cores, it is a good idea to just use the default settings.

void ezTaskSystem::WaitForTask ( ezTask pTask)
static

This function will block until the given task has finished.

This function guarantees that pTask will get executed eventually. Instead of idly waiting for the task to finish, it actively executes tasks. This is especially important when it is run on the main thread, as there might be tasks in the system that can only be executed on the main thread, which are also dependencies for pTask. In such a case these tasks must be executed properly, or otherwise one would wait forever.

Note
With the current implementation there is no guarantee that this function terminates as soon as the given task is finished, since this thread might be busy running some other task. This function is only meant to synchronize code, it is not meant to be efficient and should therefore not be called in regular scenarios.

The documentation for this class was generated from the following files: