Files
Seele/src/Engine/ThreadPool.h
T

376 lines
8.8 KiB
C++
Raw Normal View History

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"
2022-02-24 22:38:26 +01:00
#include <source_location>
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);
2022-01-12 14:40:26 +01:00
~Event() = default;
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;
}
operator bool()
{
2022-02-14 16:29:26 +01:00
std::scoped_lock lock(flag->lock);
return flag->data;
}
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;
2022-02-14 16:29:26 +01:00
struct StateStore
{
std::mutex lock;
bool data;
};
std::shared_ptr<StateStore> flag;
2021-11-11 20:12:50 +01:00
friend class ThreadPool;
2021-10-12 14:20:30 +02:00
};
2021-12-27 15:04:53 +01:00
extern std::atomic_uint64_t globalCounter;
2022-02-14 16:29:26 +01:00
template<bool MainJob>
2021-12-15 00:05:42 +01:00
struct JobBase;
template<bool MainJob>
struct JobPromiseBase
2021-11-24 12:10:23 +01:00
{
2021-12-27 15:04:53 +01:00
enum class State
{
READY,
WAITING,
SCHEDULED,
EXECUTING,
DONE
};
2022-02-24 22:38:26 +01:00
JobPromiseBase(const std::source_location& location = std::source_location::current())
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++;
2022-02-24 22:38:26 +01:00
finishedEvent = Event(std::string(location.file_name()).append(": ").append(location.function_name()));
2021-12-09 12:38:02 +01:00
}
2022-02-14 16:29:26 +01:00
~JobPromiseBase()
{}
2021-12-15 00:05:42 +01:00
JobBase<MainJob> get_return_object() noexcept;
inline auto initial_suspend() noexcept;
2022-02-14 16:29:26 +01:00
inline auto final_suspend() noexcept;
2021-12-15 00:05:42 +01:00
void return_void() noexcept {}
void unhandled_exception() noexcept {
2022-02-14 16:29:26 +01:00
std::cerr << "Unhandled exception! Exiting" << std::endl;
2021-12-15 00:05:42 +01:00
exit(1);
};
2021-11-24 12:10:23 +01:00
void resume()
{
2022-02-14 16:29:26 +01:00
std::scoped_lock lock(promiseLock);
2021-12-27 15:04:53 +01:00
if(!handle || handle.done() || executing())
2021-12-15 00:05:42 +01:00
{
2021-12-27 15:04:53 +01:00
return;
2021-12-15 00:05:42 +01:00
}
2021-12-27 15:04:53 +01:00
state = State::EXECUTING;
handle.resume();
2021-11-24 12:10:23 +01:00
}
2022-02-14 16:29:26 +01:00
void markDone()
{
state = State::DONE;
finishedEvent.raise();
if(continuation)
{
2022-02-24 22:38:26 +01:00
std::scoped_lock lock(continuation->promiseLock);
2022-02-14 16:29:26 +01:00
getGlobalThreadPool().scheduleJob(continuation);
continuation->removeRef();
}
}
2021-12-15 00:05:42 +01:00
void setContinuation(JobPromiseBase* cont)
2021-12-09 12:38:02 +01:00
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lock(promiseLock, cont->promiseLock);
2022-02-24 22:38:26 +01:00
assert(cont->ready());
2022-02-14 16:29:26 +01:00
continuation = cont;
2022-01-12 14:40:26 +01:00
cont->state = State::SCHEDULED;
2022-02-14 16:29:26 +01:00
cont->addRef();
2021-12-02 13:00:03 +01:00
}
bool done()
{
2022-02-14 16:29:26 +01:00
return state == State::DONE;
2021-12-02 13:00:03 +01:00
}
2021-12-27 15:04:53 +01:00
bool scheduled()
{
return state == State::SCHEDULED;
}
bool waiting()
{
return state == State::WAITING;
}
bool executing()
{
return state == State::EXECUTING;
}
bool ready()
{
return state == State::READY;
}
2021-12-02 13:00:03 +01:00
void reset()
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lock(promiseLock);
2021-12-15 00:05:42 +01:00
finishedEvent.reset();
}
void enqueue(Event& event)
{
2021-12-27 15:04:53 +01:00
if(!handle || handle.done() || waiting() || scheduled())
2021-12-22 11:42:07 +01:00
{
return;
}
2021-12-27 15:04:53 +01:00
state = State::WAITING;
2021-12-15 00:05:42 +01:00
getGlobalThreadPool().enqueueWaiting(event, this);
}
void schedule()
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lock(promiseLock);
2021-12-27 15:04:53 +01:00
if(!handle || done() || !ready())
2021-12-15 00:05:42 +01:00
{
return;
}
2022-02-24 22:38:26 +01:00
state = State::SCHEDULED;
2022-01-12 14:40:26 +01:00
getGlobalThreadPool().scheduleJob(this);
2021-12-15 00:05:42 +01:00
}
2022-02-14 16:29:26 +01:00
void addRef()
{
numRefs++;
}
void removeRef()
{
if(--numRefs < 1 && done())
{
handle.destroy();
}
}
2021-12-15 00:05:42 +01:00
std::mutex promiseLock;
std::coroutine_handle<JobPromiseBase> handle;
2022-02-14 16:29:26 +01:00
JobPromiseBase* continuation = nullptr;
2021-12-15 00:05:42 +01:00
uint64 id;
2022-02-14 16:29:26 +01:00
std::atomic_uint64_t numRefs = 0;
2021-12-15 00:05:42 +01:00
Event finishedEvent;
2021-12-27 15:04:53 +01:00
State state = State::READY;
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)
{
}
2022-02-14 16:29:26 +01:00
JobBase(const JobBase& other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
promise->addRef();
}
JobBase(JobBase&& other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
other.promise = nullptr;
}
2021-12-15 00:05:42 +01:00
~JobBase()
{
if(promise)
{
promise->schedule();
2022-02-14 16:29:26 +01:00
promise->removeRef();
2021-12-15 00:05:42 +01:00
}
}
2022-02-14 16:29:26 +01:00
JobBase& operator=(const JobBase& other)
{
if(this != &other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
promise->addRef();
}
return *this;
}
JobBase& operator=(JobBase&& other)
{
if(this != &other)
{
std::scoped_lock lock(other.promise->promiseLock);
promise = other.promise;
other.promise = nullptr;
}
return *this;
}
2021-12-15 00:05:42 +01:00
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);
2022-02-24 22:38:26 +01:00
promise->schedule();
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();
}
2022-01-12 14:40:26 +01:00
void finalize()
2021-12-15 00:05:42 +01:00
{
2022-01-12 14:40:26 +01:00
promise->finalize();
2021-12-02 13:00:03 +01:00
}
2022-02-24 22:38:26 +01:00
Event operator co_await() const
2021-12-02 13:00:03 +01:00
{
2022-02-24 22:38:26 +01:00
// the co_await operator keeps a reference to this, we it won't
2022-02-14 16:29:26 +01:00
// be scheduled from the destructor
2021-12-27 15:04:53 +01:00
promise->schedule();
2022-02-24 22:38:26 +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;
template<iterable Iterable>
2022-02-14 16:29:26 +01:00
static JobBase all(Iterable collection)
2021-12-02 13:00:03 +01:00
{
2022-02-14 16:29:26 +01:00
for(auto it : collection)
2021-12-02 13:00:03 +01:00
{
co_await it;
}
}
2022-02-14 16:29:26 +01:00
template<typename... Awaitable>
static JobBase all(Awaitable&&... jobs)
{
return JobBase::all(Array{jobs...});
}
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:
2022-02-14 16:29:26 +01:00
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
2021-10-12 14:20:30 +02:00
virtual ~ThreadPool();
2022-02-14 16:29:26 +01:00
void cleanup();
2022-01-12 14:40:26 +01:00
// Adds a job to the waiting queue for event
2021-12-15 00:05:42 +01:00
void enqueueWaiting(Event& event, Promise* job);
2022-01-12 14:40:26 +01:00
// Adds a job to the waiting queue for event
2021-12-15 00:05:42 +01:00
void enqueueWaiting(Event& event, MainPromise* job);
2022-01-12 14:40:26 +01:00
void scheduleJob(Promise* job);
void scheduleJob(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
2022-01-12 14:40:26 +01:00
std::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
2022-01-12 14:40:26 +01:00
std::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
2022-01-12 14:40:26 +01:00
std::map<Event, std::list<MainPromise*>> waitingMainJobs;
2021-11-01 20:25:16 +01:00
std::mutex waitingMainLock;
2022-01-12 14:40:26 +01:00
std::map<Event, std::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
{
2022-02-14 16:29:26 +01:00
numRefs++;
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
}
2022-01-12 14:40:26 +01:00
2021-12-02 13:00:03 +01:00
template<bool MainJob>
2022-02-14 16:29:26 +01:00
inline auto JobPromiseBase<MainJob>::final_suspend() noexcept
2021-12-02 13:00:03 +01:00
{
2022-02-14 16:29:26 +01:00
/*struct JobAwaitable
2021-12-02 13:00:03 +01:00
{
constexpr bool await_ready() const noexcept { return false; }
constexpr auto await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h) const noexcept
{
2022-02-14 16:29:26 +01:00
auto continuation = h.promise().continuation;
h.destroy();
2021-12-02 13:00:03 +01:00
}
constexpr void await_resume() const noexcept {}
};
2022-02-14 16:29:26 +01:00
return JobAwaitable{};*/
markDone();
return std::suspend_always{};
2021-12-02 13:00:03 +01:00
}
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);
2022-02-14 16:29:26 +01:00
flag->lock.unlock();
2021-11-01 20:25:16 +01:00
}
2021-10-12 14:20:30 +02:00
} // namespace Seele