Skip to content
This repository has been archived by the owner on Jun 11, 2022. It is now read-only.

Commit

Permalink
Merge e6fa19b into 9f6c338
Browse files Browse the repository at this point in the history
  • Loading branch information
klemens-morgenstern committed Feb 20, 2018
2 parents 9f6c338 + e6fa19b commit b5cc917
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 447 deletions.
4 changes: 2 additions & 2 deletions doc/extend.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ struct async_foo : __handler__, __require_io_service__
}
};
```
[caution All async_handlers use one signal(SIGCHLD) on posix, which is only guaranteed to work when all use the same `io_service`]

[note Inheriting [globalref boost::process::extend::require_io_service require_io_service] is necessary, so [funcref boost::process::system system] provides one.]

Additionally the handler can provide a function that is invoked when the child process exits. This is done through __async_handler__.
Expand All @@ -135,6 +133,8 @@ struct async_bar : __handler, __async_handler__

[caution `on_exit_handler` does not default and is always required when [classref boost::process::extend::async_handler async_handler] is inherited. ]

[caution `on_exit_handler` uses `boost::asio::signal_set` to listen for SIGCHLD on posix. The application must not also register a signal handler for SIGCHLD using functions such as `signal()` or `sigaction()` (but using `boost::asio::signal_set` is fine). ]

[endsect]

[section:error Error handling]
Expand Down
4 changes: 3 additions & 1 deletion include/boost/process/async.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ chlid c2("ls", on_exit=exit_code);
\note The handler is not invoked when the launch fails.
\warning When used \ref ignore_error it might get invoked on error.
\warning All `on_exit` use one signal(SIGCHLD) on posix, which is only guaranteed to work when all use the same `io_context`.
\warning `on_exit` uses `boost::asio::signal_set` to listen for `SIGCHLD` on posix, and so has the
same restrictions as that class (do not register a handler for `SIGCHLD` except by using
`boost::asio::signal_set`).
*/
constexpr static ::boost::process::detail::on_exit_ on_exit{};
#endif
Expand Down
70 changes: 21 additions & 49 deletions include/boost/process/detail/child_decl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,62 +103,42 @@ class child

bool running()
{
if (valid() && !_exited())
{
int code = -1;
auto res = boost::process::detail::api::is_running(_child_handle, code);
if (!res && !_exited())
_exit_status->store(code);

return res;
}
return false;
std::error_code ec;
bool b = running(ec);
boost::process::detail::throw_error(ec, "running error");
return b;
}

void terminate()
{
if (valid() && running())
boost::process::detail::api::terminate(_child_handle);

_terminated = true;
std::error_code ec;
terminate(ec);
boost::process::detail::throw_error(ec, "terminate error");
}

void wait()
{
if (!_exited() && valid())
{
int exit_code = 0;
boost::process::detail::api::wait(_child_handle, exit_code);
_exit_status->store(exit_code);
}
std::error_code ec;
wait(ec);
boost::process::detail::throw_error(ec, "wait error");
}

template< class Rep, class Period >
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time)
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time)
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
std::error_code ec;
bool b = wait_for(rel_time, ec);
boost::process::detail::throw_error(ec, "wait_for error");
return b;
}

template< class Clock, class Duration >
bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time )
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
std::error_code ec;
bool b = wait_until(timeout_time, ec);
boost::process::detail::throw_error(ec, "wait_until error");
return b;
}

bool running(std::error_code & ec) noexcept
Expand Down Expand Up @@ -194,17 +174,9 @@ class child
}

template< class Rep, class Period >
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time, ec);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
return wait_until(std::chrono::steady_clock::now() + rel_time, ec);
}

template< class Clock, class Duration >
Expand Down
12 changes: 6 additions & 6 deletions include/boost/process/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,16 @@ inline void throw_last_error()
throw process_error(get_last_error());
}

inline void throw_error(const std::error_code& err)
inline void throw_error(const std::error_code& ec)
{
if (err)
throw process_error(err);
if (ec)
throw process_error(ec);
}

inline void throw_error(const std::error_code& err, const char* location)
inline void throw_error(const std::error_code& ec, const char* msg)
{
if (err)
throw process_error(err, location);
if (ec)
throw process_error(ec, msg);
}

template<typename Char> constexpr Char null_char();
Expand Down
23 changes: 7 additions & 16 deletions include/boost/process/detail/posix/group_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
#ifndef BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_

#include <boost/process/detail/config.hpp>
#include <boost/process/detail/posix/child_handle.hpp>
#include <system_error>
#include <unistd.h>

namespace boost { namespace process { namespace detail { namespace posix {



struct group_handle
{
pid_t grp = -1;
Expand All @@ -26,7 +25,6 @@ struct group_handle
{
}


group_handle() = default;

~group_handle() = default;
Expand All @@ -38,7 +36,6 @@ struct group_handle
group_handle &operator=(const group_handle & c) = delete;
group_handle &operator=(group_handle && c)
{

grp = c.grp;
c.grp = -1;
return *this;
Expand All @@ -62,23 +59,14 @@ struct group_handle
bool has(handle_t proc, std::error_code & ec) noexcept
{
return ::getpgid(proc) == grp;

}

bool valid() const
{
return grp != -1;
}

};

inline void terminate(group_handle &p)
{
if (::killpg(p.grp, SIGKILL) == -1)
boost::process::detail::throw_last_error("killpg(2) failed");
p.grp = -1;
}

inline void terminate(group_handle &p, std::error_code &ec) noexcept
{
if (::killpg(p.grp, SIGKILL) == -1)
Expand All @@ -89,15 +77,18 @@ inline void terminate(group_handle &p, std::error_code &ec) noexcept
p.grp = -1;
}

inline void terminate(group_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::detail::throw_error(ec, "killpg(2) failed in terminate");
}

inline bool in_group()
{
return true;
}



}}}}


#endif /* BOOST_PROCESS_DETAIL_WINDOWS_GROUP_HPP_ */
36 changes: 11 additions & 25 deletions include/boost/process/detail/posix/is_running.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,11 @@ inline bool is_running(int code)
return !WIFEXITED(code) && !WIFSIGNALED(code);
}

inline bool is_running(const child_handle &p, int & exit_code)
{
int status;
auto ret = ::waitpid(p.pid, &status, WNOHANG|WUNTRACED);

if (ret == -1)
{
if (errno != ECHILD) //because it no child is running, than this one isn't either, obviously.
::boost::process::detail::throw_last_error("is_running error");

return false;
}
else if (ret == 0)
return true;
else //exited
{
if (!is_running(status))
exit_code = status;
return false;
}
}

inline bool is_running(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
int status;
auto ret = ::waitpid(p.pid, &status, WNOHANG|WUNTRACED);

if (ret == -1)
{
if (errno != ECHILD) //because it no child is running, than this one isn't either, obviously.
Expand All @@ -60,14 +38,22 @@ inline bool is_running(const child_handle &p, int & exit_code, std::error_code &
else
{
ec.clear();

if (!is_running(status))
exit_code = status;

return false;
}
}

inline bool is_running(const child_handle &p, int & exit_code)
{
std::error_code ec;
bool b = is_running(p, exit_code, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in is_running");
return b;
}

inline int eval_exit_status(int code)
{
if (WIFEXITED(code))
Expand Down
4 changes: 3 additions & 1 deletion include/boost/process/detail/posix/search_path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ inline boost::filesystem::path search_path(
for (const boost::filesystem::path & pp : path)
{
auto p = pp / filename;
if (!::access(p.c_str(), X_OK))
boost::system::error_code ec;
bool file = boost::filesystem::is_regular_file(p, ec);
if (!ec && file && ::access(p.c_str(), X_OK) == 0)
return p;
}
return "";
Expand Down
31 changes: 20 additions & 11 deletions include/boost/process/detail/posix/sigchld_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,28 @@ void sigchld_service::_handle_signal(const boost::system::error_code & ec)
r.second(-1, ec_);
return;
}
int status;
int pid = ::waitpid(0, &status, WNOHANG);

auto itr = std::find_if(_receivers.begin(), _receivers.end(),
[&pid](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
{
return p.first == pid;
});
if (itr != _receivers.cend())
{
_strand.get_io_context().wrap(itr->second)(status, ec_);
_receivers.erase(itr);
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(
Expand Down
16 changes: 7 additions & 9 deletions include/boost/process/detail/posix/terminate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,6 @@

namespace boost { namespace process { namespace detail { namespace posix {


inline void terminate(const child_handle &p)
{
if (::kill(p.pid, SIGKILL) == -1)
boost::process::detail::throw_last_error("kill(2) failed");
int status;
::waitpid(p.pid, &status, 0); //just to clean it up
}

inline void terminate(const child_handle &p, std::error_code &ec) noexcept
{
if (::kill(p.pid, SIGKILL) == -1)
Expand All @@ -39,6 +30,13 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
::waitpid(p.pid, &status, 0); //just to clean it up
}

inline void terminate(const child_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::detail::throw_error(ec, "kill(2) failed");
}

}}}}

#endif
6 changes: 3 additions & 3 deletions include/boost/process/detail/posix/wait_for_exit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ inline void wait(const child_handle &p, int & exit_code) noexcept
{
std::error_code ec;
wait(p, exit_code, ec);
boost::process::detail::throw_error(ec, "wait");
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait");
}

template< class Clock, class Duration >
Expand Down Expand Up @@ -91,7 +91,7 @@ inline bool wait_until(
{
std::error_code ec;
bool b = wait_until(p, exit_code, time_out, ec);
boost::process::detail::throw_error(ec, "wait_until");
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_until");
return b;
}

Expand All @@ -113,7 +113,7 @@ inline bool wait_for(
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);
boost::process::detail::throw_error(ec, "wait_for");
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_for");
return b;
}

Expand Down
Loading

0 comments on commit b5cc917

Please sign in to comment.