2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 16:52:14 +00:00

Compare commits

...

46 Commits

Author SHA1 Message Date
Klemens David Morgenstern
c3b707b709 Merge branch 'master' of github.com:boostorg/process 2018-09-26 13:53:10 +07:00
Klemens Morgenstern
57e9dfb705 Merge pull request #179 from klemens-morgenstern/develop
Update for master
2018-09-26 13:50:47 +07:00
Klemens David Morgenstern
4fd8887601 fixed group wait-for on windows 2018-09-26 11:50:18 +07:00
Lemmy
3cf4bf6480 Hope fully fixed group waiting 2018-09-25 17:32:32 +02:00
Lemmy
256523d36e Merge branch 'develop' of https://github.com/klemens-morgenstern/boost-process into develop 2018-09-25 11:45:23 +02:00
Lemmy
6ba8e88def wait-group fix 2018-09-25 11:45:04 +02:00
Klemens David Morgenstern
f139f863a0 typo fix in test 2018-09-25 16:10:11 +07:00
Lemmy
0938103427 Reworked wait_for_exit, concerns #99 and #112 2018-09-25 10:45:54 +02:00
Lemmy
e72127f9f8 Implemented proper wait_for for group_handles 2018-09-25 10:27:40 +02:00
Lemmy
eea73753b5 Fixed group wait in linux 2018-09-25 07:40:07 +02:00
Klemens David Morgenstern
4f3b425073 fixed group-wait, finally 2018-09-25 02:37:51 +07:00
Klemens David Morgenstern
dcb8a0266a preserving creation_flags, closes #176 2018-09-24 23:42:53 +07:00
Klemens David Morgenstern
99285a9de6 fixed windows-h variant 2018-09-24 23:27:01 +07:00
Klemens David Morgenstern
6cc31b93d8 readded BOOST_WINAPI_WINAPI_CC 2018-09-24 23:10:33 +07:00
Klemens David Morgenstern
d1ce19d848 fixes #178 2018-09-21 10:48:03 +07:00
Klemens Morgenstern
f00895a9fc Update tutorial.qbk
closes #49
2018-09-11 14:38:49 +08:00
Klemens David Morgenstern
8d2bd87707 ALternative (typeless) implementation of #177 2018-08-29 09:37:07 +08:00
Klemens David Morgenstern
44162ecf22 removed errornous noexcept 2018-06-19 18:34:39 +08:00
Klemens David Morgenstern
d709c1cd07 fixed tutorial example type 2018-06-18 09:46:15 +08:00
Klemens Morgenstern
90d2c0ceca Merge pull request #168 from klemens-morgenstern/develop
1.68 release merge
2018-06-13 22:00:09 +03:00
Klemens David Morgenstern
9549ffe7e1 capture list fix 2018-05-27 11:39:56 +02:00
Klemens David Morgenstern
dd0edb4aee Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-05-26 22:07:15 +02:00
Klemens David Morgenstern
3029f4623a closes #41 2018-05-26 22:06:54 +02:00
Klemens David Morgenstern
74606db379 Merge branch 'bugfixing' into develop 2018-05-26 22:00:12 +02:00
Klemens David Morgenstern
81803868a3 closes #45 2018-05-26 21:59:58 +02:00
Klemens David Morgenstern
eff42f91ef closes #42 2018-05-26 21:57:25 +02:00
Klemens David Morgenstern
a25b6ca35b closes #32 2018-05-26 21:55:07 +02:00
Klemens Morgenstern
1c8323650d Merge pull request #166 from egorpugin/patch-2
Add missing returns on windows side.
2018-05-26 20:46:14 +02:00
Klemens David Morgenstern
52f030a83c closes #167 2018-05-26 20:32:08 +02:00
Egor Pugin
9cc651bdeb Add missing returns on windows side. 2018-05-09 20:09:32 +03:00
Klemens Morgenstern
128cb0283d Merge pull request #165 from egorpugin/patch-2
Add missing returns
2018-05-09 14:41:31 +02:00
Egor Pugin
bb259f8f16 Add missing returns 2018-05-09 15:16:15 +03:00
Klemens Morgenstern
bb1bb431e5 Merge pull request #163 from tomaszjonak/develop
Fix async_pipe::async_read_some always returning 0
2018-05-04 17:22:48 +02:00
Tomasz Jonak
41b7e30c18 Add missing return to async_pipe::async_{read,write}_some 2018-05-04 14:03:54 +00:00
Klemens David Morgenstern
f1c6909eb0 Merge remote-tracking branch 'remotes/origin/develop' 2018-04-05 20:35:52 +02:00
Klemens Morgenstern
35fda5aa6a Update job_workaround.hpp 2018-04-05 20:12:51 +02:00
Klemens Morgenstern
1f7f805858 Merge pull request #158 from klemens-morgenstern/develop
Update job_workaround.hpp
2018-04-05 11:16:39 +02:00
Klemens Morgenstern
d47b7f7ac4 Update job_workaround.hpp 2018-04-05 10:08:00 +02:00
Klemens David Morgenstern
2bc2531d2a Merge branch 'develop' 2018-04-04 20:55:25 +02:00
Klemens David Morgenstern
c5798fdf7f added write_some and read_some overloads - closes #35 2018-03-13 10:11:01 +08:00
Klemens David Morgenstern
5e43e7c07c Merge branch 'develop' of github.com:boostorg/process into develop 2018-03-11 20:58:22 +01:00
Klemens Morgenstern
4fc4784506 Merge pull request #34 from Lastique/update_winapi_cc
Switch WINAPI calling convention macros to the replacements from Boost.WinAPI
2018-03-11 20:56:06 +01:00
Andrey Semashev
900aab5d6d Switched WINAPI calling convention macros to the replacements from Boost.WinAPI
WINAPI macro definition in Boost.WinAPI is deprecated as it may clash with
the macro defined in Windows SDK.
2018-03-11 20:20:49 +03:00
Klemens Morgenstern
f61a61cf59 Merge pull request #144 from klemens-morgenstern/develop
Another master updates
2018-02-26 23:25:28 +01:00
Klemens Morgenstern
9f6c338631 Merge pull request #140 from klemens-morgenstern/develop
master update
2018-02-07 00:19:10 +01:00
Klemens Morgenstern
c4ffd0c18d Merge pull request #135 from klemens-morgenstern/develop
Appveyor fixes
2018-02-02 23:26:12 +01:00
24 changed files with 556 additions and 145 deletions

View File

@@ -158,7 +158,7 @@ will change the behaviour, so that instead of throwing an exception, the error w
```
std::error_code ec;
bp::system c("g++ main.cpp", ec);
bp::system("g++ main.cpp", ec);
```
[endsect]
[section:io Synchronous I/O]
@@ -296,7 +296,7 @@ provided we also pass a reference to an io_service.
```
io_service ios;
std::vector<char> buf;
std::vector<char> buf(4096);
bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > asio_buffer(buf), ios);

View File

@@ -82,7 +82,7 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
{
#if defined(BOOST_POSIX_API)
if (errored)
return [](int exit_code, const std::error_code & ec){};
return [](int , const std::error_code &){};
#endif
auto & h = init.completion_handler;
return [h](int exit_code, const std::error_code & ec) mutable

View File

@@ -39,7 +39,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
{
}
template <typename Executor>
inline void on_success(Executor &exec)
inline void on_success(Executor)
{
auto pipe = this->pipe;
if (this->promise)
@@ -60,7 +60,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
}
else
boost::asio::async_write(*pipe, buf,
[pipe](const boost::system::error_code&ec, std::size_t size){});
[pipe](const boost::system::error_code&, std::size_t){});
std::move(*pipe).source().close();

View File

@@ -61,7 +61,7 @@ struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
{
auto pipe = this->pipe;
boost::asio::async_read(*pipe, buf,
[pipe](const boost::system::error_code&, std::size_t size){});
[pipe](const boost::system::error_code&, std::size_t){});
this->pipe = nullptr;
std::move(*pipe).sink().close();
@@ -112,7 +112,7 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
fut = promise->get_future();
}
template <typename Executor>
inline void on_success(Executor &exec)
inline void on_success(Executor &)
{
auto pipe = this->pipe;
@@ -120,7 +120,7 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
auto promise = this->promise;
boost::asio::async_read(*pipe, *buffer,
[pipe, buffer, promise](const boost::system::error_code& ec, std::size_t size)
[pipe, buffer, promise](const boost::system::error_code& ec, std::size_t)
{
if (ec && (ec.value() != ENOENT))
{

View File

@@ -125,6 +125,18 @@ public:
return _sink.write_some(buffers);
}
template<typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _source.read_some(buffers, ec);
}
template<typename MutableBufferSequence>
std::size_t write_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _sink.write_some(buffers, ec);
}
native_handle_type native_source() const {return const_cast<boost::asio::posix::stream_descriptor&>(_source).native_handle();}
native_handle_type native_sink () const {return const_cast<boost::asio::posix::stream_descriptor&>(_sink ).native_handle();}
@@ -136,7 +148,7 @@ public:
const MutableBufferSequence & buffers,
ReadHandler &&handler)
{
_source.async_read_some(buffers, std::forward<ReadHandler>(handler));
return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence,
@@ -147,7 +159,7 @@ public:
const ConstBufferSequence & buffers,
WriteHandler&& handler)
{
_sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
@@ -238,8 +250,8 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
int sink;
//cannot get the handle from a const object.
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._sink).native_handle();
if (source_in == -1)
source = -1;
else

View File

@@ -45,7 +45,7 @@ inline int execvpe(const char* filename, char * const arg_list[], char* env[])
if (e != nullptr)
{
std::vector<std::string> path;
std::vector<std::string> path;
boost::split(path, *e, boost::is_any_of(":"));
for (const std::string & pp : path)
@@ -85,7 +85,7 @@ struct on_error_t
template<typename T>
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<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
{
return on_error_t<Executor> (exec, ec);
}
template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
{
return on_fork_error_t<Executor> (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<Sequence>::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<Sequence>::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)
{

View File

@@ -56,7 +56,7 @@ struct group_handle
{
return ::getpgid(proc) == grp;
}
bool has(handle_t proc, std::error_code & ec) noexcept
bool has(handle_t proc, std::error_code &) noexcept
{
return ::getpgid(proc) == grp;
}

View File

@@ -104,8 +104,7 @@ void sigchld_service::_handle_signal(const boost::system::error_code & ec)
_signal_set.async_wait(
[this](const boost::system::error_code & ec, int)
{
_strand.post([ec]{});
this->_handle_signal(ec);
_strand.post([this, ec]{this->_handle_signal(ec);});
});
}
}

View File

@@ -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<Clock, Duration>& 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<std::chrono::seconds>(dur).count();
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(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;
@@ -87,7 +117,7 @@ template< class Clock, class Duration >
inline bool wait_until(
const child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& time_out) noexcept
const std::chrono::time_point<Clock, Duration>& time_out)
{
std::error_code ec;
bool b = wait_until(p, exit_code, time_out, ec);
@@ -109,7 +139,7 @@ template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time) noexcept
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);

View File

@@ -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<Clock, Duration>& 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<std::chrono::seconds>(dur).count();
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(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 >

View File

@@ -144,6 +144,18 @@ public:
return _sink.write_some(buffers);
}
template<typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _source.read_some(buffers, ec);
}
template<typename MutableBufferSequence>
std::size_t write_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _sink.write_some(buffers, ec);
}
native_handle_type native_source() const {return const_cast<boost::asio::windows::stream_handle&>(_source).native_handle();}
native_handle_type native_sink () const {return const_cast<boost::asio::windows::stream_handle&>(_sink ).native_handle();}
@@ -155,7 +167,7 @@ public:
const MutableBufferSequence & buffers,
ReadHandler &&handler)
{
_source.async_read_some(buffers, std::forward<ReadHandler>(handler));
return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence,
@@ -166,7 +178,7 @@ public:
const ConstBufferSequence & buffers,
WriteHandler && handler)
{
_sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
const handle_type & sink () const & {return _sink;}

View File

@@ -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_;
}
};

View File

@@ -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<void*>(&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;

View File

@@ -9,68 +9,184 @@
#include <boost/winapi/config.hpp>
#include <boost/winapi/basic_types.hpp>
#include <boost/winapi/dll.hpp>
#include <boost/winapi/overlapped.hpp>
#if defined( BOOST_USE_WINDOWS_H )
#include <windows.h>
#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 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 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_ ( WINAPI *query_information_job_object_p)(
typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *query_information_job_object_p)(
::boost::winapi::HANDLE_,
JOBOBJECTINFOCLASS_,
void *,
@@ -90,17 +206,20 @@ typedef ::boost::winapi::BOOL_ ( WINAPI *query_information_job_object_p)(
::boost::winapi::DWORD_ *);
inline ::boost::winapi::BOOL_ WINAPI query_information_job_object(
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<query_information_job_object_p>(::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<query_information_job_object_p>(::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_ WINAPI query_information_job_object(
_In_ DWORD cbJobObjectInfoLength
);*/
typedef ::boost::winapi::BOOL_ ( WINAPI *set_information_job_object_p)(
typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *set_information_job_object_p)(
::boost::winapi::HANDLE_,
JOBOBJECTINFOCLASS_,
void *,
@@ -118,22 +237,25 @@ typedef ::boost::winapi::BOOL_ ( WINAPI *set_information_job_object_p)(
}
inline ::boost::winapi::BOOL_ WINAPI set_information_job_object(
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<set_information_job_object_p>(::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<set_information_job_object_p>(::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_ */

View File

@@ -55,9 +55,9 @@ inline boost::filesystem::path search_path(
for (auto & ext : extensions)
boost::to_lower(ext);
for (const boost::filesystem::path & pp : path)
for (const boost::filesystem::path & pp_ : path)
{
auto p = pp / filename;
auto p = pp_ / filename;
for (boost::filesystem::path ext : extensions)
{
boost::filesystem::path pp = p;

View File

@@ -34,7 +34,7 @@ struct create_no_window_ : public ::boost::process::detail::handler_base
template <class Executor>
void on_setup(Executor &exec) const
{
exec.creation_flags |= ::boost::detail::winapi::CREATE_NO_WINDOW_;
exec.creation_flags |= ::boost::winapi::CREATE_NO_WINDOW_;
}
};

View File

@@ -7,6 +7,7 @@
#define BOOST_PROCESS_DETAIL_WINDOWS_WAIT_GROUP_HPP_
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/windows/group_handle.hpp>
#include <boost/winapi/jobs.hpp>
#include <boost/winapi/wait.hpp>
#include <chrono>
@@ -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<void *>(&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<std::chrono::milliseconds>(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<std::chrono::milliseconds>(
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<Rep, Period>& rel_time,
std::error_code &ec)
{
return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
auto timeout = wait_impl(p, ec, ms.count());
return !ec && !timeout;
}
template< class Rep, class Period >

View File

@@ -94,8 +94,8 @@ struct entry : const_entry<Char, Environment>
explicit entry(string_type&& name, pointer data, environment_t & env) :
father(std::move(name), data, env) {}
explicit entry(string_type &&name, environment_t & env) :
father(std::move(name), env) {}
explicit entry(string_type &&name, environment_t & env_) :
father(std::move(name), env_) {}
entry(const entry&) = default;
entry& operator=(const entry&) = default;

View File

@@ -26,10 +26,28 @@ namespace boost {
</programlisting>
\endxmlonly
*/
namespace boost { namespace process { namespace detail {
namespace boost {
namespace filesystem { class path; }
namespace process {
namespace detail {
struct exe_
{
template<typename = void>
inline exe_setter_<typename boost::filesystem::path::value_type> operator()(const boost::filesystem::path & pth) const
{
return exe_setter_<typename boost::filesystem::path::value_type>(pth.native());
}
template<typename = void>
inline exe_setter_<typename boost::filesystem::path::value_type> operator=(const boost::filesystem::path & pth) const
{
return exe_setter_<typename boost::filesystem::path::value_type>(pth.native());
}
template<typename Char>
inline exe_setter_<Char> operator()(const Char *s) const
{

View File

@@ -85,6 +85,8 @@ inline int system_impl(
return -1;
ios.run();
if (c.running())
c.wait();
return c.exit_code();
}

View File

@@ -81,7 +81,9 @@ test-suite with-valgrind :
[ run exit_code.cpp program_options system filesystem : : sparring_partner ]
[ run extensions.cpp system filesystem : : sparring_partner ]
[ run env.cpp program_options system filesystem : : sparring_partner ]
[ run group.cpp system thread filesystem : : sub_launch ]
[ run group.cpp system thread filesystem : : sub_launch ]
[ run group.cpp system thread filesystem : : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
[ run group_wait.cpp system thread filesystem : : sparring_partner ]
[ run run_exe.cpp filesystem : : sparring_partner ]
[ run run_exe_path.cpp filesystem : : sparring_partner ]
[ run search_path.cpp filesystem system : : : <target-os>windows:<source>shell32 ]

View File

@@ -28,14 +28,6 @@
#include <istream>
#include <iostream>
#include <cstdlib>
#if defined(BOOST_WINDOWS_API)
# include <windows.h>
typedef boost::asio::windows::stream_handle pipe_end;
#elif defined(BOOST_POSIX_API)
# include <sys/wait.h>
# include <unistd.h>
typedef boost::asio::posix::stream_descriptor pipe_end;
#endif
namespace bp = boost::process;

109
test/group_wait.cpp Normal file
View File

@@ -0,0 +1,109 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_TEST_MAIN
#define BOOST_TEST_IGNORE_SIGCHLD
#include <boost/test/included/unit_test.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/process/error.hpp>
#include <boost/process/io.hpp>
#include <boost/process/args.hpp>
#include <boost/process/child.hpp>
#include <boost/process/group.hpp>
#include <system_error>
#include <string>
#include <thread>
#include <istream>
#include <iostream>
#include <cstdlib>
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5))
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
bp::group g;
bp::child c1(
master_test_suite().argv[1],
"--wait", "1",
g,
ec
);
bp::child c2(
master_test_suite().argv[1],
"--wait", "1",
g,
ec
);
BOOST_CHECK(c1.running());
BOOST_CHECK(c2.running());
BOOST_REQUIRE(!ec);
BOOST_REQUIRE(c1.in_group());
BOOST_REQUIRE(c2.in_group());
g.wait();
BOOST_CHECK(!c1.running());
BOOST_CHECK(!c2.running());
}
BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
bp::group g;
bp::child c1(
master_test_suite().argv[1],
"--wait", "1",
g,
ec
);
bp::child c2(
master_test_suite().argv[1],
"--wait", "3",
g,
ec
);
BOOST_CHECK(c1.running());
BOOST_CHECK(c2.running());
BOOST_REQUIRE(!ec);
BOOST_REQUIRE(c1.in_group());
BOOST_REQUIRE(c2.in_group());
BOOST_CHECK(!g.wait_for(std::chrono::seconds(2), ec));
BOOST_CHECK_MESSAGE(!ec, std::to_string(ec.value()) + " == " + ec.message());
BOOST_CHECK(!c1.running());
BOOST_CHECK(c2.running());
BOOST_CHECK(g.wait_for(std::chrono::seconds(5), ec));
BOOST_CHECK_MESSAGE(!ec, std::to_string(ec.value()) + " == " + ec.message());
BOOST_CHECK(!c1.running());
BOOST_CHECK(!c2.running());
}

View File

@@ -67,15 +67,15 @@ BOOST_AUTO_TEST_CASE(implicit_async_io, *boost::unit_test::timeout(2))
std::future<std::string> fut;
std::error_code ec;
bp::system(
int res = bp::system(
master_test_suite().argv[1],
"test", "--echo-stdout", "abc",
bp::std_out > fut,
ec
);
BOOST_REQUIRE(!ec);
BOOST_REQUIRE(fut.valid());
BOOST_CHECK_EQUAL(res, 0);
BOOST_CHECK(boost::starts_with(
fut.get(), "abc"));
}