diff options
Diffstat (limited to 'boost/process/detail/posix/wait_group.hpp')
-rw-r--r-- | boost/process/detail/posix/wait_group.hpp | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/boost/process/detail/posix/wait_group.hpp b/boost/process/detail/posix/wait_group.hpp index ff2fe6d30d..9dc2498031 100644 --- a/boost/process/detail/posix/wait_group.hpp +++ b/boost/process/detail/posix/wait_group.hpp @@ -16,6 +16,7 @@ #include <system_error> #include <sys/types.h> #include <sys/wait.h> +#include <unistd.h> namespace boost { namespace process { namespace detail { namespace posix { @@ -61,8 +62,9 @@ inline bool wait_until( ::sigset_t sigset; ::siginfo_t siginfo; - ::sigemptyset(&sigset); - ::sigaddset(&sigset, SIGCHLD); + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + auto get_timespec = [](const Duration & dur) @@ -84,6 +86,7 @@ inline bool wait_until( return false; } +#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT) do { auto ts = get_timespec(time_out - Clock::now()); @@ -99,13 +102,65 @@ inline bool wait_until( return false; } - //check if we're done ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG); } - while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out))) ; - + while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out))); +#else + //if we do not have sigtimedwait, we fork off a child process to get the signal in time + pid_t timeout_pid = ::fork(); + if (timeout_pid == -1) + { + ec = boost::process::detail::get_last_error(); + return true; + } + else if (timeout_pid == 0) + { + auto ts = get_timespec(time_out - Clock::now()); + ::setpgid(0, p.grp); + ::nanosleep(&ts, nullptr); + ::exit(0); + } + + struct child_cleaner_t + { + pid_t pid; + ~child_cleaner_t() + { + int res; + ::kill(pid, -15); + ::waitpid(pid, &res, WNOHANG); + } + }; + child_cleaner_t child_cleaner{timeout_pid}; + + do + { + int ret_sig = 0; + int status; + if ((::waitpid(timeout_pid, &status, WNOHANG) != 0) + && (WIFEXITED(status) || WIFSIGNALED(status))) + ret = ::sigwait(&sigset, nullptr); + errno = 0; + if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN)) + old_sig.sa_handler(ret); + + ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first + if (ret == -1) + { + ec = get_last_error(); + return false; + } + + //check if we're done + ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG); + + } + while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out))); + +#endif + if (errno != ECHILD) { ec = boost::process::detail::get_last_error(); |