Files
Seele/src/Engine/ThreadPool.h
T

447 lines
11 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();
2022-03-26 12:55:04 +01:00
template<bool MainJob>
struct JobBase;
2021-11-01 20:25:16 +01:00
template<bool MainJob>
2021-12-15 00:05:42 +01:00
struct JobPromiseBase;
2021-11-01 20:25:16 +01:00
struct Event
{
public:
2022-03-26 17:37:30 +01:00
//Event(nullptr_t);
2022-03-26 12:55:04 +01:00
Event(const std::string& name, const std::source_location& location = std::source_location::current());
Event(const std::source_location& location = std::source_location::current());
2022-03-28 21:18:57 +02:00
Event(const Event& other);
2022-03-26 17:37:30 +01:00
Event(Event&& other);
2022-01-12 14:40:26 +01:00
~Event() = default;
2022-03-28 21:18:57 +02:00
Event& operator=(const Event& other);
2022-03-26 17:37:30 +01:00
Event& operator=(Event&& other);
2021-11-11 20:12:50 +01:00
auto operator<=>(const Event& other) const
{
2022-03-28 21:18:57 +02:00
return state->name <=> other.state->name;
2021-11-11 20:12:50 +01:00
}
2021-12-15 00:05:42 +01:00
bool operator==(const Event& other) const
{
2022-03-28 21:18:57 +02:00
return state->name == other.state->name;
2021-11-11 20:12:50 +01:00
}
operator bool()
{
2022-03-26 17:37:30 +01:00
std::scoped_lock lock(eventLock);
2022-03-28 21:18:57 +02:00
return state->data;
}
2021-11-11 20:12:50 +01:00
2022-03-19 22:45:30 +01:00
friend std::ostream& operator<<(std::ostream& stream, const Event& event)
{
stream
2022-03-28 21:18:57 +02:00
<< event.state->location.file_name()
2022-03-19 22:45:30 +01:00
<< "("
2022-03-28 21:18:57 +02:00
<< event.state->location.line()
2022-03-19 22:45:30 +01:00
<< ":"
2022-03-28 21:18:57 +02:00
<< event.state->location.column()
2022-03-19 22:45:30 +01:00
<< "): "
2022-03-28 21:18:57 +02:00
<< event.state->location.function_name();
2022-03-19 22:45:30 +01:00
return stream;
}
2021-11-01 20:25:16 +01:00
void raise();
void reset();
bool await_ready();
2022-03-26 12:55:04 +01:00
void await_suspend(std::coroutine_handle<JobPromiseBase<false>> h);
void await_suspend(std::coroutine_handle<JobPromiseBase<true>> h);
2021-11-01 20:25:16 +01:00
constexpr void await_resume() {}
private:
2022-03-28 21:18:57 +02:00
mutable std::mutex eventLock;
struct EventState
{
std::string name;
std::source_location location;
bool data = false;
Array<JobBase<false>> waitingJobs;
Array<JobBase<true>> waitingMainJobs;
};
std::shared_ptr<EventState> state;
2021-11-11 20:12:50 +01:00
friend class ThreadPool;
2021-10-12 14:20:30 +02:00
};
2022-03-26 12:55:04 +01:00
extern std::mutex promisesLock;
extern List<JobPromiseBase<false>*> promises;
2021-12-15 00:05:42 +01:00
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())
2022-03-28 21:18:57 +02:00
: pad0(12345)//0x7472617453)
2022-03-26 17:37:30 +01:00
, handle(std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this))
, waitingFor(nullptr)
, continuation(nullptr)
, numRefs(0)
, finishedEvent(location)
, state(State::READY)
2022-03-28 21:18:57 +02:00
, pad1(12345)//0x646E45)
2021-12-09 12:38:02 +01:00
{
2022-03-26 12:55:04 +01:00
if constexpr(!MainJob)
{
std::scoped_lock lock(promisesLock);
promises.add(this);
}
2021-12-09 12:38:02 +01:00
}
2022-02-14 16:29:26 +01:00
~JobPromiseBase()
2022-03-26 12:55:04 +01:00
{
if constexpr (!MainJob)
{
std::scoped_lock lock(promisesLock);
promises.remove(promises.find(this));
}
}
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 {}
2022-03-19 22:45:30 +01:00
void unhandled_exception() {
std::exception_ptr exception = std::current_exception();
std::cerr << "Unhandled exception!" << std::endl;
throw exception;
2021-12-15 00:05:42 +01:00
};
2021-11-24 12:10:23 +01:00
void resume()
{
2022-03-28 21:18:57 +02:00
validate();
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
}
2021-12-15 00:05:42 +01:00
void setContinuation(JobPromiseBase* cont)
2021-12-09 12:38:02 +01:00
{
2022-03-28 21:18:57 +02:00
validate();
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-03-26 12:55:04 +01:00
cont->waitingFor = &finishedEvent;
2022-02-14 16:29:26 +01:00
cont->addRef();
2021-12-02 13:00:03 +01:00
}
bool done()
{
2022-03-28 21:18:57 +02:00
validate();
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()
{
2022-03-28 21:18:57 +02:00
validate();
2021-12-27 15:04:53 +01:00
return state == State::SCHEDULED;
}
bool waiting()
{
2022-03-28 21:18:57 +02:00
validate();
2021-12-27 15:04:53 +01:00
return state == State::WAITING;
}
bool executing()
{
2022-03-28 21:18:57 +02:00
validate();
2021-12-27 15:04:53 +01:00
return state == State::EXECUTING;
}
bool ready()
{
2022-03-28 21:18:57 +02:00
validate();
2021-12-27 15:04:53 +01:00
return state == State::READY;
}
2022-03-26 16:15:50 +01:00
void enqueue(Event* event);
bool schedule();
2022-02-14 16:29:26 +01:00
void addRef()
{
2022-03-28 21:18:57 +02:00
validate();
2022-02-14 16:29:26 +01:00
numRefs++;
}
void removeRef()
{
2022-03-28 21:18:57 +02:00
validate();
2022-03-26 12:55:04 +01:00
numRefs--;
if(numRefs == 0)
2022-02-14 16:29:26 +01:00
{
2022-03-26 12:55:04 +01:00
if(!schedule())
2022-03-19 22:45:30 +01:00
{
handle.destroy();
}
}
2022-02-14 16:29:26 +01:00
}
2022-03-28 21:18:57 +02:00
void validate()
{
assert(pad0 == 12345);
assert(pad1 == 12345);
}
2022-03-26 17:37:30 +01:00
uint64 pad0;
2021-12-15 00:05:42 +01:00
std::coroutine_handle<JobPromiseBase> handle;
2022-03-26 17:37:30 +01:00
Event* waitingFor;
JobPromiseBase* continuation;
uint64 numRefs;
2021-12-15 00:05:42 +01:00
Event finishedEvent;
2022-03-26 17:37:30 +01:00
State state;
uint64 pad1;
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-03-26 12:55:04 +01:00
promise->addRef();
2021-12-15 00:05:42 +01:00
}
2022-02-14 16:29:26 +01:00
JobBase(const JobBase& other)
{
promise = other.promise;
2022-03-28 21:18:57 +02:00
if(promise != nullptr)
{
promise->addRef();
}
2022-02-14 16:29:26 +01:00
}
JobBase(JobBase&& other)
{
promise = other.promise;
other.promise = nullptr;
}
2021-12-15 00:05:42 +01:00
~JobBase()
{
if(promise)
{
2022-03-19 22:45:30 +01:00
//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)
{
2022-03-26 12:55:04 +01:00
if(promise != nullptr)
{
promise->removeRef();
}
2022-02-14 16:29:26 +01:00
promise = other.promise;
2022-03-28 21:18:57 +02:00
if(promise != nullptr)
{
promise->addRef();
}
2022-02-14 16:29:26 +01:00
}
return *this;
}
JobBase& operator=(JobBase&& other)
{
if(this != &other)
{
2022-03-26 12:55:04 +01:00
if(promise != nullptr)
{
promise->removeRef();
}
2022-02-14 16:29:26 +01:00
promise = other.promise;
other.promise = nullptr;
}
return *this;
}
2021-12-15 00:05:42 +01:00
void resume()
{
promise->resume();
}
template<std::invocable Callable>
2022-03-28 21:18:57 +02:00
inline JobBase then(Callable callable)
2021-12-15 00:05:42 +01:00
{
return then(callable());
}
2022-03-28 21:18:57 +02:00
JobBase then(JobBase continuation)
2021-12-15 00:05:42 +01:00
{
promise->setContinuation(continuation.promise);
2022-03-28 21:18:57 +02:00
continuation.promise->state = JobPromiseBase<MainJob>::State::SCHEDULED;
return continuation;
2021-12-15 00:05:42 +01:00
}
bool done()
{
return promise->done();
}
2022-03-26 12:55:04 +01:00
Event& operator co_await()
2021-12-02 13:00:03 +01:00
{
2022-03-19 22:45:30 +01:00
// the co_await operator keeps a reference to this, 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;
2022-03-28 21:18:57 +02:00
2022-03-19 22:45:30 +01:00
template<std::ranges::range Iterable>
2022-03-26 16:15:50 +01:00
requires std::same_as<std::ranges::range_value_t<Iterable>, JobBase<MainJob>>
static JobBase<MainJob> all(Iterable collection);
2022-03-28 21:18:57 +02:00
2022-03-19 22:45:30 +01:00
template<std::ranges::range 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-03-28 21:18:57 +02:00
2022-02-14 16:29:26 +01:00
template<typename... Awaitable>
2022-03-28 21:18:57 +02:00
static JobBase all(Awaitable... jobs)
2022-02-14 16:29:26 +01:00
{
2022-03-28 21:18:57 +02:00
return JobBase::all(std::initializer_list{jobs...});
2022-02-14 16:29:26 +01:00
}
2022-03-19 22:45:30 +01:00
template<typename JobFunc, std::ranges::input_range Iterable>
requires std::invocable<JobFunc, std::ranges::range_reference_t<Iterable>>
2022-03-26 16:15:50 +01:00
static JobBase launchJobs(JobFunc&& func, Iterable params);
2021-11-24 12:10:23 +01:00
private:
2021-12-15 00:05:42 +01:00
JobPromiseBase<MainJob>* promise;
2022-03-19 22:45:30 +01:00
friend class ThreadPool;
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-03-28 21:18:57 +02:00
ThreadPool(uint32 threadCount = std::thread::hardware_concurrency());
2021-10-12 14:20:30 +02:00
virtual ~ThreadPool();
2022-03-19 22:45:30 +01:00
void waitIdle();
2022-03-26 12:55:04 +01:00
void scheduleJob(Job job);
void scheduleJob(MainJob job);
2022-03-19 22:45:30 +01:00
template<std::ranges::range Iterable>
requires std::same_as<std::ranges::range_value_t<Iterable>, MainJob>
void scheduleBatch(Iterable jobs)
{
std::scoped_lock lock(mainJobLock);
for(auto job : jobs)
{
2022-03-26 12:55:04 +01:00
//job.promise->addRef();
2022-03-19 22:45:30 +01:00
job.promise->state = JobPromiseBase<true>::State::SCHEDULED;
2022-03-28 21:18:57 +02:00
mainJobs.add(std::move(job));
2022-03-19 22:45:30 +01:00
}
mainJobCV.notify_one();
}
template<std::ranges::range Iterable>
requires std::same_as<std::ranges::range_value_t<Iterable>, Job>
void scheduleBatch(Iterable jobs)
{
std::scoped_lock lock(jobQueueLock);
for(auto job : jobs)
{
2022-03-26 12:55:04 +01:00
//job.promise->addRef();
2022-03-19 22:45:30 +01:00
job.promise->state = JobPromiseBase<false>::State::SCHEDULED;
2022-03-28 21:18:57 +02:00
jobQueue.add(std::move(job));
2022-03-19 22:45:30 +01:00
}
jobQueueCV.notify_all();
}
2022-03-26 12:55:04 +01:00
void notify(Event* event);
2022-03-19 22:45:30 +01:00
void mainLoop();
void threadLoop();
2021-10-12 14:20:30 +02:00
private:
2021-11-01 20:25:16 +01:00
std::atomic_bool running;
2022-03-19 22:45:30 +01:00
std::mutex numIdlingLock;
std::condition_variable numIdlingIncr;
uint32 numIdling;
Array<std::thread> workers;
2021-11-01 20:25:16 +01:00
2022-03-26 12:55:04 +01:00
List<MainJob> 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-03-26 12:55:04 +01:00
List<Job> 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-03-26 12:55:04 +01:00
uint32 localQueueSize = 50;
2021-12-15 00:05:42 +01:00
};
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
{
2022-03-28 21:18:57 +02:00
validate();
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-03-28 21:18:57 +02:00
validate();
2022-03-26 16:15:50 +01:00
state = State::DONE;
finishedEvent.raise();
if(continuation)
{
getGlobalThreadPool().scheduleJob(JobBase<MainJob>(continuation));
continuation->removeRef();
}
2022-02-14 16:29:26 +01:00
return std::suspend_always{};
2021-12-02 13:00:03 +01:00
}
2022-03-26 16:15:50 +01:00
template<bool MainJob>
inline bool JobPromiseBase<MainJob>::schedule()
{
2022-03-28 21:18:57 +02:00
validate();
2022-03-26 16:15:50 +01:00
if(!handle || done() || !ready())
{
return false;
}
state = State::SCHEDULED;
getGlobalThreadPool().scheduleJob(std::move(JobBase<MainJob>(this)));
return true;
}
template<bool MainJob>
template<std::ranges::range Iterable>
requires std::same_as<std::ranges::range_value_t<Iterable>, JobBase<MainJob>>
inline JobBase<MainJob> JobBase<MainJob>::all(Iterable collection)
{
getGlobalThreadPool().scheduleBatch(collection);
for(auto it : collection)
{
co_await it;
}
}
template<bool MainJob>
template<typename JobFunc, std::ranges::input_range Iterable>
requires std::invocable<JobFunc, std::ranges::range_reference_t<Iterable>>
inline JobBase<MainJob> JobBase<MainJob>::launchJobs(JobFunc&& func, Iterable params)
{
Array<JobBase<MainJob>> jobs;
for(auto&& param : params)
{
jobs.add(func(param));
}
getGlobalThreadPool().scheduleBatch(jobs);
for(auto job : jobs)
{
co_await job;
}
}
2021-10-12 14:20:30 +02:00
} // namespace Seele