feat: add JobSystem
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#ifndef DESTRUM_JOBSYSTEM_H
|
||||
#define DESTRUM_JOBSYSTEM_H
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Singleton.h>
|
||||
|
||||
enum class JobPriority {
|
||||
High,
|
||||
Low,
|
||||
};
|
||||
|
||||
class JobCounter {
|
||||
public:
|
||||
JobCounter() = default;
|
||||
JobCounter(const JobCounter&) = delete;
|
||||
JobCounter& operator=(const JobCounter&) = delete;
|
||||
|
||||
[[nodiscard]] bool IsDone() const { return m_Pending.load(std::memory_order_acquire) == 0; }
|
||||
|
||||
private:
|
||||
friend class JobSystem;
|
||||
std::atomic<int> m_Pending{0};
|
||||
};
|
||||
|
||||
struct JobDispatchArgs {
|
||||
std::uint32_t index{0};
|
||||
std::uint32_t groupIndex{0};
|
||||
};
|
||||
|
||||
class JobSystem final : public Singleton<JobSystem> {
|
||||
public:
|
||||
friend class Singleton<JobSystem>;
|
||||
using Job = std::function<void()>;
|
||||
|
||||
~JobSystem() override;
|
||||
|
||||
// 0 workers picks hardware_concurrency - 1
|
||||
void Init(std::uint32_t workerCount = 0);
|
||||
void Shutdown();
|
||||
|
||||
[[nodiscard]] bool IsRunning() const { return !m_Workers.empty(); }
|
||||
[[nodiscard]] std::uint32_t GetWorkerCount() const { return static_cast<std::uint32_t>(m_Workers.size()); }
|
||||
|
||||
void Execute(JobCounter& counter, Job job, JobPriority priority = JobPriority::High);
|
||||
void Dispatch(JobCounter& counter, std::uint32_t count, std::uint32_t groupSize,
|
||||
std::function<void(JobDispatchArgs)> job);
|
||||
void Wait(JobCounter& counter);
|
||||
|
||||
template <typename F>
|
||||
auto Async(F&& fn) -> std::future<std::invoke_result_t<std::decay_t<F>>>
|
||||
{
|
||||
using Result = std::invoke_result_t<std::decay_t<F>>;
|
||||
auto task = std::make_shared<std::packaged_task<Result()>>(std::forward<F>(fn));
|
||||
auto future = task->get_future();
|
||||
Submit([task] { (*task)(); }, JobPriority::Low);
|
||||
return future;
|
||||
}
|
||||
|
||||
void RunOnMainThread(Job job);
|
||||
void DrainMainThreadQueue();
|
||||
|
||||
private:
|
||||
JobSystem() = default;
|
||||
|
||||
void Submit(Job job, JobPriority priority);
|
||||
bool TryPopLocked(Job& job, bool& isLow);
|
||||
[[nodiscard]] bool CanTakeLowLocked() const;
|
||||
bool TryRunHighJob();
|
||||
void WorkerLoop(std::uint32_t index);
|
||||
|
||||
std::vector<std::thread> m_Workers;
|
||||
std::mutex m_Mutex;
|
||||
std::condition_variable m_WorkAvailable;
|
||||
std::deque<Job> m_HighQueue;
|
||||
std::deque<Job> m_LowQueue;
|
||||
std::uint32_t m_ActiveLowJobs{0};
|
||||
std::uint32_t m_MaxActiveLowJobs{1};
|
||||
bool m_Stopping{false};
|
||||
|
||||
std::mutex m_MainThreadMutex;
|
||||
std::vector<Job> m_MainThreadQueue;
|
||||
};
|
||||
|
||||
#endif // DESTRUM_JOBSYSTEM_H
|
||||
Reference in New Issue
Block a user