#pragma once #include #include "MinimalEngine.h" #include "Containers/List.h" namespace Seele { extern class ThreadPool& getGlobalThreadPool(); template struct JobBase; template struct JobPromiseBase { JobBase get_return_object() noexcept; inline auto initial_suspend() const noexcept; 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); }; }; template struct JobBase { public: using promise_type = JobPromiseBase; explicit JobBase() {} explicit JobBase(std::coroutine_handle handle) : handle(handle) { /*if constexpr(MainJob) { std::cout << "Creating mainjob " << handle.address() << std::endl; } else { std::cout << "Creating job " << handle.address() << std::endl; }*/ } JobBase(const JobBase & rhs) = delete; JobBase(JobBase&& rhs) : handle(std::move(rhs.handle)) { rhs.handle = nullptr; } ~JobBase() { if(handle) { handle.destroy(); } } JobBase& operator=(const JobBase& rhs) = delete; JobBase& operator=(JobBase&& rhs) { if(this != &rhs) { handle = std::move(rhs.handle); rhs.handle = nullptr; } return *this; } void resume() { handle.resume(); } std::coroutine_handle handle; private: }; using MainJob = JobBase; using Job = JobBase; struct Event { public: Event(); Event(const std::string& name); auto operator<=>(const Event& other) const { return flag <=> other.flag; } Event operator co_await() { return *this; } void raise(); void reset(); bool await_ready(); template constexpr void await_suspend(std::coroutine_handle> h); constexpr void await_resume() {} private: std::string name; RefPtr flag; friend class ThreadPool; }; class ThreadPool { public: ThreadPool(uint32 threadCount = std::thread::hardware_concurrency()); virtual ~ThreadPool(); void addJob(Job&& job); void addJob(MainJob&& job); void enqueueWaiting(Event* event, Job&& job); void enqueueWaiting(Event* event, MainJob&& job); void notify(Event* event); private: std::atomic_bool running; std::thread* mainThread; std::vector workers; List mainJobs; std::mutex mainJobLock; std::condition_variable mainJobCV; List jobQueue; std::mutex jobQueueLock; std::condition_variable jobQueueCV; Map> waitingJobs; std::mutex waitingLock; Map> waitingMainJobs; std::mutex waitingMainLock; void threadLoop(const bool isMainThread); }; template inline JobBase JobPromiseBase::get_return_object() noexcept { return JobBase(); } template inline auto JobPromiseBase::initial_suspend() const noexcept { struct JobAwaitable { constexpr bool await_ready() { return false; } constexpr void await_suspend(std::coroutine_handle> h) { getGlobalThreadPool().addJob(std::move(JobBase(h))); } constexpr void await_resume() {} }; return JobAwaitable{}; } template inline constexpr void Event::await_suspend(std::coroutine_handle> h) { getGlobalThreadPool().enqueueWaiting(this, std::move(JobBase(h))); } } // namespace Seele