Files
Seele/src/Engine/ThreadPool.h
T

183 lines
4.2 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-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;
template<bool MainJob>
struct JobPromiseBase
{
JobBase<MainJob> get_return_object() noexcept;
inline auto initial_suspend() const noexcept;
2021-10-12 14:20:30 +02:00
std::suspend_never final_suspend() const noexcept { return {}; }
void return_void() noexcept {}
void unhandled_exception() noexcept {
std::cerr << "Unhandled exception caught" << std::endl;
exit(1);
};
};
2021-11-19 15:08:56 +01:00
static std::atomic_uint64_t globalCounter;
2021-11-01 20:25:16 +01:00
template<bool MainJob>
struct JobBase
2021-10-12 14:20:30 +02:00
{
public:
2021-11-01 20:25:16 +01:00
using promise_type = JobPromiseBase<MainJob>;
2021-10-12 14:20:30 +02:00
2021-11-11 20:12:50 +01:00
explicit JobBase()
2021-11-19 15:08:56 +01:00
: id(-1)
2021-11-11 20:12:50 +01:00
{}
2021-11-01 20:25:16 +01:00
explicit JobBase(std::coroutine_handle<promise_type> handle)
2021-10-12 14:20:30 +02:00
: handle(handle)
2021-11-19 15:08:56 +01:00
, id(globalCounter++)
2021-11-11 20:12:50 +01:00
{
2021-11-19 15:08:56 +01:00
std::cout << "Creating job " << id << std::endl;
2021-11-13 19:28:18 +01:00
/*if constexpr(MainJob)
{
std::cout << "Creating mainjob " << handle.address() << std::endl;
}
else
{
std::cout << "Creating job " << handle.address() << std::endl;
}*/
2021-11-11 20:12:50 +01:00
}
JobBase(const JobBase & rhs) = delete;
JobBase(JobBase&& rhs)
: handle(std::move(rhs.handle))
2021-11-19 15:08:56 +01:00
, id(std::move(rhs.id))
2021-11-11 20:12:50 +01:00
{
2021-11-19 15:08:56 +01:00
rhs.id = -1;
2021-11-11 20:12:50 +01:00
rhs.handle = nullptr;
}
2021-11-01 20:25:16 +01:00
~JobBase()
2021-10-12 14:20:30 +02:00
{
if(handle)
{
2021-11-19 15:08:56 +01:00
std::cout << "Destroying job " << id << std::endl;
2021-10-12 14:20:30 +02:00
handle.destroy();
}
}
2021-11-11 20:12:50 +01:00
JobBase& operator=(const JobBase& rhs) = delete;
JobBase& operator=(JobBase&& rhs)
{
if(this != &rhs)
{
handle = std::move(rhs.handle);
2021-11-19 15:08:56 +01:00
id = std::move(rhs.id);
rhs.id = -1;
2021-11-11 20:12:50 +01:00
rhs.handle = nullptr;
}
return *this;
}
2021-11-01 20:25:16 +01:00
void resume()
{
handle.resume();
}
std::coroutine_handle<promise_type> handle;
2021-11-19 15:08:56 +01:00
uint64 id;
2021-11-13 19:28:18 +01:00
private:
2021-11-01 20:25:16 +01:00
};
using MainJob = JobBase<true>;
using Job = JobBase<false>;
struct Event
{
public:
Event();
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
};
class ThreadPool
{
public:
2021-10-23 00:22:35 +02: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);
void enqueueWaiting(Event* event, Job&& job);
void enqueueWaiting(Event* event, MainJob&& job);
void notify(Event* event);
2021-10-12 14:20:30 +02:00
private:
2021-11-01 20:25:16 +01:00
std::atomic_bool running;
std::thread* mainThread;
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-11-01 20:25:16 +01:00
void threadLoop(const bool isMainThread);
2021-10-12 14:20:30 +02:00
};
2021-11-01 20:25:16 +01:00
template<bool MainJob>
inline JobBase<MainJob> JobPromiseBase<MainJob>::get_return_object() noexcept {
2021-11-11 20:12:50 +01:00
return JobBase<MainJob>();
2021-11-01 20:25:16 +01:00
}
template<bool MainJob>
inline auto JobPromiseBase<MainJob>::initial_suspend() const noexcept
{
struct JobAwaitable
{
constexpr bool await_ready() { return false; }
constexpr void await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
{
2021-11-11 20:12:50 +01:00
getGlobalThreadPool().addJob(std::move(JobBase<MainJob>(h)));
2021-11-01 20:25:16 +01:00
}
constexpr void await_resume() {}
};
return JobAwaitable{};
}
template<bool MainJob>
inline constexpr void Event::await_suspend(std::coroutine_handle<JobPromiseBase<MainJob>> h)
{
2021-11-11 20:12:50 +01:00
getGlobalThreadPool().enqueueWaiting(this, std::move(JobBase<MainJob>(h)));
2021-11-01 20:25:16 +01:00
}
2021-10-12 14:20:30 +02:00
} // namespace Seele