Skip to content

Commit ac49ba7

Browse files
Fixed race condition in executor waitJobResponse
1 parent a63866b commit ac49ba7

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

include/la/avdecc/executor.hpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <chrono>
3838
#include <stdexcept>
3939
#include <exception>
40+
#include <atomic>
4041

4142
namespace 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

tests/src/executor_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ TEST(ExecutorManager, WaitJobResponseHandlerThrowsWithReturn)
222222

223223
TEST(ExecutorManager, WaitJobResponseFromExecutorThread)
224224
{
225-
auto constexpr ExecutorName = "WaitTestSameThread";
225+
static auto constexpr ExecutorName = "WaitTestSameThread";
226226
auto jobExecuted = false;
227227

228228
auto executorWrapper = la::avdecc::ExecutorManager::getInstance().registerExecutor(ExecutorName, la::avdecc::ExecutorWithDispatchQueue::create());

0 commit comments

Comments
 (0)