diff --git a/src/ex/thread_pool.cpp b/src/ex/thread_pool.cpp index a5a0c1e76..e361c183c 100644 --- a/src/ex/thread_pool.cpp +++ b/src/ex/thread_pool.cpp @@ -110,6 +110,15 @@ class thread_pool::impl cv_.notify_one(); } + void + join() noexcept + { + stop(); + for(auto& t : threads_) + if(t.joinable()) + t.join(); + } + void stop() noexcept { @@ -160,6 +169,7 @@ class thread_pool::impl thread_pool:: ~thread_pool() { + impl_->join(); shutdown(); destroy(); delete impl_; diff --git a/test/unit/when_all.cpp b/test/unit/when_all.cpp index a7b22614e..0ad92599c 100644 --- a/test/unit/when_all.cpp +++ b/test/unit/when_all.cpp @@ -12,12 +12,15 @@ #include #include +#include +#include #include #include #include "test_helpers.hpp" #include +#include #include #include #include @@ -1047,5 +1050,85 @@ TEST_SUITE( when_all_io_awaitable_test, "boost.capy.when_all_io_awaitable"); +struct when_all_strand_test +{ + // Regression for #131: executor_ref::dispatch() formerly + // returned void, discarding the symmetric transfer handle + // from strand::dispatch(). This caused when_all child + // runners to never resume, deadlocking the caller. + void + testStrandWhenAll() + { + thread_pool pool(2); + strand s{pool.get_executor()}; + std::latch done(1); + bool completed = false; + + auto outer = [&]() -> task<> { + co_await when_all( + []() -> task<> { co_return; }(), + []() -> task<> { co_return; }() + ); + }; + + run_async(s, + [&](auto&&...) { + completed = true; + done.count_down(); + }, + [&](auto) { + done.count_down(); + } + )(outer()); + + done.wait(); + BOOST_TEST(completed); + } + + // Verify strand + when_all propagates values correctly + void + testStrandWhenAllWithValues() + { + thread_pool pool(2); + strand s{pool.get_executor()}; + std::latch done(1); + bool completed = false; + int result = 0; + + auto outer = [&]() -> task> { + co_return co_await when_all( + returns_int(10), + returns_int(20)); + }; + + run_async(s, + [&](std::tuple t) { + auto [a, b] = t; + completed = true; + result = a + b; + done.count_down(); + }, + [&](auto) { + done.count_down(); + } + )(outer()); + + done.wait(); + BOOST_TEST(completed); + BOOST_TEST_EQ(result, 30); + } + + void + run() + { + testStrandWhenAll(); + testStrandWhenAllWithValues(); + } +}; + +TEST_SUITE( + when_all_strand_test, + "boost.capy.when_all_strand"); + } // capy } // boost