You're breathtaking!
This commit is contained in:
357
Source/Engine/Threading/Task.h
Normal file
357
Source/Engine/Threading/Task.h
Normal file
@@ -0,0 +1,357 @@
|
||||
// Copyright (c) 2012-2020 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 RemovableObject, 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;
|
||||
|
||||
/// <summary>
|
||||
/// The current task state.
|
||||
/// </summary>
|
||||
volatile TaskState _state;
|
||||
|
||||
/// <summary>
|
||||
/// The task to start after finish.
|
||||
/// </summary>
|
||||
Task* _continueWith;
|
||||
|
||||
protected:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Task"/> class.
|
||||
/// </summary>
|
||||
Task()
|
||||
: _cancelFlag(0)
|
||||
, _state(TaskState::Created)
|
||||
, _continueWith(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets work state
|
||||
/// </summary>
|
||||
/// <returns>State</returns>
|
||||
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>
|
||||
/// <returns>The next task.</returns>
|
||||
FORCE_INLINE Task* GetContinueWithTask() const
|
||||
{
|
||||
return _continueWith;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation failed
|
||||
/// </summary>
|
||||
/// <returns>True if operation failed, otherwise false</returns>
|
||||
FORCE_INLINE bool IsFailed() const
|
||||
{
|
||||
return GetState() == TaskState::Failed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation has been canceled
|
||||
/// </summary>
|
||||
/// <returns>True if operation has been canceled, otherwise false</returns>
|
||||
FORCE_INLINE bool IsCanceled() const
|
||||
{
|
||||
return GetState() == TaskState::Canceled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation has been queued
|
||||
/// </summary>
|
||||
/// <returns>True if operation has been queued, otherwise false</returns>
|
||||
FORCE_INLINE bool IsQueued() const
|
||||
{
|
||||
return GetState() == TaskState::Queued;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation is running
|
||||
/// </summary>
|
||||
/// <returns>True if operation is running, otherwise false</returns>
|
||||
FORCE_INLINE bool IsRunning() const
|
||||
{
|
||||
return GetState() == TaskState::Running;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation has been finished
|
||||
/// </summary>
|
||||
/// <returns>True if operation has been finished, otherwise false</returns>
|
||||
FORCE_INLINE bool IsFinished() const
|
||||
{
|
||||
return GetState() == TaskState::Finished;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if operation has been ended (via cancel, fail or finish).
|
||||
/// </summary>
|
||||
/// <returns>True if operation has been ended, otherwise false</returns>
|
||||
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>
|
||||
/// <returns>True if task has been canceled and should stop it's work without calling child task start.</returns>
|
||||
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>
|
||||
/// Creates a task that completes after a specified time interval (not started).
|
||||
/// </summary>
|
||||
/// <param name="delay">The time span to wait before completing the returned task.</param>
|
||||
/// <returns>A task that represents the time delay (not started).</returns>
|
||||
FORCE_INLINE static Task* Delay(const TimeSpan& delay)
|
||||
{
|
||||
return Delay(static_cast<int32>(delay.GetTotalMilliseconds()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a task that completes after a specified time interval (not started).
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">The amount of milliseconds to wait before completing the returned task.</param>
|
||||
/// <returns>A task that represents the time delay (not started).</returns>
|
||||
static Task* Delay(int32 milliseconds);
|
||||
|
||||
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="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();
|
||||
};
|
||||
Reference in New Issue
Block a user