ThreadSchedule 2.2.0
Modern C++ thread management library
Loading...
Searching...
No Matches
thread_pool_with_errors.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include "error_handler.hpp"
9#include "thread_pool.hpp"
10#include <memory>
11
12namespace threadschedule
13{
14
32template <typename PoolType>
34{
35 public:
36 explicit PoolWithErrors(size_t num_threads = std::thread::hardware_concurrency())
37 : pool_(num_threads), error_handler_(std::make_shared<ErrorHandler>())
38 {
39 }
40
52 template <typename Arg1, typename Arg2, typename... Args>
53 explicit PoolWithErrors(Arg1&& arg1, Arg2&& arg2, Args&&... args)
54 : pool_(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...),
55 error_handler_(std::make_shared<ErrorHandler>())
56 {
57 }
58
62 template <typename F, typename... Args>
63 auto submit(F&& f, Args&&... args) -> FutureWithErrorHandler<std::invoke_result_t<F, Args...>>
64 {
65 return submit_impl({}, std::forward<F>(f), std::forward<Args>(args)...);
66 }
67
71 template <typename F, typename... Args>
72 auto submit_with_description(std::string const& description, F&& f, Args&&... args)
73 -> FutureWithErrorHandler<std::invoke_result_t<F, Args...>>
74 {
75 return submit_impl(description, std::forward<F>(f), std::forward<Args>(args)...);
76 }
77
81 template <typename F, typename... Args>
82 auto try_submit(F&& f, Args&&... args)
83 -> expected<FutureWithErrorHandler<std::invoke_result_t<F, Args...>>, std::error_code>
84 {
85 return try_submit_impl({}, std::forward<F>(f), std::forward<Args>(args)...);
86 }
87
88 auto add_error_callback(ErrorCallback callback) -> size_t
89 {
90 return error_handler_->add_callback(std::move(callback));
91 }
92
93 template <typename Callback,
94 std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<Callback>, ErrorCallback>, int> = 0>
95 auto add_error_callback(Callback&& callback) -> size_t
96 {
97 return error_handler_->add_callback(std::forward<Callback>(callback));
98 }
99
100 auto remove_error_callback(size_t id) -> bool
101 {
102 return error_handler_->remove_callback(id);
103 }
104
106 {
107 error_handler_->clear_callbacks();
108 }
109
110 [[nodiscard]] auto error_count() const -> size_t
111 {
112 return error_handler_->error_count();
113 }
114
116 {
117 error_handler_->reset_error_count();
118 }
119
120 [[nodiscard]] auto pool() -> PoolType&
121 {
122 return pool_;
123 }
124
125 [[nodiscard]] auto get_statistics() const -> decltype(auto)
126 {
127 return pool_.get_statistics();
128 }
129
130 auto configure_threads(std::string const& name_prefix, SchedulingPolicy policy = SchedulingPolicy::OTHER,
131 ThreadPriority priority = ThreadPriority::normal()) -> decltype(auto)
132 {
133 return pool_.configure_threads(name_prefix, policy, priority);
134 }
135
136 auto set_affinity(ThreadAffinity const& affinity) -> decltype(auto)
137 {
138 return pool_.set_affinity(affinity);
139 }
140
141 auto distribute_across_cpus() -> decltype(auto)
142 {
143 return pool_.distribute_across_cpus();
144 }
145
147 {
148 pool_.wait_for_tasks();
149 }
150
151 void shutdown()
152 {
153 pool_.shutdown();
154 }
155
156 [[nodiscard]] auto size() const noexcept -> size_t
157 {
158 return pool_.size();
159 }
160
161 [[nodiscard]] auto pending_tasks() const -> size_t
162 {
163 return pool_.pending_tasks();
164 }
165
166 private:
167 template <typename F, typename... Args>
168 auto submit_impl(std::string description, F&& f, Args&&... args)
169 -> FutureWithErrorHandler<std::invoke_result_t<F, Args...>>
170 {
171 using return_type = std::invoke_result_t<F, Args...>;
172 auto handler = error_handler_;
173 auto wrapped_task =
174 [bound = detail::bind_args(std::forward<F>(f), std::forward<Args>(args)...), handler,
175 desc = std::move(description)]() mutable -> return_type {
176 try
177 {
178 if constexpr (std::is_void_v<return_type>)
179 {
180 bound();
181 return;
182 }
183 else
184 {
185 return bound();
186 }
187 }
188 catch (...)
189 {
190 handler->handle_error(TaskError::capture(desc));
191 throw;
192 }
193 };
194 auto future = pool_.submit(std::move(wrapped_task));
195 return FutureWithErrorHandler<std::invoke_result_t<F, Args...>>(std::move(future));
196 }
197
198 template <typename F, typename... Args>
199 auto try_submit_impl(std::string description, F&& f, Args&&... args)
200 -> expected<FutureWithErrorHandler<std::invoke_result_t<F, Args...>>, std::error_code>
201 {
202 using return_type = std::invoke_result_t<F, Args...>;
203 auto handler = error_handler_;
204 auto wrapped_task =
205 [bound = detail::bind_args(std::forward<F>(f), std::forward<Args>(args)...), handler,
206 desc = std::move(description)]() mutable -> return_type {
207 try
208 {
209 if constexpr (std::is_void_v<return_type>)
210 {
211 bound();
212 return;
213 }
214 else
215 {
216 return bound();
217 }
218 }
219 catch (...)
220 {
221 handler->handle_error(TaskError::capture(desc));
222 throw;
223 }
224 };
225 auto result = pool_.try_submit(std::move(wrapped_task));
226 if (!result.has_value())
227 return unexpected(result.error());
228 return FutureWithErrorHandler<std::invoke_result_t<F, Args...>>(std::move(result.value()));
229 }
230
231 PoolType pool_;
232 std::shared_ptr<ErrorHandler> error_handler_;
233};
234
237
240
243
244} // namespace threadschedule
Central registry and dispatcher for task-error callbacks.
A move-only future wrapper that supports an error callback.
Thread pool wrapper that combines any pool type with an ErrorHandler.
auto try_submit(F &&f, Args &&... args) -> expected< FutureWithErrorHandler< std::invoke_result_t< F, Args... > >, std::error_code >
Submit a task, returning an error instead of throwing on shutdown.
auto submit_with_description(std::string const &description, F &&f, Args &&... args) -> FutureWithErrorHandler< std::invoke_result_t< F, Args... > >
Submit a task with a description for better error messages.
auto distribute_across_cpus() -> decltype(auto)
PoolWithErrors(Arg1 &&arg1, Arg2 &&arg2, Args &&... args)
Construct with forwarded pool arguments.
auto add_error_callback(Callback &&callback) -> size_t
auto add_error_callback(ErrorCallback callback) -> size_t
auto get_statistics() const -> decltype(auto)
auto set_affinity(ThreadAffinity const &affinity) -> decltype(auto)
PoolWithErrors(size_t num_threads=std::thread::hardware_concurrency())
auto remove_error_callback(size_t id) -> bool
auto configure_threads(std::string const &name_prefix, SchedulingPolicy policy=SchedulingPolicy::OTHER, ThreadPriority priority=ThreadPriority::normal()) -> decltype(auto)
auto submit(F &&f, Args &&... args) -> FutureWithErrorHandler< std::invoke_result_t< F, Args... > >
Submit a task with automatic error handling.
auto size() const noexcept -> size_t
Manages a set of CPU indices to which a thread may be bound.
Value-semantic wrapper for a thread scheduling priority.
static constexpr auto normal() noexcept -> ThreadPriority
A result type that holds either a value of type T or an error of type E.
Definition expected.hpp:215
Error handling primitives: TaskError, ErrorHandler, and ErrorHandledTask.
auto bind_args(F &&f, Args &&... args)
Bind a callable with its arguments into a nullary lambda.
SchedulingPolicy
Enumeration of available thread scheduling policies.
@ OTHER
Standard round-robin time-sharing.
PoolWithErrors< FastThreadPool > FastThreadPoolWithErrors
FastThreadPool with integrated error handling.
detail::copyable_callable< void(TaskError const &)> ErrorCallback
Signature for error-handling callbacks registered with ErrorHandler.
PoolWithErrors< ThreadPool > ThreadPoolWithErrors
ThreadPool with integrated error handling.
PoolWithErrors< HighPerformancePool > HighPerformancePoolWithErrors
HighPerformancePool with integrated error handling.
static auto capture(std::string description={}) -> TaskError
Capture the current in-flight exception into a TaskError.
Thread pools: HighPerformancePool, ThreadPoolBase, LightweightPoolT, and GlobalPool.