ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
error_handler.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "../detail/callable/copyable_function.hpp"
10#include "../detail/callable/move_only_function.hpp"
11#include "../task_error.hpp"
12#include <exception>
13#include <functional>
14#include <future>
15#include <map>
16#include <memory>
17#include <mutex>
18#include <string>
19#include <type_traits>
20#include <vector>
21
23{
24
25namespace error_handler_detail
26{
29} // namespace error_handler_detail
30
56{
57public:
64 auto
65 add_callback(error_callback callback) -> size_t
66 {
67 return emplace_callback(error_handler_detail::error_callback_storage(std::move(callback)));
68 }
69
70 template <typename Callback,
71 std::enable_if_t<!std::is_same_v<::threadschedule::detail::remove_cvref_t<Callback>, error_callback>, int>
72 = 0>
73 auto
74 add_callback(Callback&& callback) -> size_t
75 {
76 static_assert(std::is_invocable_r_v<void, Callback&, task_error const&>,
77 "Error callback must be invocable with task_error const&");
78 return emplace_callback(
79 ::threadschedule::detail::make_copyable_function<void(task_error const&)>(std::forward<Callback>(callback)));
80 }
81
88 auto
89 remove_callback(size_t id) -> bool
90 {
91 std::lock_guard<std::mutex> lock(mutex_);
92 return callbacks_.erase(id) > 0;
93 }
94
98 [[nodiscard]] auto
99 has_callback(size_t id) const -> bool
100 {
101 std::lock_guard<std::mutex> lock(mutex_);
102 return callbacks_.count(id) > 0;
103 }
104
111 void
113 {
114 std::lock_guard<std::mutex> lock(mutex_);
115 callbacks_.clear();
116 }
117
127 void
129 {
130 std::vector<error_handler_detail::error_callback_storage> snapshot;
131 {
132 std::lock_guard<std::mutex> lock(mutex_);
133 error_count_++;
134 snapshot.reserve(callbacks_.size());
135 for (auto const& [id, callback] : callbacks_)
136 snapshot.push_back(callback);
137 }
138
139 for (auto& callback : snapshot)
140 {
141 try
142 {
143 callback(error);
144 }
145 catch (...)
146 {
147 }
148 }
149 }
150
159 [[nodiscard]] auto
160 error_count() const -> size_t
161 {
162 std::lock_guard<std::mutex> lock(mutex_);
163 return error_count_;
164 }
165
169 void
171 {
172 std::lock_guard<std::mutex> lock(mutex_);
173 error_count_ = 0;
174 }
175
176private:
177 auto
178 emplace_callback(error_handler_detail::error_callback_storage callback) -> size_t
179 {
180 std::lock_guard<std::mutex> lock(mutex_);
181 size_t const id = next_callback_id_++;
182 callbacks_.emplace(id, std::move(callback));
183 return id;
184 }
185
186 mutable std::mutex mutex_;
187 std::map<size_t, error_handler_detail::error_callback_storage> callbacks_;
188 size_t next_callback_id_{ 0 };
189 size_t error_count_{ 0 };
190};
191
214template <typename Func>
216{
217public:
218 error_handled_task(Func func, std::shared_ptr<error_handler> handler, std::string description = "")
219 : func_(std::move(func)), handler_(std::move(handler)), description_(std::move(description))
220 {
221 }
222
223 void
225 {
226 try
227 {
228 func_();
229 }
230 catch (...)
231 {
232 if (handler_)
233 handler_->handle_error(task_error::capture(description_));
234 }
235 }
236
237private:
238 Func func_;
239 std::shared_ptr<error_handler> handler_;
240 std::string description_;
241};
242
255template <typename Func>
256auto
257make_error_handled_task(Func&& func, std::shared_ptr<error_handler> handler, std::string description = "")
258{
259 using function_type = std::decay_t<Func>;
260 return error_handled_task<function_type>(function_type(std::forward<Func>(func)), std::move(handler),
261 std::move(description));
262}
263
284template <typename T>
286{
287public:
288 explicit future_with_error_handler(std::future<T> future)
289 : future_(std::move(future)), error_callback_(nullptr), has_callback_(false)
290 {
291 }
292
297
308 auto
309 on_error(std::function<void(std::exception_ptr)> callback) -> future_with_error_handler&
310 {
311 error_callback_ = error_handler_detail::future_error_callback(std::move(callback));
312 has_callback_ = true;
313 return *this;
314 }
315
316 template <typename Callback, std::enable_if_t<!std::is_same_v<::threadschedule::detail::remove_cvref_t<Callback>,
317 std::function<void(std::exception_ptr)>>,
318 int> = 0>
319 auto
320 on_error(Callback&& callback) -> future_with_error_handler&
321 {
322 static_assert(std::is_invocable_r_v<void, Callback&, std::exception_ptr>,
323 "Error callback must be invocable with std::exception_ptr");
324 error_callback_
325 = ::threadschedule::detail::make_move_only_function<void(std::exception_ptr)>(std::forward<Callback>(callback));
326 has_callback_ = true;
327 return *this;
328 }
329
340 auto
341 get() -> T
342 {
343 try
344 {
345 if constexpr (std::is_void_v<T>)
346 future_.get();
347 else
348 return future_.get();
349 }
350 catch (...)
351 {
352 auto const original = std::current_exception();
353 if (has_callback_ && error_callback_)
354 {
355 try
356 {
357 error_callback_(original);
358 }
359 catch (...)
360 {
361 }
362 }
363 std::rethrow_exception(original);
364 }
365 }
366
372 void
373 wait() const
374 {
375 future_.wait();
376 }
377
385 template <typename Rep, typename Period>
386 auto
387 wait_for(std::chrono::duration<Rep, Period> const& timeout_duration) const
388 {
389 return future_.wait_for(timeout_duration);
390 }
391
399 template <typename Clock, typename duration>
400 auto
401 wait_until(std::chrono::time_point<Clock, duration> const& timeout_time) const
402 {
403 return future_.wait_until(timeout_time);
404 }
405
411 [[nodiscard]] auto
412 valid() const -> bool
413 {
414 return future_.valid();
415 }
416
417private:
418 std::future<T> future_;
420 bool has_callback_{ false };
421};
422
423} // namespace threadschedule::advanced
Callable wrapper that catches exceptions and routes them to an error_handler.
error_handled_task(Func func, std::shared_ptr< error_handler > handler, std::string description="")
Central registry and dispatcher for task-error callbacks.
auto remove_callback(size_t id) -> bool
Remove a single callback by its ID.
void reset_error_count()
Reset the cumulative error count to zero.
auto error_count() const -> size_t
Return the total number of errors handled since the last reset.
void clear_callbacks()
Remove all registered error callbacks.
void handle_error(task_error const &error)
Dispatch an error to all registered callbacks.
auto add_callback(Callback &&callback) -> size_t
auto has_callback(size_t id) const -> bool
Check whether a callback with the given ID is registered.
auto add_callback(error_callback callback) -> size_t
Register an error callback.
A move-only future wrapper that supports an error callback.
void wait() const
Block until the result is ready.
auto operator=(future_with_error_handler const &) -> future_with_error_handler &=delete
auto operator=(future_with_error_handler &&) -> future_with_error_handler &=default
auto wait_for(std::chrono::duration< Rep, Period > const &timeout_duration) const
Block until the result is ready or the timeout elapses.
auto on_error(std::function< void(std::exception_ptr)> callback) -> future_with_error_handler &
Attach an error callback.
auto get() -> T
Retrieve the result, invoking the error callback on failure.
auto on_error(Callback &&callback) -> future_with_error_handler &
future_with_error_handler(future_with_error_handler const &)=delete
future_with_error_handler(future_with_error_handler &&)=default
auto wait_until(std::chrono::time_point< Clock, duration > const &timeout_time) const
Block until the result is ready or the given time point is reached.
auto valid() const -> bool
Check whether the future refers to a shared state.
::threadschedule::detail::move_only_function< void(std::exception_ptr)> future_error_callback
::threadschedule::detail::copyable_function< void(task_error const &)> error_callback_storage
auto make_error_handled_task(Func &&func, std::shared_ptr< error_handler > handler, std::string description="")
Factory function that creates an error_handled_task with perfect forwarding.
auto make_copyable_function(Callable &&callable) -> copyable_function< Signature >
std::function< Signature > copyable_function
std::function< void(task_error const &)> error_callback
Callback signature for asynchronous task error reporting.
Captured information about a task failure.
static auto capture(std::string description={}) -> task_error
Capture failure context for the current exception state.