Files
Seele/src/Engine/ThreadPool.h
T

255 lines
6.0 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"
2021-10-12 14:20:30 +02:00
namespace Seele
{
2021-11-01 20:25:16 +01:00
extern class ThreadPool& getGlobalThreadPool();
2021-10-12 14:20:30 +02:00
2021-11-01 20:25:16 +01:00
template<bool MainJob>
struct JobBase;
2021-12-09 12:38:02 +01:00
struct Event;
static std::atomic_uint64_t globalCounter;
2021-11-01 20:25:16 +01:00
template<bool MainJob>
struct JobPromiseBase
{
JobBase<MainJob> get_return_object() noexcept;
2021-12-09 12:38:02 +01:00
inline auto initial_suspend() noexcept;
2021-12-02 13:00:03 +01:00
inline auto final_suspend() const noexcept;
2021-10-12 14:20:30 +02:00
void return_void() noexcept {}
void unhandled_exception() noexcept {
2021-12-02 13:00:03 +01:00
std::cerr << "Unhandled exception" << std::endl;
2021-10-12 14:20:30 +02:00
exit(1);
};
2021-12-09 12:38:02 +01:00
std::coroutine_handle<> continuation = std::noop_coroutine();
uint64 id;
Event finishedEvent;
2021-10-12 14:20:30 +02:00
};
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
}
Event operator co_await()
{
return *this;
}
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-09 12:38:02 +01:00
template<bool MainJob = false>
2021-11-24 12:10:23 +01:00
struct JobBase
{
public:
using promise_type = JobPromiseBase<MainJob>;
explicit JobBase()
: id(-1)
2021-12-09 12:38:02 +01:00
{
}
explicit JobBase(std::coroutine_handle<promise_type> handle, uint64 id, Event finishedEvent)
2021-11-24 12:10:23 +01:00
: handle(handle)
2021-12-09 12:38:02 +01:00
, event(std::move(finishedEvent))
, id(id)
2021-11-24 12:10:23 +01:00
{
}
JobBase(const JobBase & rhs) = delete;
JobBase(JobBase&& rhs)
: handle(std::move(rhs.handle))
2021-12-09 12:38:02 +01:00
, event(rhs.event)
2021-11-24 12:10:23 +01:00
, id(std::move(rhs.id))
{
rhs.id = -1;
rhs.handle = nullptr;
}
~JobBase()
{
2021-12-02 13:00:03 +01:00
if(handle && handle.done())
2021-11-24 12:10:23 +01:00
{
2021-12-09 12:38:02 +01:00
std::cout << "Destroy job " << handle.address() << std::endl;
2021-11-24 12:10:23 +01:00
handle.destroy();
}
}
JobBase& operator=(const JobBase& rhs) = delete;
JobBase& operator=(JobBase&& rhs)
{
if(this != &rhs)
{
handle = std::move(rhs.handle);
2021-12-09 12:38:02 +01:00
event = std::move(rhs.event);
2021-11-24 12:10:23 +01:00
id = std::move(rhs.id);
rhs.id = -1;
rhs.handle = nullptr;
}
return *this;
}
void resume()
{
handle.resume();
}
2021-12-09 12:38:02 +01:00
template<std::invocable Callable>
inline JobBase then(Callable callable)
{
return then(callable());
}
JobBase then(JobBase continuation)
2021-12-02 13:00:03 +01:00
{
handle.promise().continuation = continuation.handle;
2021-12-09 12:38:02 +01:00
return continuation;
2021-12-02 13:00:03 +01:00
}
bool done()
{
return handle.done();
}
void raise()
{
event.raise();
}
void reset()
{
event.reset();
}
Event operator co_await()
{
return event;
}
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:
std::coroutine_handle<promise_type> handle;
2021-12-02 13:00:03 +01:00
Event event;
2021-11-24 12:10:23 +01:00
uint64 id;
};
using MainJob = JobBase<true>;
using Job = JobBase<false>;
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-11-01 20:25:16 +01:00
void addJob(Job&& job);
void addJob(MainJob&& job);
2021-12-02 13:00:03 +01:00
void enqueueWaiting(Event& event, Job&& job);
void enqueueWaiting(Event& event, MainJob&& 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
List<MainJob> mainJobs;
std::mutex mainJobLock;
2021-11-11 20:12:50 +01:00
std::condition_variable mainJobCV;
2021-11-01 20:25:16 +01:00
List<Job> jobQueue;
std::mutex jobQueueLock;
2021-11-11 20:12:50 +01:00
std::condition_variable jobQueueCV;
2021-11-01 20:25:16 +01:00
2021-11-11 20:12:50 +01:00
Map<Event, List<Job>> waitingJobs;
2021-11-01 20:25:16 +01:00
std::mutex waitingLock;
2021-11-11 20:12:50 +01:00
Map<Event, List<MainJob>> waitingMainJobs;
2021-11-01 20:25:16 +01:00
std::mutex waitingMainLock;
2021-11-19 15:08:56 +01:00
void tryMainJob();
2021-10-12 14:20:30 +02: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
{
id = globalCounter++;
return JobBase<MainJob>(std::coroutine_handle<JobPromiseBase<MainJob>>::from_promise(*this), id, finishedEvent);
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
{
struct JobAwaitable
{
constexpr bool await_ready() { return false; }
constexpr void await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
{
2021-12-09 12:38:02 +01:00
getGlobalThreadPool().addJob(std::move(JobBase<MainJob>(h, id, event)));
2021-11-01 20:25:16 +01:00
}
constexpr void await_resume() {}
2021-12-09 12:38:02 +01:00
uint64 id;
Event& event;
2021-11-01 20:25:16 +01:00
};
2021-12-09 12:38:02 +01:00
return JobAwaitable{id, finishedEvent};
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-09 12:38:02 +01:00
getGlobalThreadPool().enqueueWaiting(*this, std::move(JobBase<MainJob>(h, h.promise().id, *this)));
2021-11-01 20:25:16 +01:00
}
2021-10-12 14:20:30 +02:00
} // namespace Seele