From d6a306e745acfee00e81ccaf3324a2a03516db41 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 5 Dec 2019 15:21:30 +0900 Subject: Imported Upstream version 1.69.0 --- boost/process/detail/posix/executor.hpp | 22 ++- boost/process/detail/posix/wait_for_exit.hpp | 32 +++- boost/process/detail/posix/wait_group.hpp | 81 ++++++-- boost/process/detail/windows/executor.hpp | 2 +- boost/process/detail/windows/group_handle.hpp | 27 ++- boost/process/detail/windows/job_workaround.hpp | 242 ++++++++++++++++++------ boost/process/detail/windows/wait_group.hpp | 73 +++++-- 7 files changed, 372 insertions(+), 107 deletions(-) (limited to 'boost/process/detail') diff --git a/boost/process/detail/posix/executor.hpp b/boost/process/detail/posix/executor.hpp index b3781f2f04..1390a58ee6 100644 --- a/boost/process/detail/posix/executor.hpp +++ b/boost/process/detail/posix/executor.hpp @@ -45,7 +45,7 @@ inline int execvpe(const char* filename, char * const arg_list[], char* env[]) if (e != nullptr) { - std::vector path; + std::vector path; boost::split(path, *e, boost::is_any_of(":")); for (const std::string & pp : path) @@ -85,7 +85,7 @@ struct on_error_t template void operator()(T & t) const { - t.on_error(exec, error); + t.on_error(exec, error); } }; @@ -157,13 +157,13 @@ struct on_fork_success_t }; template on_setup_t call_on_setup (Executor & exec) {return exec;} -template on_error_t call_on_error (Executor & exec, const std::error_code & ec) +template on_error_t call_on_error (Executor & exec, const std::error_code & ec) { return on_error_t (exec, ec); } template on_success_t call_on_success(Executor & exec) {return exec;} -template on_fork_error_t call_on_fork_error (Executor & exec, const std::error_code & ec) +template on_fork_error_t call_on_fork_error (Executor & exec, const std::error_code & ec) { return on_fork_error_t (exec, ec); } @@ -293,10 +293,14 @@ class executor auto err = errno; if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return. return; - //EAGAIN not yet forked, EINTR interrupted, i.e. try again + //EAGAIN not yet forked, EINTR interrupted, i.e. try again else if ((err != EAGAIN ) && (err != EINTR)) + { + ::close(source); set_error(std::error_code(err, std::system_category()), "Error read pipe"); + } } + ::close(source); set_error(ec, std::move(msg)); } @@ -376,7 +380,10 @@ child executor::invoke(boost::mpl::false_, boost::mpl::false_) } if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1) { - set_error(::boost::process::detail::get_last_error(), "fcntl(2) failed"); + auto err = ::boost::process::detail::get_last_error(); + ::close(p[0]); + ::close(p[1]); + set_error(err, "fcntl(2) failed"); return child(); } _ec.clear(); @@ -420,11 +427,8 @@ child executor::invoke(boost::mpl::false_, boost::mpl::false_) child c(child_handle(pid), exit_status); - - ::close(p[1]); _read_error(p[0]); - ::close(p[0]); if (_ec) { diff --git a/boost/process/detail/posix/wait_for_exit.hpp b/boost/process/detail/posix/wait_for_exit.hpp index 9c40af1624..4e734322d2 100644 --- a/boost/process/detail/posix/wait_for_exit.hpp +++ b/boost/process/detail/posix/wait_for_exit.hpp @@ -28,7 +28,8 @@ inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) no { ret = ::waitpid(p.pid, &status, 0); } - while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status))); + while (((ret == -1) && (errno == EINTR)) || + (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status))); if (ret == -1) ec = boost::process::detail::get_last_error(); @@ -53,14 +54,43 @@ inline bool wait_until( const std::chrono::time_point& time_out, std::error_code & ec) noexcept { + + ::sigset_t sigset; + + ::sigemptyset(&sigset); + ::sigaddset(&sigset, SIGCHLD); + + auto get_timespec = + [](const Duration & dur) + { + ::timespec ts; + ts.tv_sec = std::chrono::duration_cast(dur).count(); + ts.tv_nsec = std::chrono::duration_cast(dur).count() % 1000000000; + return ts; + }; + pid_t ret; int status; + struct ::sigaction old_sig; + if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig)) + { + ec = get_last_error(); + return false; + } + bool timed_out; do { + auto ts = get_timespec(time_out - Clock::now()); + auto ret_sig = ::sigtimedwait(&sigset, nullptr, &ts); + errno = 0; ret = ::waitpid(p.pid, &status, WNOHANG); + + if ((ret_sig == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN)) + old_sig.sa_handler(ret); + if (ret == 0) { timed_out = Clock::now() >= time_out; diff --git a/boost/process/detail/posix/wait_group.hpp b/boost/process/detail/posix/wait_group.hpp index 024d9003b9..ff2fe6d30d 100644 --- a/boost/process/detail/posix/wait_group.hpp +++ b/boost/process/detail/posix/wait_group.hpp @@ -22,15 +22,23 @@ namespace boost { namespace process { namespace detail { namespace posix { inline void wait(const group_handle &p, std::error_code &ec) noexcept { pid_t ret; - int status; + siginfo_t status; do { - ret = ::waitpid(-p.grp, &status, 0); - } - while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status))); + ret = ::waitpid(-p.grp, &status.si_status, 0); + if (ret == -1) + { + ec = get_last_error(); + return; + } - if (ret == -1) + //ECHILD --> no child processes left. + ret = ::waitid(P_PGID, p.grp, &status, WEXITED | WNOHANG); + } + while ((ret != -1) || (errno != ECHILD)); + + if (errno != ECHILD) ec = boost::process::detail::get_last_error(); else ec.clear(); @@ -49,31 +57,66 @@ inline bool wait_until( const std::chrono::time_point& time_out, std::error_code & ec) noexcept { - pid_t ret; - int status; - bool timed_out; + ::sigset_t sigset; + ::siginfo_t siginfo; + + ::sigemptyset(&sigset); + ::sigaddset(&sigset, SIGCHLD); + + auto get_timespec = + [](const Duration & dur) + { + ::timespec ts; + ts.tv_sec = std::chrono::duration_cast(dur).count(); + ts.tv_nsec = std::chrono::duration_cast(dur).count() % 1000000000; + return ts; + }; + + + bool timed_out = false; + int ret; + + struct ::sigaction old_sig; + if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig)) + { + ec = get_last_error(); + return false; + } do { - ret = ::waitpid(-p.grp, &status, WNOHANG); - if (ret == 0) + auto ts = get_timespec(time_out - Clock::now()); + ret = ::sigtimedwait(&sigset, nullptr, &ts); + 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) { - timed_out = Clock::now() >= time_out; - if (timed_out) - return false; + ec = get_last_error(); + return false; } - } - while ((ret == 0) || - (((ret == -1) && errno == EINTR) || - ((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)))); - if (ret == -1) + + //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))) ; + + if (errno != ECHILD) + { ec = boost::process::detail::get_last_error(); + return !timed_out; + } else + { ec.clear(); + return true; //even if timed out, there are no child proccessess left + } - return true; } template< class Clock, class Duration > diff --git a/boost/process/detail/windows/executor.hpp b/boost/process/detail/windows/executor.hpp index bc52cbb5fe..6321b619c7 100644 --- a/boost/process/detail/windows/executor.hpp +++ b/boost/process/detail/windows/executor.hpp @@ -84,7 +84,7 @@ struct startup_info_impl void set_startup_info_ex() { startup_info.cb = sizeof(startup_info_ex_t); - creation_flags = ::boost::winapi::EXTENDED_STARTUPINFO_PRESENT_; + creation_flags |= ::boost::winapi::EXTENDED_STARTUPINFO_PRESENT_; } }; diff --git a/boost/process/detail/windows/group_handle.hpp b/boost/process/detail/windows/group_handle.hpp index 42b0a9280d..b3f7143c6d 100644 --- a/boost/process/detail/windows/group_handle.hpp +++ b/boost/process/detail/windows/group_handle.hpp @@ -84,22 +84,37 @@ inline void enable_break_away(::boost::winapi::HANDLE_ h, std::error_code & ec) ec = get_last_error(); return; } +} +inline void associate_completion_port(::boost::winapi::HANDLE_ job, + ::boost::winapi::HANDLE_ io_port) +{ + workaround::JOBOBJECT_ASSOCIATE_COMPLETION_PORT_ port; + port.CompletionKey = job; + port.CompletionPort = io_port; + if (!workaround::set_information_job_object( + job, + workaround::JobObjectAssociateCompletionPortInformation_, + static_cast(&port), + sizeof(port))) + throw_last_error("SetInformationJobObject() failed"); } - struct group_handle { ::boost::winapi::HANDLE_ _job_object; + ::boost::winapi::HANDLE_ _io_port; typedef ::boost::winapi::HANDLE_ handle_t; handle_t handle() const { return _job_object; } explicit group_handle(handle_t h) : - _job_object(h) + _job_object(h), + _io_port(::CreateIoCompletionPort(::boost::winapi::INVALID_HANDLE_VALUE_, nullptr, 0, 1)) { enable_break_away(_job_object); + associate_completion_port(_job_object, _io_port); } @@ -110,15 +125,21 @@ struct group_handle ~group_handle() { ::boost::winapi::CloseHandle(_job_object); + ::boost::winapi::CloseHandle(_io_port); } group_handle(const group_handle & c) = delete; - group_handle(group_handle && c) : _job_object(c._job_object) + group_handle(group_handle && c) : _job_object(c._job_object), + _io_port(c._io_port) { c._job_object = ::boost::winapi::invalid_handle_value; + c._io_port = ::boost::winapi::invalid_handle_value; } group_handle &operator=(const group_handle & c) = delete; group_handle &operator=(group_handle && c) { + ::boost::winapi::CloseHandle(_io_port); + _io_port = c._io_port; + c._io_port = ::boost::winapi::invalid_handle_value; ::boost::winapi::CloseHandle(_job_object); _job_object = c._job_object; diff --git a/boost/process/detail/windows/job_workaround.hpp b/boost/process/detail/windows/job_workaround.hpp index 574e406609..3f15d1c195 100644 --- a/boost/process/detail/windows/job_workaround.hpp +++ b/boost/process/detail/windows/job_workaround.hpp @@ -9,68 +9,184 @@ #include #include #include +#include +#if defined( BOOST_USE_WINDOWS_H ) +#include +#else +extern "C" +{ +BOOST_SYMBOL_IMPORT ::boost::winapi::HANDLE_ BOOST_WINAPI_WINAPI_CC CreateIoCompletionPort( + ::boost::winapi::HANDLE_ FileHandle, + ::boost::winapi::HANDLE_ ExistingCompletionPort, + ::boost::winapi::ULONG_PTR_ CompletionKey, + ::boost::winapi::DWORD_ NumberOfConcurrentThreads +); + +BOOST_SYMBOL_IMPORT ::boost::winapi::BOOL_ BOOST_WINAPI_WINAPI_CC GetQueuedCompletionStatus( + ::boost::winapi::HANDLE_ CompletionPort, + ::boost::winapi::LPDWORD_ lpNumberOfBytes, + ::boost::winapi::ULONG_PTR_ *lpCompletionKey, + _OVERLAPPED **lpOverlapped, + ::boost::winapi::DWORD_ dwMilliseconds +); + +} +#endif namespace boost { namespace process { namespace detail { namespace windows { namespace workaround { +extern "C" +{ + +struct JOBOBJECT_ASSOCIATE_COMPLETION_PORT_ +{ + ::boost::winapi::PVOID_ CompletionKey; + ::boost::winapi::HANDLE_ CompletionPort; +}; + +constexpr static int JOB_OBJECT_MSG_END_OF_JOB_TIME_ = 1; +constexpr static int JOB_OBJECT_MSG_END_OF_PROCESS_TIME_ = 2; +constexpr static int JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT_ = 3; +constexpr static int JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_ = 4; +constexpr static int JOB_OBJECT_MSG_NEW_PROCESS_ = 6; +constexpr static int JOB_OBJECT_MSG_EXIT_PROCESS_ = 7; +constexpr static int JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS_ = 8; +constexpr static int JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT_ = 9; +constexpr static int JOB_OBJECT_MSG_JOB_MEMORY_LIMIT_ = 10; +constexpr static int JOB_OBJECT_MSG_NOTIFICATION_LIMIT_ = 11; +constexpr static int JOB_OBJECT_MSG_JOB_CYCLE_TIME_LIMIT_ = 12; +constexpr static int JOB_OBJECT_MSG_SILO_TERMINATED_ = 13; + +} + +BOOST_FORCEINLINE ::boost::winapi::BOOL_ get_queued_completion_status( + ::boost::winapi::HANDLE_ CompletionPort, + ::boost::winapi::LPDWORD_ lpNumberOfBytes, + ::boost::winapi::ULONG_PTR_ *lpCompletionKey, + ::boost::winapi::LPOVERLAPPED_ *lpOverlapped, + ::boost::winapi::DWORD_ dwMilliseconds) +{ + return ::GetQueuedCompletionStatus( + CompletionPort, + lpNumberOfBytes, + lpCompletionKey, + reinterpret_cast<::_OVERLAPPED**>(lpOverlapped), + dwMilliseconds); +} + +#if defined( BOOST_USE_WINDOWS_H ) + +constexpr auto static JobObjectExtendedLimitInformation_ = ::JobObjectExtendedLimitInformation; +constexpr auto static JobObjectAssociateCompletionPortInformation_ = ::JobObjectAssociateCompletionPortInformation; +constexpr auto static JobObjectBasicAccountingInformation_ = ::JobObjectBasicAccountingInformation; + +using JOBOBJECT_BASIC_LIMIT_INFORMATION_ = ::JOBOBJECT_BASIC_LIMIT_INFORMATION; +using JOBOBJECTINFOCLASS_ = ::JOBOBJECTINFOCLASS; +using IO_COUNTERS_ = ::IO_COUNTERS; +using JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ = ::JOBOBJECT_EXTENDED_LIMIT_INFORMATION; +using JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ = ::JOBOBJECT_BASIC_ACCOUNTING_INFORMATION; + +inline ::boost::winapi::BOOL_ query_information_job_object( + ::boost::winapi::HANDLE_ hJob, + JOBOBJECTINFOCLASS_ JobObjectInfoClass, + void * lpJobObjectInfo, + ::boost::winapi::DWORD_ cbJobObjectInfoLength, + ::boost::winapi::DWORD_ *lpReturnLength) +{ + return ::QueryInformationJobObject(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength, lpReturnLength); +} + +inline ::boost::winapi::BOOL_ set_information_job_object( + ::boost::winapi::HANDLE_ hJob, + JOBOBJECTINFOCLASS_ JobObjectInfoClass, + void * lpJobObjectInfo, + ::boost::winapi::DWORD_ cbJobObjectInfoLength) +{ + return ::SetInformationJobObject(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength); +} + + +#else + //this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi. extern "C" { -typedef enum _JOBOBJECTINFOCLASS_ { - JobObjectBasicAccountingInformation_ = 1, JobObjectBasicLimitInformation_, - JobObjectBasicProcessIdList_, JobObjectBasicUIRestrictions_, - JobObjectSecurityLimitInformation_, JobObjectEndOfJobTimeInformation_, - JobObjectAssociateCompletionPortInformation_, JobObjectBasicAndIoAccountingInformation_, - JobObjectExtendedLimitInformation_, JobObjectJobSetInformation_, - JobObjectGroupInformation_, - JobObjectNotificationLimitInformation_, - JobObjectLimitViolationInformation_, - JobObjectGroupInformationEx_, - JobObjectCpuRateControlInformation_, - JobObjectCompletionFilter_, - JobObjectCompletionCounter_, - JobObjectReserved1Information_ = 18, - JobObjectReserved2Information_, - JobObjectReserved3Information_, - JobObjectReserved4Information_, - JobObjectReserved5Information_, - JobObjectReserved6Information_, - JobObjectReserved7Information_, - JobObjectReserved8Information_, - MaxJobObjectInfoClass_ - } JOBOBJECTINFOCLASS_; - -typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION_ { - ::boost::winapi::LARGE_INTEGER_ PerProcessUserTimeLimit; - ::boost::winapi::LARGE_INTEGER_ PerJobUserTimeLimit; - ::boost::winapi::DWORD_ LimitFlags; - ::boost::winapi::SIZE_T_ MinimumWorkingSetSize; - ::boost::winapi::SIZE_T_ MaximumWorkingSetSize; - ::boost::winapi::DWORD_ ActiveProcessLimit; - ::boost::winapi::ULONG_PTR_ Affinity; - ::boost::winapi::DWORD_ PriorityClass; - ::boost::winapi::DWORD_ SchedulingClass; +typedef enum _JOBOBJECTINFOCLASS_ +{ + JobObjectBasicAccountingInformation_ = 1, + JobObjectBasicLimitInformation_, + JobObjectBasicProcessIdList_, + JobObjectBasicUIRestrictions_, + JobObjectSecurityLimitInformation_, + JobObjectEndOfJobTimeInformation_, + JobObjectAssociateCompletionPortInformation_, + JobObjectBasicAndIoAccountingInformation_, + JobObjectExtendedLimitInformation_, + JobObjectJobSetInformation_, + JobObjectGroupInformation_, + JobObjectNotificationLimitInformation_, + JobObjectLimitViolationInformation_, + JobObjectGroupInformationEx_, + JobObjectCpuRateControlInformation_, + JobObjectCompletionFilter_, + JobObjectCompletionCounter_, + JobObjectReserved1Information_ = 18, + JobObjectReserved2Information_, + JobObjectReserved3Information_, + JobObjectReserved4Information_, + JobObjectReserved5Information_, + JobObjectReserved6Information_, + JobObjectReserved7Information_, + JobObjectReserved8Information_, + MaxJobObjectInfoClass_ +} JOBOBJECTINFOCLASS_; + +typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION_ +{ + ::boost::winapi::LARGE_INTEGER_ PerProcessUserTimeLimit; + ::boost::winapi::LARGE_INTEGER_ PerJobUserTimeLimit; + ::boost::winapi::DWORD_ LimitFlags; + ::boost::winapi::SIZE_T_ MinimumWorkingSetSize; + ::boost::winapi::SIZE_T_ MaximumWorkingSetSize; + ::boost::winapi::DWORD_ ActiveProcessLimit; + ::boost::winapi::ULONG_PTR_ Affinity; + ::boost::winapi::DWORD_ PriorityClass; + ::boost::winapi::DWORD_ SchedulingClass; } JOBOBJECT_BASIC_LIMIT_INFORMATION_; -typedef struct _IO_COUNTERS_ { - ::boost::winapi::ULONGLONG_ ReadOperationCount; - ::boost::winapi::ULONGLONG_ WriteOperationCount; - ::boost::winapi::ULONGLONG_ OtherOperationCount; - ::boost::winapi::ULONGLONG_ ReadTransferCount; - ::boost::winapi::ULONGLONG_ WriteTransferCount; - ::boost::winapi::ULONGLONG_ OtherTransferCount; +typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ { + ::boost::winapi::LARGE_INTEGER_ TotalUserTime; + ::boost::winapi::LARGE_INTEGER_ TotalKernelTime; + ::boost::winapi::LARGE_INTEGER_ ThisPeriodTotalUserTime; + ::boost::winapi::LARGE_INTEGER_ ThisPeriodTotalKernelTime; + ::boost::winapi::DWORD_ TotalPageFaultCount; + ::boost::winapi::DWORD_ TotalProcesses; + ::boost::winapi::DWORD_ ActiveProcesses; + ::boost::winapi::DWORD_ TotalTerminatedProcesses; +} JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_; + +typedef struct _IO_COUNTERS_ +{ + ::boost::winapi::ULONGLONG_ ReadOperationCount; + ::boost::winapi::ULONGLONG_ WriteOperationCount; + ::boost::winapi::ULONGLONG_ OtherOperationCount; + ::boost::winapi::ULONGLONG_ ReadTransferCount; + ::boost::winapi::ULONGLONG_ WriteTransferCount; + ::boost::winapi::ULONGLONG_ OtherTransferCount; } IO_COUNTERS_; -typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ { - JOBOBJECT_BASIC_LIMIT_INFORMATION_ BasicLimitInformation; - IO_COUNTERS_ IoInfo; - ::boost::winapi::SIZE_T_ ProcessMemoryLimit; - ::boost::winapi::SIZE_T_ JobMemoryLimit; - ::boost::winapi::SIZE_T_ PeakProcessMemoryUsed; - ::boost::winapi::SIZE_T_ PeakJobMemoryUsed; +typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ +{ + JOBOBJECT_BASIC_LIMIT_INFORMATION_ BasicLimitInformation; + IO_COUNTERS_ IoInfo; + ::boost::winapi::SIZE_T_ ProcessMemoryLimit; + ::boost::winapi::SIZE_T_ JobMemoryLimit; + ::boost::winapi::SIZE_T_ PeakProcessMemoryUsed; + ::boost::winapi::SIZE_T_ PeakJobMemoryUsed; } JOBOBJECT_EXTENDED_LIMIT_INFORMATION_; @@ -82,7 +198,7 @@ typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ { _Out_opt_ LPDWORD lpReturnLength ); */ -typedef ::boost::winapi::BOOL_ (*query_information_job_object_p)( +typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *query_information_job_object_p)( ::boost::winapi::HANDLE_, JOBOBJECTINFOCLASS_, void *, @@ -93,14 +209,17 @@ typedef ::boost::winapi::BOOL_ (*query_information_job_object_p)( inline ::boost::winapi::BOOL_ query_information_job_object( ::boost::winapi::HANDLE_ hJob, JOBOBJECTINFOCLASS_ JobObjectInfoClass, - void * lpJobObjectInfo, + void *lpJobObjectInfo, ::boost::winapi::DWORD_ cbJobObjectInfoLength, ::boost::winapi::DWORD_ *lpReturnLength) { - static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Kernel32.dll"); - static query_information_job_object_p f = reinterpret_cast(::boost::winapi::get_proc_address(h, "QueryInformationJobObject")); + static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle( + L"Kernel32.dll"); + static query_information_job_object_p f = reinterpret_cast(::boost::winapi::get_proc_address( + h, "QueryInformationJobObject")); - return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength, lpReturnLength); + return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, + cbJobObjectInfoLength, lpReturnLength); } /*BOOL WINAPI SetInformationJobObject( @@ -110,7 +229,7 @@ inline ::boost::winapi::BOOL_ query_information_job_object( _In_ DWORD cbJobObjectInfoLength );*/ -typedef ::boost::winapi::BOOL_ (*set_information_job_object_p)( +typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *set_information_job_object_p)( ::boost::winapi::HANDLE_, JOBOBJECTINFOCLASS_, void *, @@ -121,19 +240,22 @@ typedef ::boost::winapi::BOOL_ (*set_information_job_object_p)( inline ::boost::winapi::BOOL_ set_information_job_object( ::boost::winapi::HANDLE_ hJob, JOBOBJECTINFOCLASS_ JobObjectInfoClass, - void * lpJobObjectInfo, + void *lpJobObjectInfo, ::boost::winapi::DWORD_ cbJobObjectInfoLength) { - static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Kernel32.dll"); - static set_information_job_object_p f = reinterpret_cast(::boost::winapi::get_proc_address(h, "SetInformationJobObject")); + static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle( + L"Kernel32.dll"); + static set_information_job_object_p f = reinterpret_cast(::boost::winapi::get_proc_address( + h, "SetInformationJobObject")); - return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength); + return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, + cbJobObjectInfoLength); } +#endif + constexpr static ::boost::winapi::DWORD_ JOB_OBJECT_LIMIT_BREAKAWAY_OK_ = 0x00000800; }}}}} - - #endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */ diff --git a/boost/process/detail/windows/wait_group.hpp b/boost/process/detail/windows/wait_group.hpp index 32a8cd8842..6dca466d83 100644 --- a/boost/process/detail/windows/wait_group.hpp +++ b/boost/process/detail/windows/wait_group.hpp @@ -7,6 +7,7 @@ #define BOOST_PROCESS_DETAIL_WINDOWS_WAIT_GROUP_HPP_ #include +#include #include #include #include @@ -15,11 +16,61 @@ namespace boost { namespace process { namespace detail { namespace windows { struct group_handle; + +inline bool wait_impl(const group_handle & p, std::error_code & ec, int wait_time) +{ + ::boost::winapi::DWORD_ completion_code; + ::boost::winapi::ULONG_PTR_ completion_key; + ::boost::winapi::LPOVERLAPPED_ overlapped; + + auto start_time = std::chrono::system_clock::now(); + + while (workaround::get_queued_completion_status( + p._io_port, &completion_code, + &completion_key, &overlapped, wait_time)) + { + if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object && + completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_) + { + + //double check, could be a different handle from a child + workaround::JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ info; + if (!workaround::query_information_job_object( + p._job_object, + workaround::JobObjectBasicAccountingInformation_, + static_cast(&info), + sizeof(info), nullptr)) + { + ec = get_last_error(); + return false; + } + else if (info.ActiveProcesses == 0) + return false; //correct, nothing left. + } + //reduce the remaining wait time -> in case interrupted by something else + if (wait_time != ::boost::winapi::infinite) + { + auto now = std::chrono::system_clock::now(); + auto diff = std::chrono::duration_cast(now - start_time); + wait_time -= diff.count(); + start_time = now; + if (wait_time <= 0) + return true; //timeout with other source + } + + } + + auto ec_ = get_last_error(); + if (ec_.value() == ::boost::winapi::wait_timeout) + return true; //timeout + + ec = ec_; + return false; +} + inline void wait(const group_handle &p, std::error_code &ec) { - if (::boost::winapi::WaitForSingleObject(p.handle(), - ::boost::winapi::infinite) == ::boost::winapi::wait_failed) - ec = get_last_error(); + wait_impl(p, ec, ::boost::winapi::infinite); } inline void wait(const group_handle &p) @@ -39,16 +90,8 @@ inline bool wait_until( std::chrono::duration_cast( timeout_time - Clock::now()); - ::boost::winapi::DWORD_ wait_code; - wait_code = ::boost::winapi::WaitForSingleObject(p.handle(), ms.count()); - - if (wait_code == ::boost::winapi::wait_failed) - ec = get_last_error(); - - else if (wait_code == ::boost::winapi::wait_timeout) - return false; // - - return true; + auto timeout = wait_impl(p, ec, ms.count()); + return !ec && !timeout; } template< class Clock, class Duration > @@ -68,7 +111,9 @@ inline bool wait_for( const std::chrono::duration& rel_time, std::error_code &ec) { - return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec); + auto ms = std::chrono::duration_cast(rel_time); + auto timeout = wait_impl(p, ec, ms.count()); + return !ec && !timeout; } template< class Rep, class Period > -- cgit v1.2.3