ThreadSchedule 1.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
error_handler.hpp
1#pragma once
2
3#include <chrono>
4#include <exception>
5#include <functional>
6#include <future>
7#include <memory>
8#include <mutex>
9#include <string>
10#include <thread>
11#include <vector>
12
13namespace threadschedule
14{
15
20{
21 std::exception_ptr exception;
22 std::string task_description;
23 std::thread::id thread_id;
24 std::chrono::steady_clock::time_point timestamp;
25
29 [[nodiscard]] auto what() const -> std::string
30 {
31 try
32 {
33 if (exception)
34 {
35 std::rethrow_exception(exception);
36 }
37 }
38 catch (std::exception const& e)
39 {
40 return e.what();
41 }
42 catch (...)
43 {
44 return "Unknown exception";
45 }
46 return "No exception";
47 }
48
52 void rethrow() const
53 {
54 if (exception)
55 {
56 std::rethrow_exception(exception);
57 }
58 }
59};
60
64using ErrorCallback = std::function<void(TaskError const&)>;
65
73{
74 public:
80 auto add_callback(ErrorCallback callback) -> size_t
81 {
82 std::lock_guard<std::mutex> lock(mutex_);
83 callbacks_.push_back(std::move(callback));
84 return callbacks_.size() - 1;
85 }
86
91 {
92 std::lock_guard<std::mutex> lock(mutex_);
93 callbacks_.clear();
94 }
95
100 void handle_error(TaskError const& error)
101 {
102 std::lock_guard<std::mutex> lock(mutex_);
103 error_count_++;
104
105 for (auto const& callback : callbacks_)
106 {
107 try
108 {
109 callback(error);
110 }
111 catch (...)
112 {
113 // Error handlers should not throw, but we catch just in case
114 }
115 }
116 }
117
121 [[nodiscard]] auto error_count() const -> size_t
122 {
123 std::lock_guard<std::mutex> lock(mutex_);
124 return error_count_;
125 }
126
131 {
132 std::lock_guard<std::mutex> lock(mutex_);
133 error_count_ = 0;
134 }
135
136 private:
137 mutable std::mutex mutex_;
138 std::vector<ErrorCallback> callbacks_;
139 size_t error_count_{0};
140};
141
147template <typename Func>
148class ErrorHandledTask
149{
150 public:
151 ErrorHandledTask(Func&& func, std::shared_ptr<ErrorHandler> handler, std::string description = "")
152 : func_(std::forward<Func>(func)), handler_(std::move(handler)), description_(std::move(description))
153 {
154 }
155
156 void operator()()
157 {
158 try
159 {
160 func_();
161 }
162 catch (...)
163 {
164 if (handler_)
165 {
166 TaskError error;
167 error.exception = std::current_exception();
168 error.task_description = description_;
169 error.thread_id = std::this_thread::get_id();
170 error.timestamp = std::chrono::steady_clock::now();
171
172 handler_->handle_error(error);
173 }
174 }
175 }
176
177 private:
178 Func func_;
179 std::shared_ptr<ErrorHandler> handler_;
180 std::string description_;
181};
182
186template <typename Func>
187auto make_error_handled_task(Func&& func, std::shared_ptr<ErrorHandler> handler, std::string description = "")
188{
189 return ErrorHandledTask<Func>(std::forward<Func>(func), std::move(handler), std::move(description));
190}
191
197template <typename T>
198class FutureWithErrorHandler
199{
200 public:
201 explicit FutureWithErrorHandler(std::future<T> future)
202 : future_(std::move(future)), error_callback_(nullptr), has_callback_(false)
203 {
204 }
205
206 FutureWithErrorHandler(FutureWithErrorHandler const&) = delete;
207 auto operator=(FutureWithErrorHandler const&) -> FutureWithErrorHandler& = delete;
208 FutureWithErrorHandler(FutureWithErrorHandler&&) = default;
209 auto operator=(FutureWithErrorHandler&&) -> FutureWithErrorHandler& = default;
210
216 auto on_error(std::function<void(std::exception_ptr)> callback) -> FutureWithErrorHandler&
217 {
218 error_callback_ = std::move(callback);
219 has_callback_ = true;
220 return *this;
221 }
222
226 auto get() -> T
227 {
228 try
229 {
230 return future_.get();
231 }
232 catch (...)
233 {
234 if (has_callback_ && error_callback_)
235 {
236 error_callback_(std::current_exception());
237 }
238 throw;
239 }
240 }
241
245 void wait() const
246 {
247 future_.wait();
248 }
249
253 template <typename Rep, typename Period>
254 auto wait_for(std::chrono::duration<Rep, Period> const& timeout_duration) const
255 {
256 return future_.wait_for(timeout_duration);
257 }
258
262 template <typename Clock, typename Duration>
263 auto wait_until(std::chrono::time_point<Clock, Duration> const& timeout_time) const
264 {
265 return future_.wait_until(timeout_time);
266 }
267
271 [[nodiscard]] auto valid() const -> bool
272 {
273 return future_.valid();
274 }
275
276 private:
277 std::future<T> future_;
278 std::function<void(std::exception_ptr)> error_callback_;
279 bool has_callback_{false};
280};
281
285template <>
286class FutureWithErrorHandler<void>
287{
288 public:
289 explicit FutureWithErrorHandler(std::future<void> future) : future_(std::move(future)), error_callback_(nullptr)
290 {
291 }
292
293 FutureWithErrorHandler(FutureWithErrorHandler const&) = delete;
294 auto operator=(FutureWithErrorHandler const&) -> FutureWithErrorHandler& = delete;
295 FutureWithErrorHandler(FutureWithErrorHandler&&) = default;
296 auto operator=(FutureWithErrorHandler&&) -> FutureWithErrorHandler& = default;
297
298 auto on_error(std::function<void(std::exception_ptr)> callback) -> FutureWithErrorHandler&
299 {
300 error_callback_ = std::move(callback);
301 has_callback_ = true;
302 return *this;
303 }
304
305 void get()
306 {
307 try
308 {
309 future_.get();
310 }
311 catch (...)
312 {
313 if (has_callback_ && error_callback_)
314 {
315 error_callback_(std::current_exception());
316 }
317 throw;
318 }
319 }
320
321 void wait() const
322 {
323 future_.wait();
324 }
325
326 template <typename Rep, typename Period>
327 auto wait_for(std::chrono::duration<Rep, Period> const& timeout_duration) const
328 {
329 return future_.wait_for(timeout_duration);
330 }
331
332 template <typename Clock, typename Duration>
333 auto wait_until(std::chrono::time_point<Clock, Duration> const& timeout_time) const
334 {
335 return future_.wait_until(timeout_time);
336 }
337
338 [[nodiscard]] auto valid() const -> bool
339 {
340 return future_.valid();
341 }
342
343 private:
344 std::future<void> future_;
345 std::function<void(std::exception_ptr)> error_callback_;
346 bool has_callback_{};
347};
348
349} // namespace threadschedule
Task wrapper that provides error handling.
Global error handler for thread pool tasks.
void reset_error_count()
Reset error count.
void handle_error(TaskError const &error)
Handle an exception from a task.
void clear_callbacks()
Remove all error callbacks.
auto error_count() const -> size_t
Get total number of errors handled.
auto add_callback(ErrorCallback callback) -> size_t
Add an error callback.
auto on_error(std::function< void(std::exception_ptr)> callback) -> FutureWithErrorHandler &
Attach an error callback.
auto wait_until(std::chrono::time_point< Clock, Duration > const &timeout_time) const
Wait until a specific time point.
auto get() -> T
Get the result, calling error callback if exception is thrown.
auto valid() const -> bool
Check if the future is valid.
auto wait_for(std::chrono::duration< Rep, Period > const &timeout_duration) const
Wait for the future with timeout.
void wait() const
Wait for the future to complete.
Information about a task exception.
void rethrow() const
Rethrow the exception.
auto what() const -> std::string
Get the exception message if it's a std::exception.