2021-10-12 14:20:30 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <thread>
|
2021-11-18 12:48:21 +01:00
|
|
|
#include <coroutine>
|
2021-11-05 00:01:12 +01:00
|
|
|
#include "MinimalEngine.h"
|
2021-10-23 00:22:35 +02:00
|
|
|
#include "Containers/List.h"
|
2021-12-09 12:38:02 +01:00
|
|
|
#include "Concepts.h"
|
2021-10-12 14:20:30 +02:00
|
|
|
|
|
|
|
|
namespace Seele
|
|
|
|
|
{
|
2021-11-01 20:25:16 +01:00
|
|
|
extern class ThreadPool& getGlobalThreadPool();
|
|
|
|
|
template<bool MainJob>
|
2021-12-15 00:05:42 +01:00
|
|
|
struct JobPromiseBase;
|
2021-11-01 20:25:16 +01:00
|
|
|
struct Event
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Event();
|
2021-12-02 13:00:03 +01:00
|
|
|
Event(nullptr_t);
|
2021-11-01 20:25:16 +01:00
|
|
|
Event(const std::string& name);
|
2021-11-11 20:12:50 +01:00
|
|
|
auto operator<=>(const Event& other) const
|
|
|
|
|
{
|
2021-11-13 19:28:18 +01:00
|
|
|
return flag <=> other.flag;
|
2021-11-11 20:12:50 +01:00
|
|
|
}
|
2021-12-15 00:05:42 +01:00
|
|
|
bool operator==(const Event& other) const
|
|
|
|
|
{
|
|
|
|
|
return flag == other.flag;
|
|
|
|
|
}
|
2021-11-11 20:12:50 +01:00
|
|
|
Event operator co_await()
|
|
|
|
|
{
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2021-11-14 20:36:53 +01:00
|
|
|
operator bool()
|
|
|
|
|
{
|
|
|
|
|
return flag->load();
|
|
|
|
|
}
|
2021-11-11 20:12:50 +01:00
|
|
|
|
2021-11-01 20:25:16 +01:00
|
|
|
void raise();
|
|
|
|
|
void reset();
|
|
|
|
|
bool await_ready();
|
|
|
|
|
template<bool MainJob>
|
|
|
|
|
constexpr void await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h);
|
|
|
|
|
constexpr void await_resume() {}
|
|
|
|
|
private:
|
|
|
|
|
std::string name;
|
|
|
|
|
RefPtr<std::atomic_bool> flag;
|
2021-11-11 20:12:50 +01:00
|
|
|
friend class ThreadPool;
|
2021-10-12 14:20:30 +02:00
|
|
|
};
|
|
|
|
|
|
2021-12-15 00:05:42 +01:00
|
|
|
static std::atomic_uint64_t globalCounter;
|
|
|
|
|
template<bool MainJob>
|
|
|
|
|
struct JobBase;
|
|
|
|
|
template<bool MainJob>
|
|
|
|
|
struct JobPromiseBase
|
2021-11-24 12:10:23 +01:00
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
JobPromiseBase()
|
2021-12-09 12:38:02 +01:00
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
handle = std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this);
|
|
|
|
|
id = globalCounter++;
|
|
|
|
|
finishedEvent = Event(std::format("Job {}", id));
|
2021-12-09 12:38:02 +01:00
|
|
|
}
|
2021-12-15 00:05:42 +01:00
|
|
|
JobBase<MainJob> get_return_object() noexcept;
|
|
|
|
|
|
|
|
|
|
inline auto initial_suspend() noexcept;
|
|
|
|
|
inline auto final_suspend() const noexcept;
|
|
|
|
|
|
|
|
|
|
void return_void() noexcept {}
|
|
|
|
|
void unhandled_exception() noexcept {
|
|
|
|
|
std::cerr << "Unhandled exception" << std::endl;
|
|
|
|
|
exit(1);
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-24 12:10:23 +01:00
|
|
|
void resume()
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
std::unique_lock lock(promiseLock);
|
|
|
|
|
if(handle && !handle.done())
|
|
|
|
|
{
|
|
|
|
|
handle.resume();
|
2021-12-22 11:42:07 +01:00
|
|
|
schedulable = true;
|
2021-12-15 00:05:42 +01:00
|
|
|
}
|
2021-11-24 12:10:23 +01:00
|
|
|
}
|
2021-12-15 00:05:42 +01:00
|
|
|
void setContinuation(JobPromiseBase* cont)
|
2021-12-09 12:38:02 +01:00
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
std::unique_lock lock(promiseLock);
|
|
|
|
|
std::unique_lock lock2(cont->promiseLock);
|
|
|
|
|
continuation = cont->handle;
|
|
|
|
|
cont->precondition = finishedEvent;
|
2021-12-02 13:00:03 +01:00
|
|
|
}
|
|
|
|
|
bool done()
|
|
|
|
|
{
|
|
|
|
|
return handle.done();
|
|
|
|
|
}
|
|
|
|
|
void raise()
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
std::unique_lock lock(promiseLock);
|
|
|
|
|
finishedEvent.raise();
|
2021-12-02 13:00:03 +01:00
|
|
|
}
|
|
|
|
|
void reset()
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
std::unique_lock lock(promiseLock);
|
|
|
|
|
finishedEvent.reset();
|
|
|
|
|
}
|
|
|
|
|
void enqueue(Event& event)
|
|
|
|
|
{
|
2021-12-22 11:42:07 +01:00
|
|
|
if(!handle || handle.done() || !schedulable)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-12-15 00:05:42 +01:00
|
|
|
getGlobalThreadPool().enqueueWaiting(event, this);
|
2021-12-22 11:42:07 +01:00
|
|
|
schedulable = false;
|
2021-12-15 00:05:42 +01:00
|
|
|
}
|
|
|
|
|
void schedule()
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(promiseLock);
|
2021-12-22 11:42:07 +01:00
|
|
|
if(!handle || handle.done() || !schedulable)
|
2021-12-15 00:05:42 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
getGlobalThreadPool().enqueueWaiting(precondition, this);
|
2021-12-22 11:42:07 +01:00
|
|
|
schedulable = false;
|
2021-12-15 00:05:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::mutex promiseLock;
|
|
|
|
|
std::coroutine_handle<JobPromiseBase> handle;
|
|
|
|
|
std::coroutine_handle<> continuation = std::noop_coroutine();
|
|
|
|
|
uint64 id;
|
|
|
|
|
Event finishedEvent;
|
|
|
|
|
Event precondition = nullptr;
|
2021-12-22 11:42:07 +01:00
|
|
|
bool schedulable = true;
|
2021-12-15 00:05:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<bool MainJob = false>
|
|
|
|
|
struct JobBase
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using promise_type = JobPromiseBase<MainJob>;
|
|
|
|
|
|
|
|
|
|
explicit JobBase()
|
|
|
|
|
: promise(nullptr)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
explicit JobBase(JobPromiseBase<MainJob>* promise)
|
|
|
|
|
: promise(promise)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
~JobBase()
|
|
|
|
|
{
|
|
|
|
|
if(promise)
|
|
|
|
|
{
|
|
|
|
|
promise->schedule();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void resume()
|
|
|
|
|
{
|
|
|
|
|
promise->resume();
|
|
|
|
|
}
|
|
|
|
|
template<std::invocable Callable>
|
2021-12-22 11:42:07 +01:00
|
|
|
inline JobBase&& then(Callable callable)
|
2021-12-15 00:05:42 +01:00
|
|
|
{
|
|
|
|
|
return then(callable());
|
|
|
|
|
}
|
2021-12-22 11:42:07 +01:00
|
|
|
JobBase&& then(JobBase&& continuation)
|
2021-12-15 00:05:42 +01:00
|
|
|
{
|
|
|
|
|
promise->setContinuation(continuation.promise);
|
2021-12-22 11:42:07 +01:00
|
|
|
return std::move(continuation);
|
2021-12-15 00:05:42 +01:00
|
|
|
}
|
|
|
|
|
bool done()
|
|
|
|
|
{
|
|
|
|
|
return promise->done();
|
|
|
|
|
}
|
|
|
|
|
void raise()
|
|
|
|
|
{
|
|
|
|
|
promise->raise();
|
|
|
|
|
}
|
|
|
|
|
void reset()
|
|
|
|
|
{
|
|
|
|
|
promise->reset();
|
2021-12-02 13:00:03 +01:00
|
|
|
}
|
|
|
|
|
Event operator co_await()
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
return promise->finishedEvent;
|
2021-12-02 13:00:03 +01:00
|
|
|
}
|
2021-12-09 12:38:02 +01:00
|
|
|
static JobBase all() = delete;
|
2021-12-02 13:00:03 +01:00
|
|
|
template<typename... Awaitable>
|
|
|
|
|
static JobBase all(Awaitable... jobs)
|
|
|
|
|
{
|
2021-12-09 12:38:02 +01:00
|
|
|
(co_await jobs, ...);
|
2021-12-02 13:00:03 +01:00
|
|
|
}
|
2021-12-09 12:38:02 +01:00
|
|
|
template<iterable Iterable>
|
2021-12-02 13:00:03 +01:00
|
|
|
static JobBase all(Iterable&& collection)
|
|
|
|
|
{
|
|
|
|
|
for(auto&& it : collection)
|
|
|
|
|
{
|
|
|
|
|
co_await it;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-09 12:38:02 +01:00
|
|
|
template<std::invocable JobFunc, iterable IterableParams>
|
2021-12-02 13:00:03 +01:00
|
|
|
static JobBase launchJobs(JobFunc&& func, IterableParams params)
|
|
|
|
|
{
|
|
|
|
|
List<JobBase> jobs;
|
|
|
|
|
for(auto&& param : params)
|
|
|
|
|
{
|
|
|
|
|
jobs.add(func(param));
|
|
|
|
|
}
|
|
|
|
|
for(auto job : jobs)
|
|
|
|
|
{
|
|
|
|
|
co_await job;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-24 12:10:23 +01:00
|
|
|
private:
|
2021-12-15 00:05:42 +01:00
|
|
|
JobPromiseBase<MainJob>* promise;
|
2021-11-24 12:10:23 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using MainJob = JobBase<true>;
|
|
|
|
|
using Job = JobBase<false>;
|
2021-12-15 00:05:42 +01:00
|
|
|
using MainPromise = JobPromiseBase<true>;
|
|
|
|
|
using Promise = JobPromiseBase<false>;
|
2021-11-24 12:10:23 +01:00
|
|
|
|
2021-10-12 14:20:30 +02:00
|
|
|
class ThreadPool
|
|
|
|
|
{
|
|
|
|
|
public:
|
2021-12-02 13:00:03 +01:00
|
|
|
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
|
2021-10-12 14:20:30 +02:00
|
|
|
virtual ~ThreadPool();
|
2021-12-15 00:05:42 +01:00
|
|
|
// Adds a job to the waiting queue for event, or directly to the job queue if event is nullptr
|
|
|
|
|
void enqueueWaiting(Event& event, Promise* job);
|
|
|
|
|
// Adds a job to the waiting queue for event, or directly to the job queue if event is nullptr
|
|
|
|
|
void enqueueWaiting(Event& event, MainPromise* job);
|
2021-11-24 12:10:23 +01:00
|
|
|
void notify(Event& event);
|
2021-12-02 13:00:03 +01:00
|
|
|
void threadLoop(const bool isMainThread);
|
2021-10-12 14:20:30 +02:00
|
|
|
private:
|
2021-11-01 20:25:16 +01:00
|
|
|
std::atomic_bool running;
|
2021-10-23 00:22:35 +02:00
|
|
|
std::vector<std::thread> workers;
|
2021-11-01 20:25:16 +01:00
|
|
|
|
2021-12-15 00:05:42 +01:00
|
|
|
List<MainPromise*> mainJobs;
|
2021-11-01 20:25:16 +01:00
|
|
|
std::mutex mainJobLock;
|
2021-11-11 20:12:50 +01:00
|
|
|
std::condition_variable mainJobCV;
|
2021-11-01 20:25:16 +01:00
|
|
|
|
2021-12-15 00:05:42 +01:00
|
|
|
List<Promise*> jobQueue;
|
2021-11-01 20:25:16 +01:00
|
|
|
std::mutex jobQueueLock;
|
2021-11-11 20:12:50 +01:00
|
|
|
std::condition_variable jobQueueCV;
|
2021-11-01 20:25:16 +01:00
|
|
|
|
2021-12-22 11:42:07 +01:00
|
|
|
std::map<Event, List<MainPromise*>> waitingMainJobs;
|
2021-11-01 20:25:16 +01:00
|
|
|
std::mutex waitingMainLock;
|
|
|
|
|
|
2021-12-22 11:42:07 +01:00
|
|
|
std::map<Event, List<Promise*>> waitingJobs;
|
2021-12-15 00:05:42 +01:00
|
|
|
std::mutex waitingLock;
|
|
|
|
|
};
|
2021-11-01 20:25:16 +01:00
|
|
|
|
|
|
|
|
template<bool MainJob>
|
2021-12-09 12:38:02 +01:00
|
|
|
inline JobBase<MainJob> JobPromiseBase<MainJob>::get_return_object() noexcept
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
return JobBase<MainJob>(this);
|
2021-11-01 20:25:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<bool MainJob>
|
2021-12-09 12:38:02 +01:00
|
|
|
inline auto JobPromiseBase<MainJob>::initial_suspend() noexcept
|
2021-11-01 20:25:16 +01:00
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
return std::suspend_always{};
|
2021-11-01 20:25:16 +01:00
|
|
|
}
|
2021-12-02 13:00:03 +01:00
|
|
|
template<bool MainJob>
|
|
|
|
|
inline auto JobPromiseBase<MainJob>::final_suspend() const noexcept
|
|
|
|
|
{
|
|
|
|
|
struct JobAwaitable
|
|
|
|
|
{
|
|
|
|
|
constexpr bool await_ready() const noexcept { return false; }
|
|
|
|
|
constexpr auto await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h) const noexcept
|
|
|
|
|
{
|
|
|
|
|
return h.promise().continuation;
|
|
|
|
|
}
|
|
|
|
|
constexpr void await_resume() const noexcept {}
|
|
|
|
|
};
|
|
|
|
|
return JobAwaitable{};
|
|
|
|
|
}
|
2021-11-01 20:25:16 +01:00
|
|
|
|
|
|
|
|
template<bool MainJob>
|
|
|
|
|
inline constexpr void Event::await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
|
|
|
|
|
{
|
2021-12-15 00:05:42 +01:00
|
|
|
h.promise().enqueue(*this);
|
2021-11-01 20:25:16 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-12 14:20:30 +02:00
|
|
|
} // namespace Seele
|