Skip to content

Commit e2de31c

Browse files
committed
Dispatch run(ex) task through target executor
run_awaitable_ex::await_suspend was resuming the child task via direct symmetric transfer, bypassing the target executor. This meant the first instructions of the task body ran inline on the caller's thread rather than on the executor's thread. For strands, this violated the serialization invariant from the very first instruction. Route the initial resumption through ex_.dispatch(h) so the executor decides where the task starts. Add a strand-based test that verifies running_in_this_thread() is true at the first instruction of the child task.
1 parent 55efc7f commit e2de31c

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

include/boost/capy/ex/run.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ struct [[nodiscard]] run_awaitable_ex
241241
env_.frame_allocator = caller_env->frame_allocator;
242242

243243
p.set_environment(&env_);
244-
return h;
244+
return ex_.dispatch(h);
245245
}
246246

247247
// Non-copyable

test/unit/ex/run.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include "test/unit/custom_task.hpp"
2020
#include "test/unit/test_helpers.hpp"
2121

22+
#include <boost/capy/ex/strand.hpp>
23+
#include <boost/capy/ex/thread_pool.hpp>
24+
25+
#include <latch>
2226
#include <memory>
2327

2428
namespace boost {
@@ -468,6 +472,34 @@ struct run_test
468472
BOOST_TEST(result);
469473
}
470474

475+
void
476+
testRunExStrandFirstInstruction()
477+
{
478+
// Verify that the first instructions of a task passed
479+
// to run(strand) execute inside the strand's serialization,
480+
// not inline on an unprotected thread.
481+
thread_pool pool(2, "str-pool-");
482+
strand s(pool.get_executor());
483+
bool inside_strand = false;
484+
std::latch done(1);
485+
486+
auto inner = [&]() -> task<void> {
487+
inside_strand = s.running_in_this_thread();
488+
co_return;
489+
};
490+
491+
auto outer = [&]() -> task<void> {
492+
co_await capy::run(s)(inner());
493+
};
494+
495+
run_async(pool.get_executor(),
496+
[&]() { done.count_down(); })(outer());
497+
done.wait();
498+
499+
BOOST_TEST(inside_strand);
500+
pool.join();
501+
}
502+
471503
void
472504
run()
473505
{
@@ -490,6 +522,7 @@ struct run_test
490522
testStopTokenOverrideOuterStopped();
491523
testAllocatorPropagation();
492524
testAllocatorPropagationThroughRun();
525+
testRunExStrandFirstInstruction();
493526
}
494527
};
495528

0 commit comments

Comments
 (0)