3737#include < chrono>
3838#include < stdexcept>
3939#include < exception>
40+ #include < atomic>
4041
4142namespace la
4243{
@@ -267,19 +268,30 @@ class ExecutorManager
267268 throw std::invalid_argument (" Executor not found" );
268269 }
269270 auto responsePromise = std::promise<typename Traits::result_type>{};
271+ auto shouldIgnorePromise = std::make_shared<std::atomic<bool >>(false );
270272 pushJob (name,
271- [&responsePromise, h = std::forward<CallableType>(handler)]() mutable
273+ [&responsePromise, shouldIgnorePromise, h = std::forward<CallableType>(handler)]() mutable
272274 {
273275 try
274276 {
277+ // Always run the handler
275278 if constexpr (std::is_same_v<typename Traits::result_type, void >)
276279 {
277280 h ();
278- responsePromise.set_value ();
281+ // Only set value if not timed out
282+ if (!shouldIgnorePromise->exchange (true ))
283+ {
284+ responsePromise.set_value ();
285+ }
279286 }
280287 else
281288 {
282- responsePromise.set_value (h ());
289+ auto const result = h ();
290+ // Only set value if not timed out
291+ if (!shouldIgnorePromise->exchange (true ))
292+ {
293+ responsePromise.set_value (result);
294+ }
283295 }
284296 }
285297 catch (...)
@@ -293,7 +305,13 @@ class ExecutorManager
293305 auto status = fut.wait_for (timeout.value ());
294306 if (status == std::future_status::timeout)
295307 {
296- throw std::runtime_error (" Timeout waiting for job response" );
308+ // Job has timed out, mark the promise as to be ignored
309+ auto const alreadyProcessed = shouldIgnorePromise->exchange (true );
310+ // But in case the job just completed, we must not throw
311+ if (!alreadyProcessed)
312+ {
313+ throw std::runtime_error (" Timeout waiting for job response" );
314+ }
297315 }
298316 }
299317 // fut.get() waits for the job to complete and propagates std::invalid_argument if the handler threw an exception
0 commit comments