DiffEq - Modern C++ ODE Integration Library 1.0.0
High-performance C++ library for solving ODEs with async signal processing
Loading...
Searching...
No Matches
timeout_integrator.hpp
1#pragma once
2
3#include <future>
4#include <chrono>
5#include <stdexcept>
6#include <memory>
7#include <functional>
8#include "concepts.hpp"
9#include "abstract_integrator.hpp"
10
11namespace diffeq::core {
12
16class IntegrationTimeoutException : public std::runtime_error {
17public:
18 explicit IntegrationTimeoutException(const std::string& message)
19 : std::runtime_error(message) {}
20
21 explicit IntegrationTimeoutException(std::chrono::milliseconds timeout_duration)
22 : std::runtime_error("Integration timed out after " +
23 std::to_string(timeout_duration.count()) + "ms") {}
24};
25
30 std::chrono::milliseconds timeout_duration{5000}; // Default 5 seconds
31 bool throw_on_timeout{true}; // Throw exception vs return false
32 bool enable_progress_callback{false}; // Enable progress monitoring
33 std::chrono::milliseconds progress_interval{100}; // Progress callback interval
34
35 // Optional progress callback: (current_time, end_time, elapsed_time) -> should_continue
36 std::function<bool(double, double, std::chrono::milliseconds)> progress_callback;
37};
38
43 bool completed{false}; // Whether integration completed successfully
44 std::chrono::milliseconds elapsed_time{0}; // Total elapsed time
45 double final_time{0.0}; // Final integration time reached
46 std::string error_message; // Error message if failed
47
48 // Success indicators
49 bool is_success() const { return completed && error_message.empty(); }
50 bool is_timeout() const { return !completed && error_message.find("timeout") != std::string::npos; }
51 bool is_error() const { return !completed && !is_timeout(); }
52};
53
69template<typename Integrator>
71public:
72 using integrator_type = Integrator;
73 using state_type = typename Integrator::state_type;
74 using time_type = typename Integrator::time_type;
75
82 : integrator_(std::move(integrator)), config_(std::move(config)) {}
83
89 template<typename... Args>
90 explicit TimeoutIntegrator(TimeoutConfig config, Args&&... args)
91 : integrator_(std::forward<Args>(args)...), config_(std::move(config)) {}
92
100 IntegrationResult integrate_with_timeout(state_type& state, time_type dt, time_type end_time) {
101 const auto start_time = std::chrono::high_resolution_clock::now();
102 IntegrationResult result;
103 result.final_time = integrator_.current_time();
104
105 try {
106 // Launch integration in async task
107 auto future = std::async(std::launch::async, [this, &state, dt, end_time]() {
108 integrator_.integrate(state, dt, end_time);
109 });
110
111 // Wait with timeout
112 if (config_.enable_progress_callback && config_.progress_callback) {
113 // Monitor progress with callbacks
114 result = wait_with_progress_monitoring(future, end_time, start_time);
115 } else {
116 // Simple timeout wait
117 if (future.wait_for(config_.timeout_duration) == std::future_status::timeout) {
118 result.completed = false;
119 result.error_message = "Integration timed out after " +
120 std::to_string(config_.timeout_duration.count()) + "ms";
121 } else {
122 future.get(); // Check for exceptions
123 result.completed = true;
124 }
125 }
126
127 result.final_time = integrator_.current_time();
128
129 } catch (const std::exception& e) {
130 result.completed = false;
131 result.error_message = "Integration failed: " + std::string(e.what());
132 }
133
134 // Calculate elapsed time
135 const auto end_clock_time = std::chrono::high_resolution_clock::now();
136 result.elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(
137 end_clock_time - start_time);
138
139 // Handle timeout according to configuration
140 if (!result.completed && config_.throw_on_timeout && result.is_timeout()) {
141 throw IntegrationTimeoutException(result.elapsed_time);
142 }
143
144 return result;
145 }
146
155 bool integrate_with_timeout_simple(state_type& state, time_type dt, time_type end_time,
156 std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) {
157 auto old_config = config_;
158 if (timeout.count() > 0) {
159 config_.timeout_duration = timeout;
160 }
161 config_.throw_on_timeout = false;
162
163 auto result = integrate_with_timeout(state, dt, end_time);
164 config_ = old_config; // Restore original config
165
166 return result.completed;
167 }
168
173 Integrator& integrator() { return integrator_; }
174 const Integrator& integrator() const { return integrator_; }
175
180 TimeoutConfig& config() { return config_; }
181 const TimeoutConfig& config() const { return config_; }
182
183private:
184 Integrator integrator_;
185 TimeoutConfig config_;
186
190 IntegrationResult wait_with_progress_monitoring(std::future<void>& future, time_type end_time,
191 std::chrono::high_resolution_clock::time_point start_time) {
192 IntegrationResult result;
193
194 auto last_progress_time = start_time;
195
196 while (true) {
197 // Check if integration completed
198 if (future.wait_for(config_.progress_interval) == std::future_status::ready) {
199 future.get(); // Check for exceptions
200 result.completed = true;
201 break;
202 }
203
204 // Check total timeout
205 auto current_time = std::chrono::high_resolution_clock::now();
206 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time);
207
208 if (elapsed >= config_.timeout_duration) {
209 result.completed = false;
210 result.error_message = "Integration timed out after " +
211 std::to_string(elapsed.count()) + "ms";
212 break;
213 }
214
215 // Call progress callback
216 if (config_.progress_callback) {
217 bool should_continue = config_.progress_callback(
218 static_cast<double>(integrator_.current_time()),
219 static_cast<double>(end_time),
220 elapsed
221 );
222
223 if (!should_continue) {
224 result.completed = false;
225 result.error_message = "Integration cancelled by progress callback";
226 break;
227 }
228 }
229
230 last_progress_time = current_time;
231 }
232
233 return result;
234 }
235};
236
244template<typename Integrator>
245auto make_timeout_integrator(Integrator integrator, TimeoutConfig config = {}) {
246 return TimeoutIntegrator<Integrator>(std::move(integrator), std::move(config));
247}
248
259template<typename Integrator, typename State>
260bool integrate_with_timeout(Integrator& integrator, State& state,
261 typename Integrator::time_type dt,
262 typename Integrator::time_type end_time,
263 std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) {
264 // Create a copy of the state to avoid race conditions
265 State state_copy = state;
266
267 auto future = std::async(std::launch::async, [&integrator, &state_copy, dt, end_time]() {
268 integrator.integrate(state_copy, dt, end_time);
269 });
270
271 auto status = future.wait_for(timeout);
272
273 if (status == std::future_status::ready) {
274 try {
275 future.get(); // Check for exceptions
276 state = state_copy; // Update original state only if successful
277 return true;
278 } catch (...) {
279 return false;
280 }
281 } else {
282 return false; // Timed out
283 }
284}
285
286} // namespace diffeq::core
Exception thrown when integration times out.
Timeout-enabled integration wrapper.
bool integrate_with_timeout_simple(state_type &state, time_type dt, time_type end_time, std::chrono::milliseconds timeout=std::chrono::milliseconds{0})
Simple timeout integration (backwards compatibility)
IntegrationResult integrate_with_timeout(state_type &state, time_type dt, time_type end_time)
Perform timeout-protected integration.
TimeoutIntegrator(Integrator integrator, TimeoutConfig config={})
Construct timeout integrator with an existing integrator.
TimeoutIntegrator(TimeoutConfig config, Args &&... args)
Construct timeout integrator with integrator parameters.
Integrator & integrator()
Access the underlying integrator.
TimeoutConfig & config()
Access timeout configuration.
Result of a timeout-enabled integration.
Configuration for timeout-enabled integration.
Timeout configuration for integration protection.