diff --git a/doc/reference/process_handle.adoc b/doc/reference/process_handle.adoc index f6905a2a..603c8309 100644 --- a/doc/reference/process_handle.adoc +++ b/doc/reference/process_handle.adoc @@ -100,9 +100,10 @@ struct basic_process_handle // Check if the process handle is referring to an existing process. bool is_open() const; - // Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler. - template> - auto async_wait(WaitHandler &&handler = net::default_completion_token_t()); + auto async_wait(native_exit_code_type &exit_status, WaitHandler &&handler = net::default_completion_token_t()); }; ---- \ No newline at end of file diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 8af291d9..bb2eb0d7 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -286,24 +286,21 @@ struct basic_process_handle_fd { net::posix::basic_descriptor &descriptor; pid_type pid_; - + native_exit_code_type & exit_code; template void operator()(Self &&self) { self.reset_cancellation_state(asio::enable_total_cancellation()); error_code ec; - native_exit_code_type exit_code{}; int wait_res = -1; if (pid_ <= 0) // error, complete early - ec = net::error::bad_descriptor; - else + BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::bad_descriptor); + else if (process_is_running(exit_code)) { wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) ec = get_last_error(); } - - if (!ec && (wait_res == 0)) { descriptor.async_wait(net::posix::descriptor_base::wait_read, std::move(self)); @@ -313,38 +310,39 @@ struct basic_process_handle_fd struct completer { error_code ec; - native_exit_code_type code; typename std::decay::type self; void operator()() { - self.complete(ec, code); + self.complete(ec); } }; - net::post(descriptor.get_executor(), completer{ec, exit_code, std::move(self)}); + net::dispatch( + net::get_associated_immediate_executor(self, descriptor.get_executor()), + completer{ec, std::move(self)}); } template void operator()(Self &&self, error_code ec, int = 0) { - native_exit_code_type exit_code{}; - if (!ec) + if (!ec && process_is_running(exit_code)) if (::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); - std::move(self).complete(ec, exit_code); + std::move(self).complete(ec); } }; public: - template> - auto async_wait(WaitHandler &&handler = net::default_completion_token_t()) - -> decltype(net::async_compose( - async_wait_op_{descriptor_, pid_}, handler, descriptor_)) + auto async_wait(native_exit_code_type & exit_code, + WaitHandler &&handler = net::default_completion_token_t()) + -> decltype(net::async_compose( + async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_)) { - return net::async_compose( - async_wait_op_{descriptor_, pid_}, handler, descriptor_); + return net::async_compose( + async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_); } }; diff --git a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp index 73c0e2be..a2112b8a 100644 --- a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp @@ -332,6 +332,7 @@ struct basic_process_handle_fd_or_signal int dummy; #endif pid_type pid_; + native_exit_code_type & exit_code; bool needs_post = true; template @@ -344,11 +345,10 @@ struct basic_process_handle_fd_or_signal template void operator()(Self &&self, error_code ec, int = 0) { - native_exit_code_type exit_code{}; int wait_res = -1; if (pid_ <= 0) // error, complete early ec = net::error::bad_descriptor; - else + else if (process_is_running(exit_code)) { wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) @@ -391,18 +391,19 @@ struct basic_process_handle_fd_or_signal template void operator()(Self &&self, native_exit_code_type code, error_code ec) { - self.complete(ec, code); + self.complete(ec); } }; public: - template> - auto async_wait(WaitHandler &&handler = net::default_completion_token_t()) - -> decltype(net::async_compose( - async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_)) + auto async_wait(native_exit_code_type & exit_code, + WaitHandler &&handler = net::default_completion_token_t()) + -> decltype(net::async_compose( + async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_)) { - return net::async_compose( - async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_); + return net::async_compose( + async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_); } }; } diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 098ebb7b..3f8a2605 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -308,7 +308,8 @@ struct basic_process_handle_signal net::basic_signal_set &handle; pid_type pid_; - + native_exit_code_type & exit_code; + template void operator()(Self &&self) { @@ -326,12 +327,11 @@ struct basic_process_handle_signal == net::cancellation_type::none) ec.clear(); - native_exit_code_type exit_code = -1; int wait_res = -1; if (pid_ <= 0) // error, complete early ec = net::error::bad_descriptor; - else if (!ec) + else if (!ec && process_is_running(exit_code)) { wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) @@ -345,7 +345,7 @@ struct basic_process_handle_signal } const auto exec = self.get_executor(); - net::dispatch(exec, net::append(std::move(self), exit_code, ec)); + net::dispatch(exec, net::append(std::move(self), ec)); } #else signal_set_dummy_ dummy_; @@ -360,20 +360,21 @@ struct basic_process_handle_signal } #endif template - void operator()(Self &&self, native_exit_code_type code, error_code ec) + void operator()(Self &&self, error_code ec) { - self.complete(ec, code); + self.complete(ec); } }; public: - template> - auto async_wait(WaitHandler &&handler = net::default_completion_token_t()) - -> decltype(net::async_compose( - async_wait_op_{signal_set_, pid_}, handler, signal_set_)) + auto async_wait(native_exit_code_type & exit_code, + WaitHandler &&handler = net::default_completion_token_t()) + -> decltype(net::async_compose( + async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_)) { - return net::async_compose( - async_wait_op_{signal_set_, pid_}, handler, signal_set_); + return net::async_compose( + async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_); } }; diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index 3aa4142b..9e1518a8 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -275,7 +275,7 @@ struct basic_process_handle_win struct async_wait_op_ { handle_type &handle; - + native_exit_code_type & exit_code; template void operator()(Self &&self) { @@ -296,24 +296,24 @@ struct basic_process_handle_win template void operator()(Self &&self, error_code ec) { - native_exit_code_type exit_code{}; if (ec == asio::error::operation_aborted && !self.get_cancellation_state().cancelled()) return handle.async_wait(std::move(self)); - if (!ec) + if (!ec && process_is_running(exit_code)) // exit_code could be set by another call to wait. detail::get_exit_code_(handle.native_handle(), exit_code, ec); - std::move(self).complete(ec, exit_code); + std::move(self).complete(ec); } }; public: - template> - auto async_wait(WaitHandler &&handler = net::default_completion_token_t()) - -> decltype(net::async_compose( - async_wait_op_{handle_}, handler, handle_)) + auto async_wait(native_exit_code_type & exit_code, + WaitHandler &&handler = net::default_completion_token_t()) + -> decltype(net::async_compose( + async_wait_op_{handle_, exit_code}, handler, handle_)) { - return net::async_compose( - async_wait_op_{handle_}, handler, handle_ + return net::async_compose( + async_wait_op_{handle_, exit_code}, handler, handle_ ); } }; diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index f30bc469..3ef167aa 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -20,10 +20,12 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include +#include #include #include #else #include +#include #include #include #endif @@ -359,23 +361,22 @@ private: } }; - net::post(handle.get_executor(), - completer{static_cast(res), std::move(self)}); + net::dispatch( + net::get_associated_immediate_executor(handle, handle.get_executor()), + completer{static_cast(res), std::move(self)}); } else - handle.async_wait(std::move(self)); + handle.async_wait(res, std::move(self)); } template - void operator()(Self && self, error_code ec, native_exit_code_type code) + void operator()(Self && self, error_code ec) { - if (!ec && process_is_running(code)) - handle.async_wait(std::move(self)); + if (!ec && process_is_running(res)) + handle.async_wait(res, std::move(self)); else { - if (!ec) - res = code; - std::move(self).complete(ec, evaluate_exit_code(code)); + std::move(self).complete(ec, evaluate_exit_code(res)); } } }; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index ea47eef7..cd814ae4 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -768,6 +768,26 @@ BOOST_AUTO_TEST_CASE(no_zombie) BOOST_CHECK_EQUAL(errno, ECHILD); } +BOOST_AUTO_TEST_CASE(async_terminate_code) +{ + asio::io_context ctx; + using boost::unit_test::framework::master_test_suite; + const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]); + + bpv::process proc(ctx, pth, {"sleep", "1000"}); + + proc.async_wait([&](boost::system::error_code ec, int code) + { + BOOST_CHECK_MESSAGE(!ec, ec.what()); + BOOST_CHECK_EQUAL(code, SIGKILL); + BOOST_CHECK(!proc.running()); + }); + + asio::post(ctx, [&]{proc.terminate();}); + + ctx.run(); +} + #endif