Files
FlaxEngine/Source/Engine/Threading/Task.h

331 lines
10 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Object.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/NonCopyable.h"
#include "Engine/Core/Enums.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Platform/Platform.h"
/// <summary>
/// Represents the current stage in the lifecycle of a Task.
/// </summary>
DECLARE_ENUM_EX_6(TaskState, int64, 0, Created, Failed, Canceled, Queued, Running, Finished);
/// <summary>
/// Represents an asynchronous operation.
/// </summary>
class FLAXENGINE_API Task : public Object, public NonCopyable
{
//
// Tasks execution and states flow:
//
// Task() [Created]
// \/
// Start() [Queued]
// \/
// Run() [Running]
// |
// ------------------------
// \/ \/
// Finish() [Finished] Fail/Cancel() [Failed/Canceled]
// \/ \/
// child.Start() child.Cancel()
// | \/
// -------------------------
// \/
// End()
//
protected:
/// <summary>
/// The cancel flag used to indicate that there is request to cancel task operation.
/// </summary>
volatile int64 _cancelFlag = 0;
/// <summary>
/// The current task state.
/// </summary>
volatile TaskState _state = TaskState::Created;
/// <summary>
/// The task to start after finish.
/// </summary>
Task* _continueWith = nullptr;
public:
/// <summary>
/// Gets the task state.
/// </summary>
FORCE_INLINE TaskState GetState() const
{
return static_cast<TaskState>(Platform::AtomicRead((int64 volatile*)&_state));
}
/// <summary>
/// Determines whether the specified object has reference to the given object.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns>True if the specified object has reference to the given object, otherwise false.</returns>
virtual bool HasReference(Object* obj) const
{
return false;
}
/// <summary>
/// Gets the task to start after this one.
/// </summary>
FORCE_INLINE Task* GetContinueWithTask() const
{
return _continueWith;
}
public:
/// <summary>
/// Checks if operation failed.
/// </summary>
FORCE_INLINE bool IsFailed() const
{
return GetState() == TaskState::Failed;
}
/// <summary>
/// Checks if operation has been canceled.
/// </summary>
FORCE_INLINE bool IsCanceled() const
{
return GetState() == TaskState::Canceled;
}
/// <summary>
/// Checks if operation has been queued.
/// </summary>
FORCE_INLINE bool IsQueued() const
{
return GetState() == TaskState::Queued;
}
/// <summary>
/// Checks if operation is running.
/// </summary>
FORCE_INLINE bool IsRunning() const
{
return GetState() == TaskState::Running;
}
/// <summary>
/// Checks if operation has been finished.
/// </summary>
FORCE_INLINE bool IsFinished() const
{
return GetState() == TaskState::Finished;
}
/// <summary>
/// Checks if operation has been ended (via cancel, fail or finish).
/// </summary>
bool IsEnded() const
{
auto state = GetState();
return state == TaskState::Failed || state == TaskState::Canceled || state == TaskState::Finished;
}
/// <summary>
/// Returns true if task has been requested to cancel it's operation.
/// </summary>
FORCE_INLINE bool IsCancelRequested()
{
return Platform::AtomicRead(&_cancelFlag) != 0;
}
public:
/// <summary>
/// Starts this task execution (and will continue with all children).
/// </summary>
void Start();
/// <summary>
/// Cancels this task (and all child tasks).
/// </summary>
void Cancel();
/// <summary>
/// Waits the specified timeout for the task to be finished.
/// </summary>
/// <param name="timeout">The maximum amount of time to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting.</param>
/// <returns>True if task failed or has been canceled or has timeout, otherwise false.</returns>
FORCE_INLINE bool Wait(const TimeSpan& timeout) const
{
return Wait(timeout.GetTotalMilliseconds());
}
/// <summary>
/// Waits the specified timeout for the task to be finished.
/// </summary>
/// <param name="timeoutMilliseconds">The maximum amount of milliseconds to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting.</param>
/// <returns>True if task failed or has been canceled or has timeout, otherwise false.</returns>
bool Wait(double timeoutMilliseconds = -1) const;
/// <summary>
/// Waits for all the tasks from the list.
/// </summary>
/// <param name="tasks">The tasks list to wait for.</param>
/// <param name="timeoutMilliseconds">The maximum amount of milliseconds to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting.</param>
/// <returns>True if any task failed or has been canceled or has timeout, otherwise false.</returns>
template<class T = Task>
static bool WaitAll(Array<T*>& tasks, double timeoutMilliseconds = -1)
{
for (int32 i = 0; i < tasks.Count(); i++)
{
if (tasks[i]->Wait())
return true;
}
return false;
}
public:
/// <summary>
/// Continues that task execution with a given task (will call Start on given task after finishing that one).
/// </summary>
/// <param name="task">The task to Start after current finish (will propagate OnCancel event if need to).</param>
/// <returns>Enqueued task.</returns>
Task* ContinueWith(Task* task);
/// <summary>
/// Continues that task execution with a given action (will spawn new async action).
/// </summary>
/// <param name="action">Action to run.</param>
/// <param name="target">The action target object.</param>
/// <returns>Enqueued task.</returns>
Task* ContinueWith(const Action& action, Object* target = nullptr);
/// <summary>
/// Continues that task execution with a given action (will spawn new async action).
/// </summary>
/// <param name="action">Action to run.</param>
/// <param name="target">The action target object.</param>
/// <returns>Enqueued task.</returns>
Task* ContinueWith(Function<void()> action, Object* target = nullptr);
/// <summary>
/// Continues that task execution with a given action (will spawn new async action).
/// </summary>
/// <param name="action">Action to run.</param>
/// <param name="target">The action target object.</param>
/// <returns>Enqueued task.</returns>
Task* ContinueWith(Function<bool()> action, Object* target = nullptr);
public:
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="task">The task.</param>
/// <returns>Task</returns>
static Task* StartNew(Task* task);
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="target">The action target object.</param>
/// <returns>Task</returns>
static Task* StartNew(Function<void()>& action, Object* target = nullptr);
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="target">The action target object.</param>
/// <returns>Task</returns>
static Task* StartNew(Function<void()>::Signature action, Object* target = nullptr);
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="callee">The callee object.</param>
/// <returns>Task</returns>
template<class T, void(T::* Method)()>
static Task* StartNew(T* callee)
{
Function<void()> action;
action.Bind<T, Method>(callee);
return StartNew(action, dynamic_cast<Object*>(callee));
}
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="target">The action target object.</param>
/// <returns>Task</returns>
static Task* StartNew(Function<bool()>& action, Object* target = nullptr);
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="target">The action target object.</param>
/// <returns>Task</returns>
static Task* StartNew(Function<bool()>::Signature& action, Object* target = nullptr);
/// <summary>
/// Starts the new task.
/// </summary>
/// <param name="callee">The callee object.</param>
/// <returns>Task</returns>
template<class T, bool(T::*Method)()>
static Task* StartNew(T* callee)
{
Function<bool()> action;
action.Bind<T, Method>(callee);
return StartNew(action, dynamic_cast<Object*>(callee));
}
/// <summary>
/// Cancels all the tasks from the list and clears it.
/// </summary>
template<class T = Task>
static void CancelAll(Array<T*>& tasks)
{
for (int32 i = 0; i < tasks.Count(); i++)
{
tasks[i]->Cancel();
}
tasks.Clear();
}
protected:
/// <summary>
/// Executes this task.
/// It should be called by the task consumer (thread pool or other executor of this task type).
/// It calls run() and handles result).
/// </summary>
void Execute();
/// <summary>
/// Runs the task specified operations
/// Does not handles any task related logic, only performs the actual job.
/// </summary>
/// <returns>The task execution result. Returns true if failed, otherwise false.</returns>
virtual bool Run() = 0;
protected:
virtual void Enqueue() = 0;
virtual void OnStart();
virtual void OnFinish();
virtual void OnFail();
virtual void OnCancel();
virtual void OnEnd();
};