Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:50:06

0001 // Copyright (c) 2006, 2007 Julio M. Merino Vidal
0002 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
0003 // Copyright (c) 2009 Boris Schaeling
0004 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling
0005 // Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 
0010 #ifndef BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
0011 #define BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
0012 
0013 #include <boost/process/detail/config.hpp>
0014 #include <boost/process/detail/posix/child_handle.hpp>
0015 #include <system_error>
0016 #include <sys/types.h>
0017 #include <sys/wait.h>
0018 #include <unistd.h>
0019 
0020 namespace boost { namespace process { namespace detail { namespace posix {
0021 
0022 inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
0023 {
0024     pid_t ret;
0025     int status;
0026 
0027     do
0028     {
0029         ret = ::waitpid(p.pid, &status, 0);
0030     } 
0031     while (((ret == -1) && (errno == EINTR)) || 
0032            (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
0033 
0034     if (ret == -1)
0035         ec = boost::process::detail::get_last_error();
0036     else
0037     {
0038         ec.clear();
0039         exit_code = status;
0040     }
0041 }
0042 
0043 inline void wait(const child_handle &p, int & exit_code) noexcept
0044 {
0045     std::error_code ec;
0046     wait(p, exit_code, ec);
0047     boost::process::detail::throw_error(ec, "waitpid(2) failed in wait");
0048 }
0049 
0050 template< class Clock, class Duration >
0051 inline bool wait_until(
0052         const child_handle &p,
0053         int & exit_code,
0054         const std::chrono::time_point<Clock, Duration>& time_out,
0055         std::error_code & ec) noexcept
0056 {
0057     ::sigset_t  sigset;
0058 
0059     //I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
0060 
0061     using _signal_t = void(*)(int);
0062     static thread_local _signal_t sigchld_handler = SIG_DFL;
0063 
0064     struct signal_interceptor_t
0065     {
0066         static void handler_func(int val)
0067         {
0068             if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
0069                 sigchld_handler(val);
0070         }
0071         signal_interceptor_t()  { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
0072         ~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
0073 
0074     } signal_interceptor{};
0075 
0076     if (sigemptyset(&sigset) != 0)
0077     {
0078         ec = get_last_error();
0079         return false;
0080     }
0081     if (sigaddset(&sigset, SIGCHLD) != 0)
0082     {
0083         ec = get_last_error();
0084         return false;
0085     }
0086 
0087     auto get_timespec = 
0088             [](const Duration & dur)
0089             {
0090                 ::timespec ts;
0091                 ts.tv_sec  = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
0092                 ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
0093                 return ts;
0094             };
0095 
0096     int ret;
0097     int status{0};
0098 
0099     struct ::sigaction old_sig;
0100     if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
0101     {
0102         ec = get_last_error();
0103         return false;
0104     }
0105 
0106     bool timed_out;
0107 
0108 #if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
0109     do
0110     {
0111         auto ts = get_timespec(time_out - Clock::now());
0112         auto ret_sig = ::sigtimedwait(&sigset, nullptr, &ts);
0113         errno = 0;
0114         ret = ::waitpid(p.pid, &status, WNOHANG);
0115 
0116         if ((ret_sig == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
0117             old_sig.sa_handler(ret);
0118 
0119         if (ret == 0)
0120         {
0121             timed_out = Clock::now() >= time_out;
0122             if (timed_out)
0123                 return false;
0124         }
0125     }
0126     while ((ret == 0) ||
0127           (((ret == -1) && errno == EINTR) ||
0128            ((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
0129 #else
0130     //if we do not have sigtimedwait, we fork off a child process  to get the signal in time
0131     pid_t timeout_pid = ::fork();
0132     if (timeout_pid  == -1)
0133     {
0134         ec = boost::process::detail::get_last_error();
0135         return true;
0136     }
0137     else if (timeout_pid == 0)
0138     {
0139         auto ts = get_timespec(time_out - Clock::now());
0140         ::timespec rem;
0141         while (ts.tv_sec > 0 || ts.tv_nsec > 0)
0142         {
0143             if (::nanosleep(&ts, &rem) != 0)
0144             {
0145                 auto err = errno;
0146                 if ((err == EINVAL) || (err == EFAULT))
0147                     break;
0148             }
0149             ts = get_timespec(time_out - Clock::now());
0150         }
0151         ::exit(0);
0152     }
0153 
0154     struct child_cleaner_t
0155     {
0156         pid_t pid;
0157         ~child_cleaner_t()
0158         {
0159             int res;
0160             ::kill(pid, SIGKILL);
0161             ::waitpid(pid, &res, 0);
0162         }
0163     };
0164     child_cleaner_t child_cleaner{timeout_pid};
0165 
0166     do
0167     {
0168         int sig_{0};
0169         if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
0170             && (WIFEXITED(status) || WIFSIGNALED(status)))
0171 
0172             return false;
0173 
0174         ret = ::sigwait(&sigset, &sig_);
0175         errno = 0;
0176 
0177         if ((sig_ == SIGCHLD) &&
0178             (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
0179             old_sig.sa_handler(ret);
0180 
0181         ret = ::waitpid(p.pid, &status, WNOHANG);
0182         if (ret == 0) // == > is running
0183         {
0184             timed_out = Clock::now() >= time_out;
0185             if (timed_out)
0186                 return false;
0187         }
0188     }
0189     while ((ret == 0) ||
0190            (((ret == -1) && errno == EINTR) ||
0191             ((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
0192 #endif
0193 
0194     if (ret == -1)
0195         ec = boost::process::detail::get_last_error();
0196     else
0197     {
0198         ec.clear();
0199         exit_code = status;
0200     }
0201 
0202     return true;
0203 }
0204 
0205 template< class Clock, class Duration >
0206 inline bool wait_until(
0207         const child_handle &p,
0208         int & exit_code,
0209         const std::chrono::time_point<Clock, Duration>& time_out)
0210 {
0211     std::error_code ec;
0212     bool b = wait_until(p, exit_code, time_out, ec);
0213     boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_until");
0214     return b;
0215 }
0216 
0217 template< class Rep, class Period >
0218 inline bool wait_for(
0219         const child_handle &p,
0220         int & exit_code,
0221         const std::chrono::duration<Rep, Period>& rel_time,
0222         std::error_code & ec) noexcept
0223 {
0224     return wait_until(p, exit_code, std::chrono::steady_clock::now() + rel_time, ec);
0225 }
0226 
0227 template< class Rep, class Period >
0228 inline bool wait_for(
0229         const child_handle &p,
0230         int & exit_code,
0231         const std::chrono::duration<Rep, Period>& rel_time)
0232 {
0233     std::error_code ec;
0234     bool b = wait_for(p, exit_code, rel_time, ec);
0235     boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_for");
0236     return b;
0237 }
0238 
0239 }}}}
0240 
0241 #endif