Skip to content

Commit

Permalink
Fix CPU usage on macOS wait_until.
Browse files Browse the repository at this point in the history
Related (I believe) to boostorg#55

With the fix from klemens-morgenstern/boost-process#197, multiple processes can still at times be spawned, and they take extremely high CPU usage. This PR basically uses the same approach used for boost 1.68.0 (which was the last time wait_for seemed to work reliably) but adds a 1ms sleep to each iteration of the loop so as not to spike the CPU.
  • Loading branch information
toonetown committed Aug 27, 2019
1 parent 0a554c9 commit 7bbdd1f
Showing 1 changed file with 6 additions and 47 deletions.
53 changes: 6 additions & 47 deletions include/boost/process/detail/posix/wait_for_exit.hpp
Expand Up @@ -17,6 +17,10 @@
#include <sys/wait.h>
#include <unistd.h>

#if !defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
#include <thread>
#endif

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

inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
Expand Down Expand Up @@ -127,63 +131,18 @@ inline bool wait_until(
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
#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());
::timespec rem;
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
::exit(0);
}

struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
}
};
child_cleaner_t child_cleaner{timeout_pid};

//if we do not have sigtimedwait, we just loop and sleep for a millisecond to prevent CPU spike
do
{
int sig_{0};
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))

return false;

ret = ::sigwait(&sigset, &sig_);
errno = 0;

if ((sig_ == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);

ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
{
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
while ((ret == 0) ||
Expand Down

0 comments on commit 7bbdd1f

Please sign in to comment.