Program Listing for File ThreadPool.hpp
↰ Return to documentation for file (engine/include/Cacao/ThreadPool.hpp
)
#pragma once
#include "DllHelper.hpp"
#include "Exceptions.hpp"
#include <memory>
#include <future>
#include <concepts>
#include <vector>
#include <functional>
#include <algorithm>
namespace Cacao {
class CACAO_API ThreadPool {
public:
static ThreadPool& Get();
ThreadPool(const ThreadPool&) = delete;
ThreadPool(ThreadPool&&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
ThreadPool& operator=(ThreadPool&&) = delete;
template<typename F, typename... Args, typename R = std::invoke_result_t<F&&, Args&&...>>
requires std::invocable<F&&, Args&&...>
std::shared_future<R> Exec(F func, Args... args) {
Check<BadInitStateException>(IsRunning(), "The thread pool must be running to execute a task!");
//Create a task and get a result future
std::shared_ptr<std::packaged_task<R()>> task;
if constexpr(sizeof...(args) == 0) {
task = std::make_shared<std::packaged_task<R()>>(std::move(func));
} else {
//Wrap the function so it doesn't need any arguments
auto wrapper = std::bind(std::forward<F>(func), std::forward<Args...>(args...));
task = std::make_shared<std::packaged_task<R()>>(std::move(wrapper));
}
std::shared_future<R> result = task->get_future().share();
//Use closures to capture the task so its future can be set
ImplSubmit([task]() { (*task)(); });
return result;
}
template<typename F, typename... Args>
requires std::invocable<F&&, std::stop_token, Args&&...> && std::is_same_v<std::invoke_result_t<F&&, std::stop_token, Args&&...>, void>
std::stop_source ExecContinuous(F func, Args... args) {
Check<BadInitStateException>(IsRunning(), "The thread pool must be running to execute a continuous function!");
//Create stop source
std::stop_source& stop = stops.emplace_back();
//Wrap the function so it doesn't need any arguments
if constexpr(sizeof...(args) == 0) {
auto wrapper = std::bind_front(std::forward<F>(func), stop.get_token());
//Submit the function
ImplSubmit([this, wrapper, &stop]() {
//Invoke function and then remove
wrapper();
if(auto it = std::find(stops.begin(), stops.end(), stop); it != stops.end()) {
stops.erase(it);
}
});
} else {
auto wrapper = std::bind_front(std::forward<F>(func), stop.get_token(), std::forward<Args...>(args...));
//Submit the function
ImplSubmit([this, wrapper, &stop]() {
//Invoke function and then remove
wrapper();
if(auto it = std::find(stops.begin(), stops.end(), stop); it != stops.end()) {
stops.erase(it);
}
});
}
return stop;
}
void Start();
void Stop();
bool IsRunning() const;
std::size_t GetThreadCount() const;
private:
struct Impl;
std::unique_ptr<Impl> impl;
std::vector<std::stop_source> stops;
void ImplSubmit(std::function<void()> job);
ThreadPool();
~ThreadPool();
};
}