From 13045b6017e13b5884b4a95b6a80f485b42f1edc Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Tue, 23 Aug 2022 10:34:56 +1000 Subject: [PATCH 1/3] Catch exceptions and rethrow outside of spawned thread. --- asio/include/asio/impl/spawn.hpp | 48 +++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/asio/include/asio/impl/spawn.hpp b/asio/include/asio/impl/spawn.hpp index 1d0a8a1727..6bea9d9e6e 100644 --- a/asio/include/asio/impl/spawn.hpp +++ b/asio/include/asio/impl/spawn.hpp @@ -45,6 +45,14 @@ namespace asio { namespace detail { +#if !defined(ASIO_NO_EXCEPTIONS) +static void spawned_thread_rethrow(void* ex) +{ + if (*static_cast(ex)) + rethrow_exception(*static_cast(ex)); +} +#endif // !defined(ASIO_NO_EXCEPTIONS) + #if defined(ASIO_HAS_BOOST_COROUTINE) // Spawned thread implementation using Boost.Coroutine. @@ -139,8 +147,24 @@ class spawned_coroutine_thread : public spawned_thread_base *spawned_thread_out_ = &spawned_thread; spawned_thread_out_ = 0; spawned_thread.suspend(); - function(&spawned_thread); - spawned_thread.suspend(); +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + function(&spawned_thread); + spawned_thread.suspend(); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (const boost::coroutines::detail::forced_unwind&) + { + throw; + } + catch (...) + { + exception_ptr ex = current_exception(); + spawned_thread.suspend_with(spawned_thread_rethrow, &ex); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) } private: @@ -249,8 +273,24 @@ class spawned_fiber_thread : public spawned_thread_base *spawned_thread_out_ = &spawned_thread; spawned_thread_out_ = 0; spawned_thread.suspend(); - function(&spawned_thread); - spawned_thread.suspend(); +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + function(&spawned_thread); + spawned_thread.suspend(); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (const boost::context::detail::forced_unwind&) + { + throw; + } + catch (...) + { + exception_ptr ex = current_exception(); + spawned_thread.suspend_with(spawned_thread_rethrow, &ex); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) return {}; } From 96e1a95449664d426afeacd010454b9750a34de4 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Thu, 25 Aug 2022 10:29:23 +1000 Subject: [PATCH 2/3] Allow a terminal-state spawned thread to run to completion when destroyed. --- asio/include/asio/impl/spawn.hpp | 11 +++++++++-- asio/include/asio/spawn.hpp | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/asio/include/asio/impl/spawn.hpp b/asio/include/asio/impl/spawn.hpp index 6bea9d9e6e..2813ef36e1 100644 --- a/asio/include/asio/impl/spawn.hpp +++ b/asio/include/asio/impl/spawn.hpp @@ -125,6 +125,8 @@ class spawned_coroutine_thread : public spawned_thread_base { callee_type callee; callee.swap(callee_); + if (terminal_) + callee(); } private: @@ -152,6 +154,7 @@ class spawned_coroutine_thread : public spawned_thread_base #endif // !defined(ASIO_NO_EXCEPTIONS) { function(&spawned_thread); + spawned_thread.terminal_ = true; spawned_thread.suspend(); } #if !defined(ASIO_NO_EXCEPTIONS) @@ -162,6 +165,7 @@ class spawned_coroutine_thread : public spawned_thread_base catch (...) { exception_ptr ex = current_exception(); + spawned_thread.terminal_ = true; spawned_thread.suspend_with(spawned_thread_rethrow, &ex); } #endif // !defined(ASIO_NO_EXCEPTIONS) @@ -249,7 +253,8 @@ class spawned_fiber_thread : public spawned_thread_base void destroy() { fiber_type callee = ASIO_MOVE_CAST(fiber_type)(callee_); - (void)callee; + if (terminal_) + fiber_type(ASIO_MOVE_CAST(fiber_type)(callee)).resume(); } private: @@ -278,6 +283,7 @@ class spawned_fiber_thread : public spawned_thread_base #endif // !defined(ASIO_NO_EXCEPTIONS) { function(&spawned_thread); + spawned_thread.terminal_ = true; spawned_thread.suspend(); } #if !defined(ASIO_NO_EXCEPTIONS) @@ -288,10 +294,11 @@ class spawned_fiber_thread : public spawned_thread_base catch (...) { exception_ptr ex = current_exception(); + spawned_thread.terminal_ = true; spawned_thread.suspend_with(spawned_thread_rethrow, &ex); } #endif // !defined(ASIO_NO_EXCEPTIONS) - return {}; + return ASIO_MOVE_CAST(fiber_type)(spawned_thread.caller_); } private: diff --git a/asio/include/asio/spawn.hpp b/asio/include/asio/spawn.hpp index 5bd56c074b..5495cb396e 100644 --- a/asio/include/asio/spawn.hpp +++ b/asio/include/asio/spawn.hpp @@ -42,7 +42,8 @@ class spawned_thread_base spawned_thread_base() : owner_(0), has_context_switched_(false), - throw_if_cancelled_(false) + throw_if_cancelled_(false), + terminal_(false) { } @@ -130,6 +131,7 @@ class spawned_thread_base asio::cancellation_state cancellation_state_; bool has_context_switched_; bool throw_if_cancelled_; + bool terminal_; private: // Disallow copying and assignment. From 8406f4fe64913debd8e8d52aab9344efef03b49c Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Thu, 25 Aug 2022 10:31:57 +1000 Subject: [PATCH 3/3] Re-throw exception from top-level spawn()-ed function. --- asio/src/examples/cpp11/spawn/echo_server.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/asio/src/examples/cpp11/spawn/echo_server.cpp b/asio/src/examples/cpp11/spawn/echo_server.cpp index 39e3d04c2f..012718feb4 100644 --- a/asio/src/examples/cpp11/spawn/echo_server.cpp +++ b/asio/src/examples/cpp11/spawn/echo_server.cpp @@ -99,7 +99,12 @@ int main(int argc, char* argv[]) std::make_shared(io_context, std::move(socket))->go(); } } - }, asio::detached); + }, + [](std::exception_ptr e) + { + if (e) + std::rethrow_exception(e); + }); io_context.run(); }