mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
2 Commits
issue/452
...
no-signal-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
229faab9b1 | ||
|
|
5e792f006c |
@@ -18,6 +18,8 @@
|
||||
#else
|
||||
#if defined(BOOST_PROCESS_V2_PDFORK)
|
||||
#include <boost/process/v2/posix/pdfork_launcher.hpp>
|
||||
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
#include <boost/process/v2/posix/pipe_fork_launcher.hpp>
|
||||
#else
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
#endif
|
||||
@@ -48,6 +50,8 @@ typedef windows::default_launcher default_process_launcher;
|
||||
#else
|
||||
#if defined(BOOST_PROCESS_V2_PDFORK)
|
||||
typedef posix::pdfork_launcher default_process_launcher;
|
||||
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
typedef posix::pipe_fork_launcher default_process_launcher;
|
||||
#else
|
||||
typedef posix::default_launcher default_process_launcher;
|
||||
#endif
|
||||
|
||||
@@ -165,7 +165,7 @@ BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#if defined(SYS_pidfd_open)
|
||||
#if defined(SYS_pidfd_open) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
|
||||
#define BOOST_PROCESS_V2_PIDFD_OPEN 1
|
||||
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
|
||||
#endif
|
||||
|
||||
@@ -255,7 +255,7 @@ struct basic_process_handle_fd
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
|
||||
@@ -18,19 +18,27 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/associated_immediate_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/windows/signal_set.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <asio/signal_set.hpp>
|
||||
#endif
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/associated_immediate_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
@@ -45,7 +53,7 @@ struct basic_process_handle_fd_or_signal
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return signal_set_.get_executor(); }
|
||||
{ return descriptor_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
@@ -277,14 +285,13 @@ struct basic_process_handle_fd_or_signal
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
if (process_is_running(res))
|
||||
else if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -311,12 +318,19 @@ struct basic_process_handle_fd_or_signal
|
||||
struct basic_process_handle_fd_or_signal;
|
||||
pid_type pid_ = -1;
|
||||
net::posix::basic_stream_descriptor<Executor> descriptor_;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> signal_set_{descriptor_.get_executor(), SIGCHLD};
|
||||
|
||||
#else
|
||||
int signal_set_;
|
||||
#endif
|
||||
struct async_wait_op_
|
||||
{
|
||||
net::posix::basic_descriptor<Executor> &descriptor;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> &handle;
|
||||
#else
|
||||
int dummy;
|
||||
#endif
|
||||
pid_type pid_;
|
||||
bool needs_post = true;
|
||||
|
||||
@@ -343,35 +357,41 @@ struct basic_process_handle_fd_or_signal
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
needs_post = false;
|
||||
if (descriptor.is_open())
|
||||
descriptor.async_wait(
|
||||
net::posix::descriptor_base::wait_read,
|
||||
std::move(self));
|
||||
{
|
||||
needs_post = false;
|
||||
descriptor.async_wait(
|
||||
net::posix::descriptor_base::wait_read,
|
||||
std::move(self));
|
||||
return;
|
||||
}
|
||||
else
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
needs_post = false;
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
#else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
completer cpl{ec, exit_code, std::move(self)};
|
||||
if (needs_post)
|
||||
net::post(exec, std::move(cpl));
|
||||
{
|
||||
auto exec = net::get_associated_immediate_executor(self, descriptor.get_executor());
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
}
|
||||
else
|
||||
net::dispatch(exec, std::move(cpl));
|
||||
|
||||
{
|
||||
auto exec = net::get_associated_executor(self);
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
}
|
||||
}
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, native_exit_code_type code, error_code ec)
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
public:
|
||||
|
||||
@@ -17,17 +17,25 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/associated_immediate_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <asio/signal_set.hpp>
|
||||
#endif
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/associated_immediate_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
@@ -86,9 +94,14 @@ struct basic_process_handle_signal
|
||||
basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
|
||||
{
|
||||
pid_ = handle.id();
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
|
||||
signal_set_.~basic_signal_set();
|
||||
using ss = net::basic_signal_set<Executor>;
|
||||
new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
|
||||
#else
|
||||
signal_set_.executor = handle.signal_set_.executor;
|
||||
#endif
|
||||
handle.pid_ = -1;
|
||||
return *this;
|
||||
}
|
||||
@@ -244,11 +257,13 @@ struct basic_process_handle_signal
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
|
||||
if (res == 0)
|
||||
else if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -273,10 +288,24 @@ struct basic_process_handle_signal
|
||||
template<typename>
|
||||
friend struct basic_process_handle_signal;
|
||||
pid_type pid_ = -1;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> signal_set_;
|
||||
|
||||
#else
|
||||
struct signal_set_dummy_
|
||||
{
|
||||
signal_set_dummy_(signal_set_dummy_ &&) = default;
|
||||
signal_set_dummy_(const signal_set_dummy_ &) = default;
|
||||
Executor executor;
|
||||
using executor_type = Executor;
|
||||
executor_type get_executor() {return executor;}
|
||||
signal_set_dummy_(Executor executor, int) : executor(std::move(executor)) {}
|
||||
};
|
||||
signal_set_dummy_ signal_set_;
|
||||
#endif
|
||||
struct async_wait_op_
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
|
||||
net::basic_signal_set<Executor> &handle;
|
||||
pid_type pid_;
|
||||
|
||||
@@ -315,20 +344,25 @@ struct basic_process_handle_signal
|
||||
return;
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
net::dispatch(exec, completer{ec, exit_code, std::move(self)});
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
}
|
||||
#else
|
||||
signal_set_dummy_ dummy_;
|
||||
pid_t pid;
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
auto exec = net::get_associated_immediate_executor(self, dummy_.get_executor());
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
|
||||
net::dispatch(exec, net::append(std::move(self), native_exit_code_type(), ec));
|
||||
}
|
||||
#endif
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, native_exit_code_type code, error_code ec)
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
public:
|
||||
|
||||
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2022 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)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A launcher using `pipe_fork`. Default on FreeBSD
|
||||
struct pipe_fork_launcher : default_launcher
|
||||
{
|
||||
/// The file descriptor of the subprocess. Set after fork.
|
||||
pipe_fork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
int fd = -1;
|
||||
{
|
||||
pipe_guard pg, pg_wait;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::pipe(pg_wait.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg_wait.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
fd_whitelist.push_back(pg_wait.p[1]);
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
::close(pg.p[0]);
|
||||
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
::close(pg_wait.p[1]);
|
||||
pg_wait.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
std::swap(fd, pg_wait.p[0]);
|
||||
}
|
||||
|
||||
basic_process<Executor> proc(exec, pid, fd);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
|
||||
#include <boost/process/v2/detail/process_handle_fd.hpp>
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK)
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
#include <boost/process/v2/detail/process_handle_fd_or_signal.hpp>
|
||||
#else
|
||||
// with asio support we could use EVFILT_PROC:NOTE_EXIT as well.
|
||||
@@ -107,9 +107,9 @@ struct basic_process_handle
|
||||
void request_exit()
|
||||
|
||||
/// Unconditionally terminates the process and stores the exit code in exit_status.
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec);\
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec);
|
||||
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
|
||||
void terminate(native_exit_code_type &exit_status);/
|
||||
void terminate(native_exit_code_type &exit_status);
|
||||
|
||||
/// Checks if the current process is running.
|
||||
/**If it has already completed, it assigns the exit code to `exit_code`.
|
||||
@@ -137,7 +137,7 @@ using basic_process_handle = detail::basic_process_handle_win<Executor>;
|
||||
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
|
||||
template<typename Executor = net::any_io_executor>
|
||||
using basic_process_handle = detail::basic_process_handle_fd<Executor>;
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPE_LAUNCHER)
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
template<typename Executor = net::any_io_executor>
|
||||
using basic_process_handle = detail::basic_process_handle_fd_or_signal<Executor>;
|
||||
#else
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
#include <boost/process/v2/start_dir.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ext)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -655,7 +655,7 @@ BOOST_AUTO_TEST_CASE(async_cancel_wait)
|
||||
proc.async_wait(asio::cancel_after(std::chrono::milliseconds(100),
|
||||
[&](boost::system::error_code ec, int)
|
||||
{
|
||||
BOOST_CHECK(ec == asio::error::operation_aborted);
|
||||
BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted);
|
||||
BOOST_CHECK(proc.running());
|
||||
if (proc.running())
|
||||
proc.terminate();
|
||||
|
||||
Reference in New Issue
Block a user