123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- // Copyright (c) 2017 Klemens D. Morgenstern
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
- #define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
- #include <boost/asio/dispatch.hpp>
- #include <boost/asio/post.hpp>
- #include <boost/asio/signal_set.hpp>
- #include <boost/asio/strand.hpp>
- #include <boost/optional.hpp>
- #include <signal.h>
- #include <functional>
- #include <sys/wait.h>
- namespace boost { namespace process { namespace detail { namespace posix {
- class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
- {
- boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
- boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
- std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
- inline void _handle_signal(const boost::system::error_code & ec);
- public:
- sigchld_service(boost::asio::io_context & io_context)
- : boost::asio::detail::service_base<sigchld_service>(io_context)
- {
- }
- template <typename SignalHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(SignalHandler,
- void (int, std::error_code))
- async_wait(::pid_t pid, SignalHandler && handler)
- {
- boost::asio::async_completion<
- SignalHandler, void(boost::system::error_code)> init{handler};
- auto & h = init.completion_handler;
- boost::asio::dispatch(
- _strand,
- [this, pid, h]
- {
- //check if the child actually is running first
- int status;
- auto pid_res = ::waitpid(pid, &status, WNOHANG);
- if (pid_res < 0)
- h(-1, get_last_error());
- else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
- h(status, {}); //successfully exited already
- else //still running
- {
- if (_receivers.empty())
- _signal_set.async_wait(
- [this](const boost::system::error_code &ec, int)
- {
- boost::asio::dispatch(_strand, [this, ec]{this->_handle_signal(ec);});
- });
- _receivers.emplace_back(pid, h);
- }
- });
- return init.result.get();
- }
- void shutdown() override
- {
- _receivers.clear();
- }
- void cancel()
- {
- _signal_set.cancel();
- }
- void cancel(boost::system::error_code & ec)
- {
- _signal_set.cancel(ec);
- }
- };
- void sigchld_service::_handle_signal(const boost::system::error_code & ec)
- {
- std::error_code ec_{ec.value(), std::system_category()};
- if (ec_)
- {
- for (auto & r : _receivers)
- r.second(-1, ec_);
- return;
- }
- for (auto & r : _receivers) {
- int status;
- int pid = ::waitpid(r.first, &status, WNOHANG);
- if (pid < 0) {
- // error (eg: the process no longer exists)
- r.second(-1, get_last_error());
- r.first = 0; // mark for deletion
- } else if (pid == r.first) {
- r.second(status, ec_);
- r.first = 0; // mark for deletion
- }
- // otherwise the process is still around
- }
- _receivers.erase(std::remove_if(_receivers.begin(), _receivers.end(),
- [](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
- {
- return p.first == 0;
- }),
- _receivers.end());
- if (!_receivers.empty())
- {
- _signal_set.async_wait(
- [this](const boost::system::error_code & ec, int)
- {
- boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
- });
- }
- }
- }
- }
- }
- }
- #endif
|