2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 04:42:24 +00:00

Compare commits

...

74 Commits

Author SHA1 Message Date
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
8e8d36772e Merge pull request #151 from amerry/is-running-fixes
is_running fixes
2018-02-26 13:53:41 +01:00
Alex Merry
ec04919825 Rename tests to reflect what they do 2018-02-22 14:32:54 +00:00
Alex Merry
6625999765 Check the "still running" status code does not clash with WIFSIGNALED
This makes the static_assert reflect the actual assumptions in the code.
2018-02-22 14:31:22 +00:00
Alex Merry
0d3688aca5 Ignore stopped processes
is_running should only concern itself with terminated processes, not
stopped processes (which might be continued later).
2018-02-22 14:12:46 +00:00
Klemens Morgenstern
40be786c43 Merge pull request #150 from amerry/async-exit-status
Return the same exit status in the async handler as the sync method
2018-02-22 13:43:06 +01:00
Klemens Morgenstern
d4a0444223 Merge pull request #147 from amerry/args
Always set arguments on POSIX
2018-02-22 13:05:33 +01:00
Klemens Morgenstern
f99cfe77f4 Merge pull request #148 from pepsiman/patch-1
Fix grammar in tutorial
2018-02-22 12:59:52 +01:00
Alex Merry
ed32531369 Return the same exit status in the async handler as the sync method
Previously, if the process terminated via a signal on posix, the async
handler would provide an exit status of 0.
2018-02-22 11:53:42 +00:00
Malcolm Parsons
751af041cd Fix grammar in tutorial 2018-02-22 10:29:59 +00:00
Alex Merry
a0ceebd59f Fix copyright headers
Insofar as there are copyrights on these changes, they are owned by my
employer, as I'm doing this on company time.
2018-02-22 09:02:02 +00:00
Klemens Morgenstern
b0b37f2ce6 Merge pull request #146 from amerry/test-for-sigchld-fix
Add unit tests for async exit watching with multiple io_contexts
2018-02-21 17:50:27 +01:00
Alex Merry
cf1f904ae2 Fix compilation on MSVC 2018-02-21 15:53:46 +00:00
Alex Merry
8aaf53d76d Always set arguments on POSIX
Explicitly specifying an executable (either with boost::filesystem::path
or boost::process::exe) and no arguments causes NULL to be passed as the
argument list.

Not only is this unexpected behaviour for the child process (which
doesn't even have argv[0]), it is not portable across UNIX systems. From
the execve(2) man page on Linux:

"On Linux, either argv or envp can be specified as NULL, which has the
same effect as specifying these arguments as a pointer to a list
containing a single NULL pointer.  Do not take advantage of this
misfeature!  It is nonstandard and nonportable:  on  most other UNIX
systems doing this will result in an error (EFAULT)."
2018-02-21 15:43:32 +00:00
Alex Merry
76c03ded89 Add unit tests for async exit watching with multiple io_contexts
These tests fail without the commit "Only reap children we are watching
for when handling SIGCHLD" applied, and succeed with it applied.
2018-02-21 15:13:28 +00:00
Klemens Morgenstern
e6fa19b4c5 Merge pull request #145 from amerry/async-wait-fix
Only reap children we are watching for when handling SIGCHLD
2018-02-20 19:53:54 +01:00
Alex Merry
92ee239891 Only reap children we are watching for when handling SIGCHLD
There may be other io_context instances with child instances, and child
instances with no associated io_context. If we pass 0 to ::waitpid(), we
will reap their processes as well, without updating the state of the
corresponding child instance.

Instead, we call `::waitpid` once for each child we are watching for.
This has some amount of overhead (multiple system calls), but ensures
correct behaviour (providing nothing other than an asio::signal_set is
watching for SIGCHLD).
2018-02-20 14:32:33 +00:00
Klemens Morgenstern
c37e2a7524 Merge pull request #142 from cls/search_path_returns_dir
Fix search_path not to return directories on POSIX
2018-02-14 15:41:58 +01:00
Connor Lane Smith
a610fe74ff Fix search_path not to return directories on POSIX 2018-02-14 13:56:34 +00:00
Klemens Morgenstern
ea49952da2 Merge pull request #141 from markus-t314/fix_issue139
Fix: Inconsistent behaviour in various overloaded functions/methods #139
2018-02-09 21:15:19 +01:00
Markus Tillinger
a55946eb5d Fix: Inconsistent behaviour in various overloaded functions/methods #139
Removed duplicated code in overloaded functions.
Replaced system_clock with steady_clock.
2018-02-08 21:49:49 +01:00
Klemens Morgenstern
9f6c338631 Merge pull request #140 from klemens-morgenstern/develop
master update
2018-02-07 00:19:10 +01:00
Klemens David Morgenstern
60302c0017 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-02-06 20:20:18 +01:00
Klemens David Morgenstern
08eaf8b7a1 added BOOST_NO_ANSI_APIS support, closes #26 2018-02-06 20:20:02 +01:00
Klemens Morgenstern
cc70ec9362 Update pipe_in.hpp 2018-02-06 10:29:16 +01:00
Klemens Morgenstern
b58ecc7c9d Update async_in.hpp 2018-02-06 10:27:16 +01:00
Klemens Morgenstern
668cbcdaf4 Update windows.hpp 2018-02-06 10:23:02 +01:00
Klemens David Morgenstern
6d7cbd0989 closes #83 2018-02-06 00:32:55 +01:00
Klemens David Morgenstern
0764f788a6 typo fix 2018-02-06 00:24:45 +01:00
Klemens David Morgenstern
2b95dd7011 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-02-06 00:08:26 +01:00
Klemens David Morgenstern
ae380c30ad added CREATE_NO_WINDOW flags, closes #129 2018-02-06 00:07:35 +01:00
Klemens Morgenstern
d2265890bd Merge pull request #138 from markus-t314/consistent_wait_handling
Fixed inconsistent posix-wait-handling
2018-02-05 23:42:04 +01:00
Klemens Morgenstern
831d49c1b3 Merge pull request #137 from markus-t314/fix_issue136
Fix: [posix] inconsistent exit status when child received signal #136
2018-02-05 23:38:59 +01:00
Klemens Morgenstern
6935c53510 Merge pull request #132 from egorpugin/patch-1
Fix initialization of atomic var. This fixes mingw build.
2018-02-05 23:34:11 +01:00
Klemens David Morgenstern
f30d90a179 Merge remote-tracking branch 'remotes/boostorg/develop' into develop 2018-02-05 23:32:17 +01:00
Klemens Morgenstern
55cfcecfb8 Merge pull request #27 from adrianimboden/develop
fix use of uninitalized variable
2018-02-05 23:16:21 +01:00
Klemens Morgenstern
233f46a2cb Merge pull request #30 from cinghiale/patch-1
update the code snippets to reflect the doc
2018-02-05 23:15:15 +01:00
Markus Tillinger
342554b3d8 Fixed inconsistent posix-wait-handling
Refactored: Removed duplicated code
Made wait_until compatible with steady_clock
2018-02-03 22:11:37 +01:00
Markus Tillinger
7aa812a0e1 Fix: [posix] inconsistent exit status when child received signal #136 2018-02-03 17:47:14 +01:00
Klemens Morgenstern
c4ffd0c18d Merge pull request #135 from klemens-morgenstern/develop
Appveyor fixes
2018-02-02 23:26:12 +01:00
Klemens David Morgenstern
a411f06dc4 Merge remote-tracking branch 'remotes/origin/master' into develop
# Conflicts:
#	test/Jamfile.jam
#	test/async_system_stackless.cpp
2018-02-02 22:37:32 +01:00
Klemens David Morgenstern
d085262076 added /bigobj flag for msvc 2018-02-02 22:14:42 +01:00
Klemens David Morgenstern
0396740467 splitted up the async_system tests 2018-02-02 21:52:14 +01:00
Klemens Morgenstern
0fd7de9481 Merge pull request #134 from hrayrbabajanyan/patch-1
eval_exit_status would return 0 in case if the child received SIBABRT
2018-02-01 20:44:00 +01:00
hrayrbabajanyan
ba790dad0a eval_exit_status would return 0 in case if the child received SIBABRT 2018-02-01 02:53:24 +04:00
David Mugnai
f2e8776965 update the code snippets to reflect the doc 2018-01-23 23:35:54 +01:00
Adrian Imboden
444d5eb702 fix use of uninitalized variable 2018-01-06 13:36:42 +01:00
Egor Pugin
3e12e989ab Fix initialization of atomic var. This fixes mingw build. 2018-01-06 14:04:32 +03:00
43 changed files with 823 additions and 676 deletions

View File

@@ -108,8 +108,6 @@ struct async_foo : __handler__, __require_io_service__
}
};
```
[caution All async_handlers use one signal(SIGCHLD) on posix, which is only guaranteed to work when all use the same `io_service`]
[note Inheriting [globalref boost::process::extend::require_io_service require_io_service] is necessary, so [funcref boost::process::system system] provides one.]
Additionally the handler can provide a function that is invoked when the child process exits. This is done through __async_handler__.
@@ -135,6 +133,8 @@ struct async_bar : __handler, __async_handler__
[caution `on_exit_handler` does not default and is always required when [classref boost::process::extend::async_handler async_handler] is inherited. ]
[caution `on_exit_handler` uses `boost::asio::signal_set` to listen for SIGCHLD on posix. The application must not also register a signal handler for SIGCHLD using functions such as `signal()` or `sigaction()` (but using `boost::asio::signal_set` is fine). ]
[endsect]
[section:error Error handling]

View File

@@ -104,8 +104,8 @@ This also includes to add a file suffix on windows, such as `.exe` or `.bat`.]
[section:launch_mode Launch functions]
Given that in our example used the [funcref boost::process::system system] function,
our program will wait until the child process is completed. This maybe unwanted,
Given that our example used the [funcref boost::process::system system] function,
our program will wait until the child process is completed. This may be unwanted,
especially since compiling can take a while.
In order to avoid that, boost.process provides several ways to launch a process.
@@ -151,10 +151,10 @@ This can be avoided by calling __detach__ beforehand]
Until now, we have assumed that everything works out, but it is not impossible,
that "g++" is not present. That will cause the launch of the process to fail.
The default behaviour of all functions is to throw an
The default behaviour of all functions is to throw a
[@http://en.cppreference.com/w/cpp/error/system_error std::system_error] on failure.
As with many other functions in this library, passing an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code]
will change the behaviour, so that instead of throwing an exception, the error will be a assigned to the error code.
will change the behaviour, so that instead of throwing an exception, the error will be assigned to the error code.
```
std::error_code ec;
@@ -288,7 +288,6 @@ asio_async_read(ap, asio_buffer(buf),
[](const boost::system::error_code &ec, std::size_t size){});
ios.run();
c.wait();
int result = c.exit_code();
```
@@ -297,12 +296,11 @@ 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);
ios.run();
c.wait();
int result = c.exit_code();
```

View File

@@ -118,7 +118,9 @@ chlid c2("ls", on_exit=exit_code);
\note The handler is not invoked when the launch fails.
\warning When used \ref ignore_error it might get invoked on error.
\warning All `on_exit` use one signal(SIGCHLD) on posix, which is only guaranteed to work when all use the same `io_context`.
\warning `on_exit` uses `boost::asio::signal_set` to listen for `SIGCHLD` on posix, and so has the
same restrictions as that class (do not register a handler for `SIGCHLD` except by using
`boost::asio::signal_set`).
*/
constexpr static ::boost::process::detail::on_exit_ on_exit{};
#endif

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

@@ -103,72 +103,52 @@ public:
bool running()
{
if (valid() && !_exited())
{
int code = -1;
auto res = boost::process::detail::api::is_running(_child_handle, code);
if (!res && !_exited())
_exit_status->store(code);
return res;
}
return false;
std::error_code ec;
bool b = running(ec);
boost::process::detail::throw_error(ec, "running error");
return b;
}
void terminate()
{
if (valid() && running())
boost::process::detail::api::terminate(_child_handle);
_terminated = true;
std::error_code ec;
terminate(ec);
boost::process::detail::throw_error(ec, "terminate error");
}
void wait()
{
if (!_exited() && valid())
{
int exit_code = 0;
boost::process::detail::api::wait(_child_handle, exit_code);
_exit_status->store(exit_code);
}
std::error_code ec;
wait(ec);
boost::process::detail::throw_error(ec, "wait error");
}
template< class Rep, class Period >
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time)
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time)
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
std::error_code ec;
bool b = wait_for(rel_time, ec);
boost::process::detail::throw_error(ec, "wait_for error");
return b;
}
template< class Clock, class Duration >
bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time )
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
std::error_code ec;
bool b = wait_until(timeout_time, ec);
boost::process::detail::throw_error(ec, "wait_until error");
return b;
}
bool running(std::error_code & ec) noexcept
{
if (valid() && !_exited())
{
int code;
auto res = boost::process::detail::api::is_running(_child_handle, code, ec);
int exit_code = 0;
auto res = boost::process::detail::api::is_running(_child_handle, exit_code, ec);
if (!res && !_exited())
_exit_status->store(code);
_exit_status->store(exit_code);
return res;
}
@@ -194,17 +174,9 @@ public:
}
template< class Rep, class Period >
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept
bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept
{
if (!_exited())
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_for(_child_handle, exit_code, rel_time, ec);
if (!b)
return false;
_exit_status->store(exit_code);
}
return true;
return wait_until(std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Clock, class Duration >

View File

@@ -82,6 +82,17 @@ inline void throw_last_error()
throw process_error(get_last_error());
}
inline void throw_error(const std::error_code& ec)
{
if (ec)
throw process_error(ec);
}
inline void throw_error(const std::error_code& ec, const char* msg)
{
if (ec)
throw process_error(ec, msg);
}
template<typename Char> constexpr Char null_char();
template<> constexpr char null_char<char> (){return '\0';}

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();
@@ -85,7 +85,9 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
if (::dup2(pipe->native_source(), STDIN_FILENO) == -1)
exec.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(pipe->native_source());
if (pipe->native_source() != STDIN_FILENO)
::close(pipe->native_source());
::close(pipe->native_sink());
}
};

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();
@@ -89,6 +89,8 @@ struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
exec.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(pipe->native_sink());
::close(pipe->native_source());
}
};
@@ -110,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;
@@ -118,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))
{
@@ -161,6 +163,7 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
exec.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(pipe->native_sink());
::close(pipe->native_source());
}
};

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

@@ -118,12 +118,8 @@ struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
else
exec.exe = &exe.front();
if (!args.empty())
{
cmd_impl = make_cmd();
exec.cmd_line = cmd_impl.data();
}
cmd_impl = make_cmd();
exec.cmd_line = cmd_impl.data();
}
static exe_cmd_init exe_args(std::string && exe, std::vector<std::string> && args) {return exe_cmd_init(std::move(exe), std::move(args));}
static exe_cmd_init cmd (std::string && cmd)
@@ -163,8 +159,10 @@ std::vector<char*> exe_cmd_init<char>::make_cmd()
if (!exe.empty())
vec.push_back(&exe.front());
for (auto & v : args)
vec.push_back(&v.front());
if (!args.empty()) {
for (auto & v : args)
vec.push_back(&v.front());
}
vec.push_back(nullptr);

View File

@@ -6,14 +6,13 @@
#ifndef BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/posix/child_handle.hpp>
#include <system_error>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
struct group_handle
{
pid_t grp = -1;
@@ -26,7 +25,6 @@ struct group_handle
{
}
group_handle() = default;
~group_handle() = default;
@@ -38,7 +36,6 @@ struct group_handle
group_handle &operator=(const group_handle & c) = delete;
group_handle &operator=(group_handle && c)
{
grp = c.grp;
c.grp = -1;
return *this;
@@ -59,26 +56,17 @@ 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;
}
bool valid() const
{
return grp != -1;
}
};
inline void terminate(group_handle &p)
{
if (::killpg(p.grp, SIGKILL) == -1)
boost::process::detail::throw_last_error("killpg(2) failed");
p.grp = -1;
}
inline void terminate(group_handle &p, std::error_code &ec) noexcept
{
if (::killpg(p.grp, SIGKILL) == -1)
@@ -89,15 +77,18 @@ inline void terminate(group_handle &p, std::error_code &ec) noexcept
p.grp = -1;
}
inline void terminate(group_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::detail::throw_error(ec, "killpg(2) failed in terminate");
}
inline bool in_group()
{
return true;
}
}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_GROUP_HPP_ */

View File

@@ -18,6 +18,7 @@
#include <boost/process/detail/posix/sigchld_service.hpp>
#include <boost/process/detail/posix/is_running.hpp>
#include <functional>
#include <type_traits>
@@ -95,11 +96,11 @@ struct io_context_ref : handler_base_ext
auto & es = exec.exit_status;
auto wh = [funcs, es](int val, const std::error_code & ec)
{
es->store(val);
{
es->store(val);
for (auto & func : funcs)
func(WEXITSTATUS(val), ec);
};
func(::boost::process::detail::posix::eval_exit_status(val), ec);
};
sigchld_service.async_wait(exec.pid, std::move(wh));
}

View File

@@ -13,37 +13,22 @@
namespace boost { namespace process { namespace detail { namespace posix {
// Use the "stopped" state (WIFSTOPPED) to indicate "not terminated".
// This bit arrangement of status codes is not guaranteed by POSIX, but (according to comments in
// the glibc <bits/waitstatus.h> header) is the same across systems in practice.
constexpr int still_active = 0x7F;
static_assert(!WIFEXITED(still_active), "Internal Error");
static_assert(!WIFEXITED(still_active) && !WIFSIGNALED(still_active), "Internal Error");
inline bool is_running(const child_handle &p, int & exit_code)
inline bool is_running(int code)
{
int status;
auto ret = ::waitpid(p.pid, &status, WNOHANG|WUNTRACED);
if (ret == -1)
{
if (errno != ECHILD) //because it no child is running, than this one isn't either, obviously.
::boost::process::detail::throw_last_error("is_running error");
return false;
}
else if (ret == 0)
return true;
else //exited
{
if (WIFEXITED(status))
exit_code = status;
return false;
}
return !WIFEXITED(code) && !WIFSIGNALED(code);
}
inline bool is_running(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
int status;
auto ret = ::waitpid(p.pid, &status, WNOHANG|WUNTRACED);
auto ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == -1)
{
if (errno != ECHILD) //because it no child is running, than this one isn't either, obviously.
@@ -55,22 +40,36 @@ inline bool is_running(const child_handle &p, int & exit_code, std::error_code &
else
{
ec.clear();
if (WIFEXITED(status))
if (!is_running(status))
exit_code = status;
return false;
}
}
inline bool is_running(int code)
inline bool is_running(const child_handle &p, int & exit_code)
{
return !WIFEXITED(code);
std::error_code ec;
bool b = is_running(p, exit_code, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in is_running");
return b;
}
inline int eval_exit_status(int code)
{
return WEXITSTATUS(code);
if (WIFEXITED(code))
{
return WEXITSTATUS(code);
}
else if (WIFSIGNALED(code))
{
return WTERMSIG(code);
}
else
{
return code;
}
}
}}}}

View File

@@ -27,7 +27,9 @@ inline boost::filesystem::path search_path(
for (const boost::filesystem::path & pp : path)
{
auto p = pp / filename;
if (!::access(p.c_str(), X_OK))
boost::system::error_code ec;
bool file = boost::filesystem::is_regular_file(p, ec);
if (!ec && file && ::access(p.c_str(), X_OK) == 0)
return p;
}
return "";

View File

@@ -77,26 +77,34 @@ void sigchld_service::_handle_signal(const boost::system::error_code & ec)
r.second(-1, ec_);
return;
}
int status;
int pid = ::waitpid(0, &status, WNOHANG);
auto itr = std::find_if(_receivers.begin(), _receivers.end(),
[&pid](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
{
return p.first == pid;
});
if (itr != _receivers.cend())
{
_strand.get_io_context().wrap(itr->second)(status, ec_);
_receivers.erase(itr);
for (auto & r : _receivers) {
int status;
int pid = ::waitpid(r.first, &status, WNOHANG);
if (pid < 0) {
// error (eg: the process no longer exists)
r.second(-1, get_last_error());
r.first = 0; // mark for deletion
} else if (pid == r.first) {
r.second(status, ec_);
r.first = 0; // mark for deletion
}
// otherwise the process is still around
}
_receivers.erase(std::remove_if(_receivers.begin(), _receivers.end(),
[](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
{
return p.first == 0;
}),
_receivers.end());
if (!_receivers.empty())
{
_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

@@ -19,15 +19,6 @@
namespace boost { namespace process { namespace detail { namespace posix {
inline void terminate(const child_handle &p)
{
if (::kill(p.pid, SIGKILL) == -1)
boost::process::detail::throw_last_error("kill(2) failed");
int status;
::waitpid(p.pid, &status, 0); //just to clean it up
}
inline void terminate(const child_handle &p, std::error_code &ec) noexcept
{
if (::kill(p.pid, SIGKILL) == -1)
@@ -39,6 +30,13 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
::waitpid(p.pid, &status, 0); //just to clean it up
}
inline void terminate(const child_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::detail::throw_error(ec, "kill(2) failed");
}
}}}}
#endif

View File

@@ -19,21 +19,6 @@
namespace boost { namespace process { namespace detail { namespace posix {
inline void wait(const child_handle &p, int & exit_code)
{
pid_t ret;
int status;
do
{
ret = ::waitpid(p.pid, &status, 0);
} while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
if (WIFSIGNALED(status))
throw process_error(std::error_code(), "process terminated due to receipt of a signal");
exit_code = status;
}
inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
pid_t ret;
@@ -44,87 +29,6 @@ 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)));
if (ret == -1)
ec = boost::process::detail::get_last_error();
else
{
ec.clear();
exit_code = status;
}
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time)
{
pid_t ret;
int status;
auto start = std::chrono::system_clock::now();
auto time_out = start + rel_time;
bool timed_out;
do
{
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0)
{
timed_out = std::chrono::system_clock::now() >= time_out;
if (timed_out)
return false;
}
}
while ((ret == 0) ||
((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
exit_code = status;
return true;
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code & ec) noexcept
{
pid_t ret;
int status;
auto start = std::chrono::system_clock::now();
auto time_out = start + rel_time;
bool timed_out;
do
{
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0)
{
timed_out = std::chrono::system_clock::now() >= time_out;
if (timed_out)
return false;
}
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
if (timed_out && (ret == -1))
return false;
if (ret == -1)
ec = boost::process::detail::get_last_error();
@@ -133,49 +37,15 @@ inline bool wait_for(
ec.clear();
exit_code = status;
}
return true;
}
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)
inline void wait(const child_handle &p, int & exit_code) noexcept
{
pid_t ret;
int status;
bool timed_out;
do
{
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0)
{
timed_out = std::chrono::system_clock::now() >= time_out;
if (timed_out)
return false;
}
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
if (timed_out && !WIFEXITED(status))
return false;
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
exit_code = status;
return true;
std::error_code ec;
wait(p, exit_code, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait");
}
template< class Clock, class Duration >
inline bool wait_until(
const child_handle &p,
@@ -183,7 +53,6 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
pid_t ret;
int status;
@@ -194,20 +63,15 @@ inline bool wait_until(
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0)
{
timed_out = std::chrono::system_clock::now() >= time_out;
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
}
}
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
if (timed_out && !WIFEXITED(status))
return false;
if (ret == -1)
ec = boost::process::detail::get_last_error();
else
@@ -219,6 +83,40 @@ inline bool wait_until(
return true;
}
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)
{
std::error_code ec;
bool b = wait_until(p, exit_code, time_out, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_until");
return b;
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code & ec) noexcept
{
return wait_until(p, exit_code, std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_for");
return b;
}
}}}}
#endif

View File

@@ -12,26 +12,13 @@
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/posix/group_handle.hpp>
#include <chrono>
#include <system_error>
#include <sys/types.h>
#include <sys/wait.h>
namespace boost { namespace process { namespace detail { namespace posix {
inline void wait(const group_handle &p)
{
pid_t ret;
int status;
do
{
ret = ::waitpid(-p.grp, &status, 0);
} while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
if (WIFSIGNALED(status))
throw process_error(std::error_code(), "process group terminated due to receipt of a signal");
}
inline void wait(const group_handle &p, std::error_code &ec) noexcept
{
pid_t ret;
@@ -42,50 +29,63 @@ inline void wait(const group_handle &p, std::error_code &ec) noexcept
ret = ::waitpid(-p.grp, &status, 0);
}
while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
ec = boost::process::detail::get_last_error();
else if (WIFSIGNALED(status))
ec = std::make_error_code(std::errc::no_such_process);
else
ec.clear();
}
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time)
inline void wait(const group_handle &p) noexcept
{
std::error_code ec;
wait(p, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait");
}
template< class Clock, class Duration >
inline bool wait_until(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
pid_t ret;
int status;
auto start = std::chrono::system_clock::now();
auto time_out = start + rel_time;
bool timed_out;
bool time_out_occured = false;
do
{
ret = ::waitpid(-p.grp, &status, WUNTRACED | WNOHANG);
if (std::chrono::system_clock::now() >= time_out)
ret = ::waitpid(-p.grp, &status, WNOHANG);
if (ret == 0)
{
time_out_occured = true;
break;
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
}
}
while (((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)));
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
if (WIFSIGNALED(status))
throw process_error(std::error_code(), "process group terminated due to receipt of a signal");
ec = boost::process::detail::get_last_error();
else
ec.clear();
return !time_out_occured;
return true;
}
template< class Clock, class Duration >
inline bool wait_until(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& time_out) noexcept
{
std::error_code ec;
bool b = wait_until(p, time_out, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_until");
return b;
}
template< class Rep, class Period >
inline bool wait_for(
@@ -93,104 +93,18 @@ inline bool wait_for(
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code & ec) noexcept
{
pid_t ret;
int status;
auto start = std::chrono::system_clock::now();
auto time_out = start + rel_time;
bool time_out_occured = false;
do
{
ret = ::waitpid(-p.grp, &status, WUNTRACED | WNOHANG);
if (std::chrono::system_clock::now() >= time_out)
{
time_out_occured = true;
break;
}
}
while (((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
ec = boost::process::detail::get_last_error();
else if (WIFSIGNALED(status))
ec = std::make_error_code(std::errc::no_such_process);
else
ec.clear();
return !time_out_occured;
return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Rep, class Period >
inline bool wait_until(
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& time_out)
const std::chrono::duration<Rep, Period>& rel_time) noexcept
{
pid_t ret;
int status;
bool time_out_occured = false;
do
{
ret = ::waitpid(-p.grp, &status, WUNTRACED | WNOHANG);
if (std::chrono::system_clock::now() >= time_out)
{
time_out_occured = true;
break;
}
}
while (((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
boost::process::detail::throw_last_error("waitpid(2) failed");
if (WIFSIGNALED(status))
throw process_error(std::error_code(), "process group terminated due to receipt of a signal");
return !time_out_occured;
}
template< class Rep, class Period >
inline bool wait_until(
const group_handle &p,
const std::chrono::duration<Rep, Period>& time_out,
std::error_code & ec) noexcept
{
pid_t ret;
int status;
bool time_out_occured = false;
do
{
ret = ::waitpid(-p.grp, &status, WUNTRACED | WNOHANG);
if (std::chrono::system_clock::now() >= time_out)
{
time_out_occured = true;
break;
}
}
while (((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
ec = boost::process::detail::get_last_error();
else if (WIFSIGNALED(status))
ec = std::make_error_code(std::errc::no_such_process);
else
ec.clear();
return !time_out_occured;
std::error_code ec;
bool b = wait_for(p, rel_time, ec);
boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_for");
return b;
}
}}}}

View File

@@ -27,7 +27,7 @@ inline std::string make_pipe_name()
auto pid = ::boost::winapi::GetCurrentProcessId();
static std::atomic_size_t cnt = 0;
static std::atomic_size_t cnt{0};
name += std::to_string(pid);
name += "_";
name += std::to_string(cnt++);
@@ -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;}
@@ -265,7 +277,11 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
::boost::winapi::HANDLE_ source = ::boost::winapi::create_named_pipe(
#if defined(BOOST_NO_ANSI_APIS)
::boost::process::detail::convert(name).c_str(),
#else
name.c_str(),
#endif
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, 1, 8192, 8192, 0, nullptr);
@@ -277,7 +293,11 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
_source.assign(source);
::boost::winapi::HANDLE_ sink = boost::winapi::create_file(
#if defined(BOOST_NO_ANSI_APIS)
::boost::process::detail::convert(name).c_str(),
#else
name.c_str(),
#endif
::boost::winapi::GENERIC_WRITE_, 0, nullptr,
::boost::winapi::OPEN_EXISTING_,
FILE_FLAG_OVERLAPPED_, //to allow read

View File

@@ -45,11 +45,19 @@ struct file_descriptor
}
file_descriptor(const std::string & path , mode_t mode = read_write)
: file_descriptor(path.c_str(), mode) {}
#if defined(BOOST_NO_ANSI_APIS)
: file_descriptor(::boost::process::detail::convert(path), mode)
#else
: file_descriptor(path.c_str(), mode)
#endif
{}
file_descriptor(const std::wstring & path, mode_t mode = read_write)
: file_descriptor(path.c_str(), mode) {}
file_descriptor(const char* path, mode_t mode = read_write)
#if defined(BOOST_NO_ANSI_APIS)
: file_descriptor(std::string(path), mode)
#else
: _handle(
::boost::winapi::create_file(
path,
@@ -62,8 +70,8 @@ struct file_descriptor
::boost::winapi::FILE_ATTRIBUTE_NORMAL_,
nullptr
))
#endif
{
}
file_descriptor(const wchar_t * path, mode_t mode = read_write)
: _handle(

View File

@@ -103,7 +103,7 @@ struct group_handle
}
group_handle() : group_handle(::boost::winapi::CreateJobObjectA(nullptr, nullptr))
group_handle() : group_handle(::boost::winapi::CreateJobObjectW(nullptr, nullptr))
{
}

View File

@@ -18,22 +18,6 @@ constexpr static ::boost::winapi::DWORD_ still_active = 259;
struct child_handle;
inline bool is_running(const child_handle &p, int & exit_code)
{
::boost::winapi::DWORD_ code;
//single value, not needed in the winapi.
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &code))
::boost::process::detail::throw_last_error("GetExitCodeProcess() failed");
if (code == still_active)
return true;
else
{
exit_code = code;
return false;
}
}
inline bool is_running(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
::boost::winapi::DWORD_ code;
@@ -49,7 +33,15 @@ inline bool is_running(const child_handle &p, int & exit_code, std::error_code &
{
exit_code = code;
return false;
}
}
}
inline bool is_running(const child_handle &p, int & exit_code)
{
std::error_code ec;
bool b = is_running(p, exit_code, ec);
boost::process::detail::throw_error(ec, "GetExitCodeProcess() failed in is_running");
return b;
}
inline bool is_running(int code)

View File

@@ -82,7 +82,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_ (*query_information_job_object_p)(
::boost::winapi::HANDLE_,
JOBOBJECTINFOCLASS_,
void *,
@@ -90,14 +90,14 @@ 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,
::boost::winapi::DWORD_ cbJobObjectInfoLength,
::boost::winapi::DWORD_ *lpReturnLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle("Kernel32.dll");
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);
@@ -110,7 +110,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_ (*set_information_job_object_p)(
::boost::winapi::HANDLE_,
JOBOBJECTINFOCLASS_,
void *,
@@ -118,13 +118,13 @@ 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,
::boost::winapi::DWORD_ cbJobObjectInfoLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle("Kernel32.dll");
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);

View File

@@ -41,11 +41,15 @@ class windows_file_codecvt
wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const override
{
boost::ignore_unused(state);
::boost::winapi::UINT_ codepage = AreFileApisANSI() ?
::boost::winapi::CP_ACP_ :
::boost::winapi::CP_OEMCP_;
int count;
auto codepage =
#if !defined(BOOST_NO_ANSI_APIS)
::boost::winapi::AreFileApisANSI() ?
::boost::winapi::CP_ACP_ :
#endif
::boost::winapi::CP_OEMCP_;
int count = 0;
if ((count = ::boost::winapi::MultiByteToWideChar(codepage,
::boost::winapi::MB_PRECOMPOSED_, from,
static_cast<int>(from_end - from), to, static_cast<int>(to_end - to))) == 0)
@@ -64,11 +68,15 @@ class windows_file_codecvt
char* to, char* to_end, char*& to_next) const override
{
boost::ignore_unused(state);
auto codepage = ::boost::winapi::AreFileApisANSI() ?
auto codepage =
#if !defined(BOOST_NO_ANSI_APIS)
::boost::winapi::AreFileApisANSI() ?
::boost::winapi::CP_ACP_ :
#endif
::boost::winapi::CP_OEMCP_;
int count = 0;
int count;
if ((count = ::boost::winapi::WideCharToMultiByte(codepage,
::boost::winapi::WC_NO_BEST_FIT_CHARS_, from,
static_cast<int>(from_end - from), to, static_cast<int>(to_end - to), 0, 0)) == 0)

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

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

View File

@@ -20,15 +20,6 @@ namespace boost { namespace process { namespace detail { namespace windows {
struct child_handle;
inline void terminate(child_handle &p)
{
if (!::boost::winapi::TerminateProcess(p.process_handle(), EXIT_FAILURE))
boost::process::detail::throw_last_error("TerminateProcess() failed");
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
}
inline void terminate(child_handle &p, std::error_code &ec) noexcept
{
if (!::boost::winapi::TerminateProcess(p.process_handle(), EXIT_FAILURE))
@@ -41,8 +32,12 @@ inline void terminate(child_handle &p, std::error_code &ec) noexcept
}
}
inline void terminate(child_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::detail::throw_error(ec, "TerminateProcess() failed in terminate");
}
}}}}

View File

@@ -20,21 +20,6 @@
namespace boost { namespace process { namespace detail { namespace windows {
inline void wait(child_handle &p, int & exit_code)
{
if (::boost::winapi::WaitForSingleObject(p.process_handle(),
::boost::winapi::infinite) == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
::boost::winapi::DWORD_ _exit_code;
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &_exit_code))
throw_last_error("GetExitCodeProcess() failed");
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
exit_code = static_cast<int>(_exit_code);
}
inline void wait(child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
::boost::winapi::DWORD_ _exit_code = 1;
@@ -56,104 +41,13 @@ inline void wait(child_handle &p, int & exit_code, std::error_code &ec) noexcept
exit_code = static_cast<int>(_exit_code);
}
template< class Rep, class Period >
inline bool wait_for(
child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time)
inline void wait(child_handle &p, int & exit_code)
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.process_handle(),
static_cast<::boost::winapi::DWORD_>(ms.count()));
if (wait_code == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
else if (wait_code == ::boost::winapi::wait_timeout)
return false; //
::boost::winapi::DWORD_ _exit_code;
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &_exit_code))
throw_last_error("GetExitCodeProcess() failed");
exit_code = static_cast<int>(_exit_code);
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
return true;
std::error_code ec;
wait(p, exit_code, ec);
boost::process::detail::throw_error(ec, "wait error");
}
template< class Rep, class Period >
inline bool wait_for(
child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code &ec) noexcept
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.process_handle(),
static_cast<::boost::winapi::DWORD_>(ms.count()));
if (wait_code == ::boost::winapi::wait_failed)
ec = std::error_code(
::boost::winapi::GetLastError(),
std::system_category());
else if (wait_code == ::boost::winapi::wait_timeout)
return false; //
::boost::winapi::DWORD_ _exit_code = 1;
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &_exit_code))
{
ec = std::error_code(
::boost::winapi::GetLastError(),
std::system_category());
return false;
}
else
ec.clear();
exit_code = static_cast<int>(_exit_code);
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
return true;
;
}
template< class Clock, class Duration >
inline bool wait_until(
child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& timeout_time)
{
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
timeout_time - std::chrono::system_clock::now());
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.process_handle(),
static_cast<::boost::winapi::DWORD_>(ms.count()));
if (wait_code == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
else if (wait_code == ::boost::winapi::wait_timeout)
return false;
::boost::winapi::DWORD_ _exit_code;
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &_exit_code))
throw_last_error("GetExitCodeProcess() failed");
exit_code = static_cast<int>(_exit_code);
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
return true;
}
template< class Clock, class Duration >
inline bool wait_until(
child_handle &p,
@@ -163,7 +57,7 @@ inline bool wait_until(
{
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
timeout_time - std::chrono::system_clock::now());
timeout_time - Clock::now());
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.process_handle(),
@@ -174,7 +68,7 @@ inline bool wait_until(
::boost::winapi::GetLastError(),
std::system_category());
else if (wait_code == ::boost::winapi::wait_timeout)
return false;
return false;
::boost::winapi::DWORD_ _exit_code;
if (!::boost::winapi::GetExitCodeProcess(p.process_handle(), &_exit_code))
@@ -188,9 +82,41 @@ inline bool wait_until(
::boost::winapi::CloseHandle(p.proc_info.hProcess);
p.proc_info.hProcess = ::boost::winapi::INVALID_HANDLE_VALUE_;
return true;
;
}
template< class Clock, class Duration >
inline bool wait_until(
child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& timeout_time)
{
std::error_code ec;
bool b = wait_until(p, exit_code, timeout_time, ec);
boost::process::detail::throw_error(ec, "wait_until error");
return b;
}
template< class Rep, class Period >
inline bool wait_for(
child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code &ec) noexcept
{
return wait_until(p, exit_code, std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Rep, class Period >
inline bool wait_for(
child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);
boost::process::detail::throw_error(ec, "wait_for error");
return b;
}
}}}}

View File

@@ -9,20 +9,12 @@
#include <boost/process/detail/config.hpp>
#include <boost/winapi/jobs.hpp>
#include <boost/winapi/wait.hpp>
#include <chrono>
namespace boost { namespace process { namespace detail { namespace windows {
struct group_handle;
inline void wait(const group_handle &p)
{
if (::boost::winapi::WaitForSingleObject(p.handle(),
::boost::winapi::infinite) == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
}
inline void wait(const group_handle &p, std::error_code &ec)
{
if (::boost::winapi::WaitForSingleObject(p.handle(),
@@ -30,38 +22,26 @@ inline void wait(const group_handle &p, std::error_code &ec)
ec = get_last_error();
}
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time)
inline void wait(const group_handle &p)
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.handle(), ms.count());
if (wait_code == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
else if (wait_code == ::boost::winapi::wait_timeout)
return false; //
return true;
std::error_code ec;
wait(p, ec);
boost::process::detail::throw_error(ec, "wait error");
}
template< class Rep, class Period >
inline bool wait_for(
template< class Clock, class Duration >
inline bool wait_until(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time,
const std::chrono::time_point<Clock, Duration>& timeout_time,
std::error_code &ec)
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
std::chrono::milliseconds ms =
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();
@@ -76,44 +56,30 @@ inline bool wait_until(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& timeout_time)
{
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
timeout_time - std::chrono::system_clock::now());
::boost::winapi::DWORD_ wait_code;
wait_code = ::boost::winapi::WaitForSingleObject(p.handle(), ms.count());
if (wait_code == ::boost::winapi::wait_failed)
throw_last_error("WaitForSingleObject() failed");
else if (wait_code == ::boost::winapi::wait_timeout)
return false; //
return true;
std::error_code ec;
bool b = wait_until(p, timeout_time, ec);
boost::process::detail::throw_error(ec, "wait_until error");
return b;
}
template< class Clock, class Duration >
inline bool wait_until(
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& timeout_time,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code &ec)
{
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
timeout_time - std::chrono::system_clock::now());
return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec);
}
::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;
;
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, rel_time, ec);
boost::process::detail::throw_error(ec, "wait_for error");
return b;
}
}}}}

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;
@@ -631,14 +631,17 @@ public:
};
#if !defined(BOOST_NO_ANSI_APIS)
///Definition of the environment for the current process.
typedef basic_native_environment<char> native_environment;
#endif
///Definition of the environment for the current process.
typedef basic_native_environment<wchar_t> wnative_environment;
#if !defined(BOOST_NO_ANSI_APIS)
///Type definition to hold a seperate environment.
typedef basic_environment<char> environment;
#endif
///Type definition to hold a seperate environment.
typedef basic_environment<wchar_t> wenvironment;
@@ -651,8 +654,10 @@ namespace this_process
///Definition of the native handle type.
typedef ::boost::process::detail::api::native_handle_t native_handle_type;
#if !defined(BOOST_NO_ANSI_APIS)
///Definition of the environment for this process.
using ::boost::process::native_environment;
#endif
///Definition of the environment for this process.
using ::boost::process::wnative_environment;
@@ -660,8 +665,10 @@ using ::boost::process::wnative_environment;
inline int get_id() { return ::boost::process::detail::api::get_id();}
///Get the native handle of the current process.
inline native_handle_type native_handle() { return ::boost::process::detail::api::native_handle();}
#if !defined(BOOST_NO_ANSI_APIS)
///Get the enviroment of the current process.
inline native_environment environment() { return ::boost::process:: native_environment(); }
#endif
///Get the enviroment of the current process.
inline wnative_environment wenvironment() { return ::boost::process::wnative_environment(); }
///Get the path environment variable of the current process runs.

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

@@ -23,6 +23,7 @@ namespace boost {
<emphasis>unspecified</emphasis> <globalname alt="boost::process::windows::not_active">not_active</globalname>;
<emphasis>unspecified</emphasis> <globalname alt="boost::process::windows::show">show</globalname>;
<emphasis>unspecified</emphasis> <globalname alt="boost::process::windows::show_normal">show_normal</globalname>;
<emphasis>unspecified</emphasis> <globalname alt="boost::process::windows::create_no_window">create_no_window</globalname>;
}
}
}
@@ -34,7 +35,7 @@ namespace boost {
namespace boost { namespace process {
///Namespace containing the windows exensions.
///Namespace containing the windows extensions.
namespace windows {
///Hides the window and activates another window.
@@ -52,6 +53,8 @@ constexpr ::boost::process::detail::windows::show_window<::boost::winapi::SW_SHO
///Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.
constexpr ::boost::process::detail::windows::show_window<::boost::winapi::SW_SHOWNORMAL_ > show_normal;
///Adds the [CREATE_NO_WINDOW](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx) flag.
constexpr ::boost::process::detail::windows::create_no_window_ create_no_window;
}}}

View File

@@ -15,16 +15,17 @@ if [ os.name ] = NT
{
lib ws2_32 ;
lib shell32 ;
lib Advapi32 ;
}
project : requirements
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
<toolset>msvc:<cxxflags>/bigobj
<target-os>windows:<define>WIN32_LEAN_AND_MEAN
<target-os>linux:<linkflags>-lpthread
<os>NT,<toolset>cw:<library>ws2_32
<os>NT,<toolset>gcc:<library>ws2_32
;
@@ -45,17 +46,25 @@ exe sparring_partner : sparring_partner.cpp program_options system filesystem io
<warnings>off <target-os>windows:<source>shell32
;
exe exit_argc : exit_argc.cpp :
<warnings>off <target-os>windows:<source>shell32
;
exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem : <warnings>off <target-os>windows:<source>shell32 ;
test-suite bare :
[ run environment.cpp system filesystem ]
[ run async_pipe.cpp system filesystem ]
[ run pipe.cpp system filesystem ]
[ run environment.cpp system filesystem ]
[ run async_pipe.cpp system filesystem ]
[ run pipe.cpp system filesystem ]
[ compile no_ansi_apps.cpp ]
[ compile-fail spawn_fail.cpp ]
[ compile-fail async_system_fail.cpp ]
;
test-suite with-valgrind :
[ run async.cpp system thread filesystem : : sparring_partner ]
[ run async_fut.cpp system thread filesystem : : sparring_partner ]
[ run args_handling.cpp system thread filesystem : : exit_argc ]
[ run args_cmd.cpp system filesystem : : sparring_partner ]
[ run wargs_cmd.cpp system filesystem : : sparring_partner ]
[ run bind_stderr.cpp filesystem : : sparring_partner ]
@@ -89,17 +98,17 @@ test-suite with-valgrind :
[ run on_exit.cpp system filesystem : : sparring_partner ]
[ run on_exit2.cpp system filesystem : : sparring_partner ]
[ run on_exit3.cpp system filesystem : : sparring_partner ]
[ compile-fail spawn_fail.cpp ]
[ compile-fail async_system_fail.cpp ]
[ run posix_specific.cpp system filesystem : : sparring_partner : <build>no <target-os>linux:<build>yes ]
[ run windows_specific.cpp filesystem system : : sparring_partner : <build>no <target-os>windows:<build>yes ]
: <dependency>bare ;
test-suite without-valgrind :
[ run async_system.cpp filesystem system coroutine : : sparring_partner : <link>static ]
[ run async_system_future.cpp filesystem system coroutine : : sparring_partner : <link>static ]
[ run async_system_stackless.cpp filesystem system coroutine : : sparring_partner : <link>static ]
[ run vfork.cpp system filesystem : : sparring_partner : <build>no <target-os>linux:<build>yes ]
[ run async_system_future.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
[ run async_system_stackful.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
[ run async_system_stackful_error.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
[ run async_system_stackful_except.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
[ run async_system_stackless.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
[ run vfork.cpp system filesystem : : sparring_partner : <build>no <target-os>linux:<build>yes ]
;

79
test/args_handling.cpp Normal file
View File

@@ -0,0 +1,79 @@
// 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
// Copyright (c) 2018 Oxford Nanopore Technologies
//
// 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 <system_error>
#include <boost/filesystem.hpp>
#include <boost/process/cmd.hpp>
#include <boost/process/error.hpp>
#include <boost/process/child.hpp>
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(implicit_args_fs_path)
{
using boost::unit_test::framework::master_test_suite;
boost::filesystem::path exe = master_test_suite().argv[1];
std::error_code ec;
bp::child c(
exe,
ec
);
BOOST_REQUIRE(!ec);
c.wait(ec);
BOOST_REQUIRE(!ec);
BOOST_CHECK(c.exit_code() == 1); // should pass at least exe!
}
BOOST_AUTO_TEST_CASE(implicit_args_cmd)
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
bp::child c(
master_test_suite().argv[1],
ec
);
BOOST_REQUIRE(!ec);
c.wait(ec);
BOOST_REQUIRE(!ec);
BOOST_CHECK(c.exit_code() == 1); // should pass at least exe!
}
BOOST_AUTO_TEST_CASE(explicit_args_fs_path)
{
using boost::unit_test::framework::master_test_suite;
boost::filesystem::path exe = master_test_suite().argv[1];
std::error_code ec;
bp::child c(
exe,
"hello",
ec
);
BOOST_REQUIRE(!ec);
c.wait(ec);
BOOST_REQUIRE(!ec);
BOOST_CHECK(c.exit_code() == 2); // exe + "hello"
}

View File

@@ -35,12 +35,71 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
boost::asio::io_context io_context;
std::error_code ec;
bool exit_called_for_c1 = false;
int exit_code_c1 = 0;
bp::child c1(
master_test_suite().argv[1],
"test", "--exit-code", "123",
ec,
io_context,
bp::on_exit([&](int exit, const std::error_code& ec_in)
{
BOOST_CHECK(!exit_called_for_c1);
exit_code_c1 = exit; exit_called_for_c1=true;
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
bool exit_called_for_c2 = false;
int exit_code_c2 = 0;
bp::child c2(
master_test_suite().argv[1],
"test", "--exit-code", "21",
ec,
io_context,
bp::on_exit([&](int exit, const std::error_code& ec_in)
{
BOOST_CHECK(!exit_called_for_c2);
exit_code_c2 = exit; exit_called_for_c2=true;
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
io_context.run();
BOOST_CHECK(exit_called_for_c1);
BOOST_CHECK_EQUAL(exit_code_c1, 123);
BOOST_CHECK_EQUAL(c1.exit_code(), 123);
BOOST_CHECK(exit_called_for_c2);
BOOST_CHECK_EQUAL(exit_code_c2, 21);
BOOST_CHECK_EQUAL(c2.exit_code(), 21);
}
BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(3))
{
using boost::unit_test::framework::master_test_suite;
using namespace boost::asio;
boost::asio::io_context io_context;
bool exit_called = false;
int exit_code = 0;
std::error_code ec;
bp::child c(
bp::child c1(
master_test_suite().argv[1],
"test", "--exit-code", "123",
"test", "--exit-code", "1",
ec
);
BOOST_REQUIRE(!ec);
bp::child c2(
master_test_suite().argv[1],
"test", "--exit-code", "2", "--wait", "1",
ec,
io_context,
bp::on_exit([&](int exit, const std::error_code& ec_in)
@@ -49,12 +108,106 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
io_context.run();
// Regression test for #143: make sure the async SIGCHLD handler on POSIX does not reap the
// child c1 is watching (this will error if so)
c1.wait(ec);
BOOST_REQUIRE(!ec);
BOOST_CHECK(exit_called);
BOOST_CHECK_EQUAL(exit_code, 123);
BOOST_CHECK_EQUAL(c.exit_code(), 123);
BOOST_CHECK_EQUAL(exit_code, 2);
BOOST_CHECK_EQUAL(c2.exit_code(), 2);
}
BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3))
{
using boost::unit_test::framework::master_test_suite;
using namespace boost::asio;
boost::asio::io_context io_context1;
boost::asio::io_context io_context2;
std::error_code ec;
bool exit_called_for_c1 = false;
int exit_code_c1 = 0;
bp::child c1(
master_test_suite().argv[1],
"test", "--exit-code", "1",
ec,
io_context1,
bp::on_exit([&](int exit, const std::error_code& ec_in)
{
BOOST_CHECK(!exit_called_for_c1);
exit_code_c1 = exit; exit_called_for_c1=true;
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
bool exit_called_for_c2 = false;
int exit_code_c2 = 0;
bp::child c2(
master_test_suite().argv[1],
"test", "--exit-code", "2", "--wait", "1",
ec,
io_context2,
bp::on_exit([&](int exit, const std::error_code& ec_in)
{
BOOST_CHECK(!exit_called_for_c2);
exit_code_c2 = exit; exit_called_for_c2=true;
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
// Regression test for #143: make sure each io_context handles its own children
io_context2.run();
io_context1.run();
c1.wait(ec);
BOOST_REQUIRE(!ec);
BOOST_CHECK(exit_called_for_c1);
BOOST_CHECK_EQUAL(exit_code_c1, 1);
BOOST_CHECK_EQUAL(c1.exit_code(), 1);
BOOST_CHECK(exit_called_for_c2);
BOOST_CHECK_EQUAL(exit_code_c2, 2);
BOOST_CHECK_EQUAL(c2.exit_code(), 2);
}
BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(2))
{
using boost::unit_test::framework::master_test_suite;
using namespace boost::asio;
boost::asio::io_context io_context;
std::error_code ec;
bool exit_called = false;
int exit_code = 0;
bp::child c(
master_test_suite().argv[1],
"test", "--abort",
ec,
io_context,
bp::on_exit([&](int exit, const std::error_code& ec_in)
{
BOOST_CHECK(!exit_called);
exit_code = exit; exit_called=true;
BOOST_TEST_MESSAGE(ec_in.message());
BOOST_CHECK(!ec_in);
})
);
BOOST_REQUIRE(!ec);
io_context.run();
BOOST_CHECK(exit_called);
BOOST_CHECK(exit_code != 0);
BOOST_CHECK_EQUAL(c.exit_code(), exit_code);
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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/process/error.hpp>
#include <boost/process/io.hpp>
#include <boost/process/async.hpp>
#include <boost/process/child.hpp>
#include <boost/process/async_system.hpp>
#include <string>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/asio/yield.hpp>
#include <vector>
#include <array>
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
{
using boost::unit_test::framework::master_test_suite;
bool did_something_else = false;
boost::asio::io_context ios;
auto stackful =
[&](boost::asio::yield_context yield_)
{
int ret =
bp::async_system(
ios, yield_,
master_test_suite().argv[1],
"test", "--exit-code", "123");
BOOST_CHECK_EQUAL(ret, 123);
BOOST_CHECK(did_something_else);
};
boost::asio::spawn(ios, stackful);
ios.post([&]{did_something_else = true;});
ios.run();
BOOST_CHECK(did_something_else);
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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/process/error.hpp>
#include <boost/process/io.hpp>
#include <boost/process/async.hpp>
#include <boost/process/child.hpp>
#include <boost/process/async_system.hpp>
#include <string>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/asio/yield.hpp>
#include <vector>
#include <array>
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(stackful_except, *boost::unit_test::timeout(15))
{
using boost::unit_test::framework::master_test_suite;
bool did_something_else = false;
boost::asio::io_context ios;
auto stackful =
[&](boost::asio::yield_context yield_)
{
BOOST_CHECK_THROW(
bp::async_system(
ios, yield_,
"none-existing-exe"), boost::system::system_error);
BOOST_CHECK(did_something_else);
};
boost::asio::spawn(ios, stackful);
ios.post([&]{did_something_else = true;});
ios.run();
BOOST_CHECK(did_something_else);
}

View File

@@ -24,7 +24,6 @@
#include <array>
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
{
using boost::unit_test::framework::master_test_suite;
@@ -65,5 +64,3 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
BOOST_CHECK(did_something_else);
}

14
test/exit_argc.cpp Normal file
View File

@@ -0,0 +1,14 @@
// Copyright (c) 2018 Oxford Nanopore Technologies
//
// 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)
#include <iostream>
int main(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i) {
std::cout << argv[i] << '\n';
}
return argc;
}

View File

@@ -48,6 +48,26 @@ BOOST_AUTO_TEST_CASE(sync_wait)
c.wait();
}
BOOST_AUTO_TEST_CASE(sync_wait_abort)
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
bp::child c(
master_test_suite().argv[1],
"test", "--abort",
ec
);
BOOST_REQUIRE(!ec);
c.wait();
int exit_code = c.exit_code();
BOOST_CHECK(exit_code != 0);
c.wait();
}
#if defined(BOOST_WINDOWS_API)
struct wait_handler
{

7
test/no_ansi_apps.cpp Normal file
View File

@@ -0,0 +1,7 @@
// Copyright (c) 2018 Klemens D. Morgenstern
//
// 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_NO_ANSI_APIS 1
#include <boost/process.hpp>

View File

@@ -51,6 +51,7 @@ int main(int argc, char *argv[])
("is-nul-stdout", bool_switch())
("is-nul-stderr", bool_switch())
("loop", bool_switch())
("abort", bool_switch())
("prefix", value<std::string>())
("prefix-once", value<std::string>())
("pwd", bool_switch())
@@ -147,6 +148,10 @@ int main(int argc, char *argv[])
{
while (true);
}
else if (vm["abort"].as<bool>())
{
std::abort();
}
else if (vm.count("prefix"))
{
std::string line;