summaryrefslogtreecommitdiff
path: root/boost/process/detail/posix/wait_group.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/process/detail/posix/wait_group.hpp')
-rw-r--r--boost/process/detail/posix/wait_group.hpp65
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();