sandbox
Loading...
Searching...
No Matches
thread_pool.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_ASSETS_THREAD_POOL_HPP_
3#define LIBSBX_ASSETS_THREAD_POOL_HPP_
4
5#include <thread>
6#include <mutex>
7#include <condition_variable>
8#include <queue>
9#include <vector>
10#include <functional>
11#include <atomic>
12#include <future>
13#include <memory>
14#include <ranges>
15#include <concepts>
16
17#include <fmt/format.h>
18
19#include <libsbx/utility/logger.hpp>
20
21namespace sbx::assets {
22
23class thread_pool final {
24
25public:
26
27 thread_pool(const std::size_t size = std::thread::hardware_concurrency())
28 : _is_running{true} {
29 _workers.reserve(size);
30
31 for ([[maybe_unused]] auto i : std::views::iota(0u, size)) {
32 _workers.emplace_back([this](){ _worker(); });
33 }
34 }
35
36 thread_pool(const thread_pool&) = delete;
37 thread_pool(thread_pool&&) = delete;
38
39 ~thread_pool() {
40 _is_running = false;
41 _condition.notify_all();
42
43 for (auto& worker : _workers) {
44 worker.join();
45 }
46 }
47
48 auto operator=(const thread_pool&) -> thread_pool& = delete;
49 auto operator=(thread_pool&&) -> thread_pool& = delete;
50
51 template<typename Function, typename... Args>
52 requires (std::is_invocable_v<Function, Args...>)
53 auto submit(Function&& function, Args&&... args) -> std::future<std::invoke_result_t<Function, Args...>> {
54 using result_type = std::invoke_result_t<Function, Args...>;
55
56 auto promise = std::make_shared<std::promise<result_type>>();
57 auto future = promise->get_future();
58
59 {
60 auto lock = std::scoped_lock{_mutex};
61
62 _tasks.emplace([promise, function = std::forward<Function>(function), ...args = std::forward<Args>(args)](){
63 try {
64 if constexpr (std::is_void_v<result_type>) {
65 std::invoke(function, std::forward<Args>(args)...);
66 promise->set_value();
67 } else {
68 promise->set_value(std::invoke(function, std::forward<Args>(args)...));
69 }
70 } catch (...) {
71 try {
72 promise->set_exception(std::current_exception());
73 } catch (...) {
74 utility::logger<"assets">::error("Failed to set exception for promise");
75 }
76 }
77 });
78 }
79
80 _condition.notify_one();
81
82 return future;
83 }
84
85
86private:
87
88 auto _worker() -> void {
89 while (true) {
90 auto lock = std::unique_lock{_mutex};
91
92 _condition.wait(lock, [this](){ return !_tasks.empty() || !_is_running; });
93
94 if (!_is_running) {
95 break;
96 }
97
98 auto task = std::move(_tasks.front());
99 _tasks.pop();
100
101 lock.unlock();
102
103 std::invoke(task);
104 }
105 }
106
107 std::vector<std::thread> _workers;
108 std::queue<std::function<void()>> _tasks;
109
110 std::mutex _mutex;
111 std::condition_variable _condition;
112
113 std::atomic_bool _is_running;
114
115}; // class thread_pool
116
117} // namespace sbx::assets
118
119#endif // LIBSBX_ASSETS_THREAD_POOL_HPP_
Definition: thread_pool.hpp:23
Definition: logger.hpp:124