Files
libsst/Include/ZUtil/ZTaskStream.hpp
2026-04-03 00:22:39 -05:00

405 lines
11 KiB
C++

/*
ZTaskStream.hpp
Author: James Russell <jcrussell@762studios.com>
Purpose: Manages the execution of 'Tasks' by a number of threads determined at runtime. Attempts to parallelize
execution as well as possible with the provided information.
Changelog
2011/04/10 - creation (jcrussell)
*/
#ifndef _ZTASKSTREAM_H
#define _ZTASKSTREAM_H
#include <ZUtil/ZThread.hpp>
#include <stdlib.h> //size_t
//Forward Declarations
class ZTaskStream;
typedef size_t ZTaskPriority; //Task Priority
typedef size_t ZTaskThreadId; //Task Thread Id
//Definition for immediate task execution priority
#define ZTASK_PRIORITY_IMMEDIATE (0)
//Default allocator buffer size for the task thread
#define ZTT_DEFAULT_BUFFER_SIZE 256
//Default allocator buffer size for the task stream
#define ZTS_DEFAULT_BUFFER_SIZE 1024
/*
ZTask execute return values.
*/
enum ZTaskReturnStatus
{
ZTRS_SUCCESS, // Task has succeeded
ZTRS_FAILURE, // Task has failed
ZTRS_ENUM_SIZE
};
/*
ZTask thread Status enumeration.
*/
enum ZTaskThreadStatus
{
ZTTS_UNINITIALIZED, // Task thread is uninitialized
ZTTS_WAITING, // Task thread is awaiting tasks
ZTTS_EXECUTING, // Task thread is executing tasks
ZTTS_ENUM_SIZE
};
/*
Tasks are the basic unit of execution for a ZTaskStream. They are added to the ZTaskStream and
assigned to a thread for execution. They are then executed as TaskThreads
become available.
*/
class ZTask
{
friend class ZTaskThread;
friend class ZTaskStream;
public:
ZTaskStream* TaskStream; // The task stream that is executing this task
ZTaskPriority Priority; // Priority, assigned at construction, decreased each time it is delayed to zero, which is immediate priority
bool bCancelTask; // Cancel Boolean, if set to true, will prevent the task from being executed
/*
Parameterized c'tor (with default arguments).
@param _priority - The amount of time (in ms) the task can be delayed is priority. This means the closer to zero
the priority is set, the faster it will be executed.
*/
ZTask(ZTaskPriority _priority = ZTASK_PRIORITY_IMMEDIATE)
: TaskStream(NULL), Priority(_priority), bCancelTask(false), Func(NULL), TaskArgument(NULL) { }
/*
Parameterized c'tor.
@param _func - function to execute
@param _taskArg - argument to the task
@param _priority - The amount of time (in ms) the task can be delayed is priority. This means the closer to zero
the priority is set, the faster it will be executed.
*/
ZTask(ZTaskReturnStatus (*_func)(ZTaskStream*, ZTask*, void*), void *_taskArg, ZTaskPriority _priority = ZTASK_PRIORITY_IMMEDIATE)
: TaskStream(NULL), Priority(_priority), bCancelTask(false), Func(_func),TaskArgument(_taskArg) { }
/*
Destructor.
*/
virtual ~ZTask() { }
/*
virtual public ZTask::Execute
Execute method. Defined by the task implementation.
@param _arg - argument to the task (NULL by default)
@return (ZTaskReturnStatus) - task return value
*/
virtual ZTaskReturnStatus Execute(void *_arg)
{ URFP(_arg); if (Func != NULL) return Func(TaskStream, this, TaskArgument); else return ZTRS_FAILURE; }
/*
virtual public ZTask::OnTaskFailed
Handler function for task failure. No-op by default.
@return (void)
*/
virtual void OnTaskFailed(void *_taskArgument)
{ URFP(_taskArgument); }
protected:
ZTaskReturnStatus (*Func)(ZTaskStream*, ZTask*, void*); // function for the ZTask to execute if not implemented as a subtask
void* TaskArgument; // Argument passed to this task
};
/*
Future. Used to asynchronously execute a function that will return a value when completed.
*/
template <typename R, typename A>
class ZFuture
{
friend class ZTaskStream;
protected:
// Future Task
class ZFutureTask : public ZTask
{
public:
R (*Func)(A); // The Function
A Arg; // The argument
R Ret; // The return value
bool Completed; // Completed flag
ZEvent CompletedEvent; // Completed Event
// c'tor
ZFutureTask(R (*_func)(A), A _arg)
: ZTask(), Func(_func), Arg(_arg), Ret(), Completed(false), CompletedEvent() { }
// d'tor
~ZFutureTask() { }
// subclass override
virtual ZTaskReturnStatus Execute(void *_arg)
{
URFP(_arg);
CompletedEvent.Reset();
Ret = Func(Arg);
Completed = true;
CompletedEvent.Notify();
return ZTRS_SUCCESS;
}
};
ZPtr<ZFutureTask> FutureTask; // The function execute task
public:
/*
Parameterized c'tor.
@param _func - the function to execute, must return a value of type R
@param _arg - argument to the function
*/
ZFuture(R (_func)(A), A _arg) : FutureTask(znew ZFutureTask(_func, _arg)) { }
/*
public ZFuture<R, A>::IsComplete
Returns whether or not the future has completed.
@return (bool) - true if the task has completed
*/
bool IsComplete() { return FutureTask->Completed; }
/*
public ZFuture<R, A>::GetValue
Gets the return value from the future.
@return (R) - return from the task
*/
R GetValue() { if (!FutureTask->Completed) FutureTask->CompletedEvent.Wait(); return FutureTask->Ret; }
/*
public ZFuture<R, A>::GetValueRef
Gets the return value from the future as a reference.
@return (R&) - reference to return from the task
*/
R& GetValueRef() { if (!FutureTask->Completed) FutureTask->CompletedEvent.Wait(); return FutureTask->Ret; }
};
/*
Threads which execute tasks in the ZTaskStream. The number of operating Task Threads is
assigned at construction of a ZTaskStream and can be changed.
*/
class ZTaskThread : public ZThread
{
friend class ZTaskStream;
public:
/*
Parameterized c'tor.
@param _stream - the stream giving us tasks
@param _id - the id of this task
*/
ZTaskThread(ZTaskStream *_stream, ZTaskThreadId _id);
// d'tor
virtual ~ZTaskThread();
/*
public ZTaskThread::PushTask()
Adds a task to the task thread.
@param _task - the task to add to this task thread (argument already set)
@return (void)
*/
void PushTask(ZPtr<ZTask> _task);
protected:
// Subclass implementation of thread run, which executes tasks
ZThreadReturn run(uint64_t _dt);
private:
ZMutex TaskLock; // Concurrency lock
ZSemaphore TaskSema; // Event that is set when new tasks are available
ZTaskStream* Stream; // The task stream providing us with tasks
ZTaskThreadId Id; // Task thread ID, used to identify this task thread
ZTaskThreadStatus ThrStatus; // Status of this task thread
ZTaskPriority CurrentTaskPriority; //The priority of the currently executing task
ZArray< ZPtr<ZTask> > Tasks; //Set of tasks to execute
};
/*
The task stream. Assigns tasks to task threads that execute when desired. Can also
handle 'background' tasks.
*/
class ZTaskStream
{
public:
/*
Default c'tor.
Creates a single task thread. The amount of task threads running concurrently can be changed
using SetTaskThreadCount.
*/
ZTaskStream();
// d'tor
~ZTaskStream();
/*
public ZTaskStream::ExecuteTasks
Begins execution on the currently loaded set of tasks and any further tasks generated
during the execution of those tasks. Returns after all tasks that will be executed this
frame are done executing.
Tasks that were not completed have had their priority values reduced, meaning that if
they are again added to the ZTaskStream for execution repeatedly until executed, they
are guaranteed to eventually execute within their priority time frame.
@param _frameTime - (given in ms) is the desired amount of time that execution of all tasks
should take; if any time is remaining after execution of ALL priority zero
tasks, a number of extra priority > 0 tasks are executed as can be fit into
the extra time. Because of this definition, frameTime is an estimate and
not a guarantee - execution of priority 0 tasks can take more than frameTime.
@return (void)
*/
void ExecuteTasks(int _frameTime);
/*
public ZTaskStream::GetTaskThreadCount
Gets the current number of worker threads.
@return (size_t) - number of task stream worker threads
*/
size_t GetTaskThreadCount();
/*
public ZTaskStream::HaltFrame
Notifies the ZTaskStream to stop assigning tasks tasks and return from the ExecuteTasks
call that started the process. This does not stop the execution of tasks that have already
been assigned.
@return (void)
*/
void HaltFrame();
/*
public ZTaskStream::Pause
Notifies the ZTaskStream to pause task execution for all tasks.
@param _pause - true to pause the stream, false to unpause
@return (void)
*/
void PauseFrame(bool _pause);
/*
public ZTaskStream::PushFuture
Adds a ZFuture to the task stream, which will execute the future at the soonest available
opportunity.
The ZSmartPointer version will hold a reference to the future until after execution. If
the raw pointer version is used, the user is required to keep the pointer valid until after
execution.
@param _future - the future to execute
@return (void)
*/
template <typename R, typename A>
void PushFuture(ZPtr< ZFuture<R, A> > _future) {
TaskThreads[AssignTask(_future->FutureTask.Pointer())]->PushTask(_future->FutureTask);
}
template <typename R, typename A>
void PushFuture(ZFuture<R, A>* _future) {
// TODO
}
/*
public ZTaskStream::PushTask
Adds a task to the task list. The task is added to the task queue in the currently executing
frame phase, or if a phase is not yet executing, it will be added to the next frame phase to
execute.
The ZSmartPointer version will hold a reference to the task until after execution. If the
raw pointer version is used, the user is required to keep the pointer valid until after
execution.
@param _task - the task to add
@return (void) - the id of the task
*/
void PushTask(ZPtr<ZTask> _task);
void PushTask(ZTask* _task);
/*
public ZTaskStream::PushTasks
Adds a list of tasks. Identical to PushTask() but with less locking overhead. The list of
tasks is not emptied (i.e _tasks.Clear() is not called).
The ZSmartPointer version will hold a reference to the tasks until after execution. If the
raw pointer version is used, the user is required to keep the pointer valid until after
execution.
@param _tasks - the tasks to add
@param _count - the number of tasks
@return (void)
*/
void PushTasks(ZArray< ZPtr<ZTask> >& _tasks);
void PushTasks(ZTask* _tasks, size_t _count);
/*
public ZTaskStream::SetTaskThreadCount
Sets the number of task threads that will be running. Should be greater than zero, or the
call has no effect.
@param _count - the number of task threads to run simultaneously
@return (void)
*/
void SetTaskThreadCount(size_t _count);
protected:
ZMutex TaskStreamLock; // Lock for controlling access to the ZTaskStream objects
ZSemaphore TaskStreamSema; // Semaphore for signaling events
ZEvent FrameCompletedEvent; // Event for notifying the execute caller thread that a frame is completed
ZEvent PausedEvent; // Event for pausing the Task Stream
ZArray<ZTaskThread*> TaskThreads; // Task threads that run the set of tasks
ZArray< ZPtr<ZTask> > Tasks; // The task list
// Pause flag, which indicates that task execution should be paused
// This is only used for debugging purposes, 'PausedEvent' has the same state
// wrapped into an OS object.
bool bPaused;
//Halt flag, which indicates that task execution should halt
bool bShouldHalt;
//Assigns a task to one of the task threads
ZTaskThreadId AssignTask( ZTask *_task);
//Kills a given task thread
void KillTaskThread(size_t _idx);
};
#endif