sigchld_service.hpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright (c) 2017 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
  6. #define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
  7. #include <boost/asio/bind_executor.hpp>
  8. #include <boost/asio/dispatch.hpp>
  9. #include <boost/asio/post.hpp>
  10. #include <boost/asio/consign.hpp>
  11. #include <boost/asio/append.hpp>
  12. #include <boost/asio/signal_set.hpp>
  13. #include <boost/asio/strand.hpp>
  14. #include <boost/optional.hpp>
  15. #include <signal.h>
  16. #include <functional>
  17. #include <sys/wait.h>
  18. #include <list>
  19. namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
  20. class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
  21. {
  22. boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
  23. boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
  24. std::list<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
  25. inline void _handle_signal(const boost::system::error_code & ec);
  26. struct initiate_async_wait_op
  27. {
  28. sigchld_service * self;
  29. template<typename Initiation>
  30. void operator()(Initiation && init, ::pid_t pid)
  31. {
  32. // check if the child actually is running first
  33. int status;
  34. auto pid_res = ::waitpid(pid, &status, WNOHANG);
  35. if (pid_res < 0)
  36. {
  37. auto ec = get_last_error();
  38. boost::asio::post(
  39. self->_strand,
  40. asio::append(std::forward<Initiation>(init), pid_res, ec));
  41. }
  42. else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
  43. boost::asio::post(
  44. self->_strand,
  45. boost::asio::append(std::forward<Initiation>(init), status, std::error_code{}));
  46. else //still running
  47. {
  48. sigchld_service * self_ = self;
  49. if (self->_receivers.empty())
  50. self->_signal_set.async_wait(
  51. boost::asio::bind_executor(
  52. self->_strand,
  53. [self_](const boost::system::error_code &ec, int)
  54. {
  55. self_->_handle_signal(ec);
  56. }));
  57. self->_receivers.emplace_back(pid, init);
  58. }
  59. }
  60. };
  61. public:
  62. sigchld_service(boost::asio::io_context & io_context)
  63. : boost::asio::detail::service_base<sigchld_service>(io_context)
  64. {
  65. }
  66. template <typename SignalHandler>
  67. BOOST_ASIO_INITFN_RESULT_TYPE(SignalHandler,
  68. void (int, std::error_code))
  69. async_wait(::pid_t pid, SignalHandler && handler)
  70. {
  71. return boost::asio::async_initiate<
  72. SignalHandler,
  73. void(int, std::error_code)>(
  74. initiate_async_wait_op{this}, handler, pid);
  75. }
  76. void shutdown() override
  77. {
  78. _receivers.clear();
  79. }
  80. void cancel()
  81. {
  82. _signal_set.cancel();
  83. }
  84. void cancel(boost::system::error_code & ec)
  85. {
  86. _signal_set.cancel(ec);
  87. }
  88. };
  89. void sigchld_service::_handle_signal(const boost::system::error_code & ec)
  90. {
  91. std::error_code ec_{ec.value(), std::system_category()};
  92. if (ec_)
  93. {
  94. for (auto & r : _receivers)
  95. r.second(-1, ec_);
  96. return;
  97. }
  98. for (auto & r : _receivers) {
  99. int status;
  100. int pid = ::waitpid(r.first, &status, WNOHANG);
  101. if (pid < 0) {
  102. // error (eg: the process no longer exists)
  103. r.second(-1, get_last_error());
  104. r.first = 0; // mark for deletion
  105. } else if (pid == r.first) {
  106. r.second(status, ec_);
  107. r.first = 0; // mark for deletion
  108. }
  109. // otherwise the process is still around
  110. }
  111. _receivers.erase(std::remove_if(_receivers.begin(), _receivers.end(),
  112. [](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
  113. {
  114. return p.first == 0;
  115. }),
  116. _receivers.end());
  117. if (!_receivers.empty())
  118. {
  119. _signal_set.async_wait(
  120. [this](const boost::system::error_code & ec, int)
  121. {
  122. boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
  123. });
  124. }
  125. }
  126. }
  127. }
  128. }
  129. }
  130. }
  131. #endif