This commit is contained in:
2026-03-23 20:54:41 +08:00
commit e13b3650e9
4596 changed files with 1015768 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
// 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_DETAIL_CONFIG_HPP
#define BOOST_PROCESS_V2_DETAIL_CONFIG_HPP
#if defined(BOOST_PROCESS_V2_STANDALONE)
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig)
#include <asio/detail/config.hpp>
#include <system_error>
#include <filesystem>
#include <string_view>
#include <iomanip>
#include <optional>
#if defined(ASIO_WINDOWS)
#define BOOST_PROCESS_V2_WINDOWS 1
#endif
#if defined(ASIO_HAS_UNISTD_H)
#define BOOST_PROCESS_V2_POSIX 1
#endif
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace process_v2 {
#define BOOST_PROCESS_V2_END_NAMESPACE }
#define BOOST_PROCESS_V2_NAMESPACE process_v2
namespace asio {}
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace net = ::asio;
BOOST_PROCESS_V2_END_NAMESPACE
#else
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig)
#include <boost/config.hpp>
#include <boost/io/quoted.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_category.hpp>
#include <boost/system/system_error.hpp>
#include <boost/optional.hpp>
#if defined(BOOST_WINDOWS_API)
#define BOOST_PROCESS_V2_WINDOWS 1
#endif
#if defined(BOOST_POSIX_API)
#define BOOST_PROCESS_V2_POSIX 1
#endif
#if !defined(BOOST_PROCESS_V2_WINDOWS) && !defined(BOOST_POSIX_API)
#error Unsupported operating system
#endif
#if defined(BOOST_PROCESS_USE_STD_FS)
#include <filesystem>
#else
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#endif
#if !defined(BOOST_PROCESS_VERSION)
#define BOOST_PROCESS_VERSION 2
#endif
#if BOOST_PROCESS_VERSION == 1
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 {
#else
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { inline namespace v2 {
#endif
#define BOOST_PROCESS_V2_END_NAMESPACE } } }
#define BOOST_PROCESS_V2_NAMESPACE boost::process::v2
namespace boost { namespace asio {} }
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace net = ::boost::asio;
BOOST_PROCESS_V2_END_NAMESPACE
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(BOOST_PROCESS_STANDALONE)
using std::error_code ;
using std::error_category ;
using std::system_category ;
using std::system_error ;
namespace filesystem = std::filesystem;
using std::quoted;
using std::optional;
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) ec.assign(__VA_ARGS__);
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error()); \
#else
using boost::system::error_code ;
using boost::system::error_category ;
using boost::system::system_category ;
using boost::system::system_error ;
using boost::io::quoted;
using boost::optional;
#ifdef BOOST_PROCESS_USE_STD_FS
namespace filesystem = std::filesystem;
#else
namespace filesystem = boost::filesystem;
#endif
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) \
do \
{ \
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
ec.assign(__VA_ARGS__, &loc##__LINE__); \
} \
while (false)
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
do \
{ \
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(), &loc##__LINE__); \
} \
while (false)
#endif
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROCESS_DYN_LINK)
#if defined(BOOST_PROCESS_SOURCE)
#define BOOST_PROCESS_V2_DECL BOOST_SYMBOL_EXPORT
#else
#define BOOST_PROCESS_V2_DECL BOOST_SYMBOL_IMPORT
#endif
#else
#define BOOST_PROCESS_V2_DECL
#endif
#if !defined(BOOST_PROCESS_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_PROCESS_NO_LIB)
#define BOOST_LIB_NAME boost_process
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROCESS_DYN_LINK)
#define BOOST_DYN_LINK
#endif
#include <boost/config/auto_link.hpp>
#endif
#if defined(BOOST_PROCESS_V2_POSIX)
#if defined(__linux__) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
#include <sys/syscall.h>
#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
#endif
#if defined(__FreeBSD__) && defined(BOOST_PROCESS_V2_ENABLE_PDFORK)
#define BOOST_PROCESS_V2_PDFORK 1
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif
#else
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif
#endif //BOOST_PROCESS_V2_DETAIL_CONFIG_HPP

View File

@@ -0,0 +1,90 @@
//
// process/environment/detail/environment_posix.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
//
// 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_DETAIL_ENVIRONMENT_POSIX_HPP
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_POSIX_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#if defined(__APPLE__)
# include <crt_externs.h>
# if !defined(environ)
# define environ (*_NSGetEnviron())
# endif
#elif defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
extern "C" { extern char **environ; }
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace environment
{
using char_type = char;
template<typename Char>
using key_char_traits = std::char_traits<Char>;
template<typename Char>
using value_char_traits = std::char_traits<Char>;
constexpr char_type equality_sign = '=';
constexpr char_type delimiter = ':';
namespace detail
{
BOOST_PROCESS_V2_DECL
basic_cstring_ref<char_type, value_char_traits<char>>
get(basic_cstring_ref<char_type, key_char_traits<char_type>> key, error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
basic_cstring_ref<char_type, value_char_traits<char_type>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
error_code & ec);
}
using native_handle_type = const char * const *;
using native_iterator = native_handle_type;
namespace detail
{
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
struct native_handle_deleter
{
void operator()(native_handle_type) const {}
};
BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh);
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
inline const char_type * dereference(native_iterator iterator) {return *iterator;}
BOOST_PROCESS_V2_DECL bool has_x_access(const char * pth);
inline bool is_executable(const filesystem::path & pth, error_code & ec)
{
return filesystem::is_regular_file(pth, ec) && has_x_access(pth.c_str());
}
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif

View File

@@ -0,0 +1,220 @@
//
// process/environment/detail/environment_win.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
//
// 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_DETAIL_ENVIRONMENT_WIN_HPP
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#include <cwctype>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace environment
{
using char_type = wchar_t;
template<typename Char>
struct key_char_traits
{
typedef Char char_type;
typedef typename std::char_traits<char_type>::int_type int_type;
typedef typename std::char_traits<char_type>::off_type off_type;
typedef typename std::char_traits<char_type>::pos_type pos_type;
typedef typename std::char_traits<char_type>::state_type state_type;
BOOST_CONSTEXPR static char to_lower(char c) {return std::tolower(to_int_type(c));}
BOOST_CONSTEXPR static wchar_t to_lower(wchar_t c) {return std::towlower(to_int_type(c));}
BOOST_CONSTEXPR static int_type to_lower(int_type i, char ) {return std::tolower(i);}
BOOST_CONSTEXPR static int_type to_lower(int_type i, wchar_t) {return std::towlower(i);}
BOOST_CONSTEXPR static
void assign(char_type& c1, const char_type& c2) BOOST_NOEXCEPT
{
c1 = c2;
}
BOOST_CONSTEXPR static
bool eq(char_type c1, char_type c2) BOOST_NOEXCEPT
{
return to_lower(c1) == to_lower(c2);
}
BOOST_CONSTEXPR static
bool lt(char_type c1, char_type c2) BOOST_NOEXCEPT
{
return to_lower(c1) < to_lower(c2);
}
BOOST_CXX14_CONSTEXPR static
int compare(const char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
auto itrs = std::mismatch(s1, s1 + n, s2, &eq);
if (itrs.first == (s1 + n))
return 0;
auto c1 = to_lower(*itrs.first);
auto c2 = to_lower(*itrs.second);
return (c1 < c2 ) ? -1 : 1;
}
static size_t length(const char* s) BOOST_NOEXCEPT { return std::strlen(s); }
static size_t length(const wchar_t* s) BOOST_NOEXCEPT { return std::wcslen(s); }
BOOST_CXX14_CONSTEXPR static
const char_type* find(const char_type* s, size_t n, const char_type& a) BOOST_NOEXCEPT
{
const char_type u = to_lower(a);
return std::find_if(s, s + n, [u](char_type c){return to_lower(c) == u;});
}
BOOST_CXX14_CONSTEXPR static
char_type* move(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
if (s1 < s2)
return std::move(s2, s2 + n, s1);
else
return std::move_backward(s2, s2 + n, s1 + n);
}
BOOST_CONSTEXPR static
char_type* copy(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
return std::copy(s2, s2 + n, s1);
}
BOOST_CXX14_CONSTEXPR static
char_type* assign(char_type* s, size_t n, char_type a) BOOST_NOEXCEPT
{
std::fill(s, s + n, a);
return s +n;
}
BOOST_CONSTEXPR static
int_type not_eof(int_type c) BOOST_NOEXCEPT
{
return eq_int_type(c, eof()) ? ~eof() : c;
}
BOOST_CONSTEXPR static
char_type to_char_type(int_type c) BOOST_NOEXCEPT
{
return char_type(c);
}
BOOST_CONSTEXPR static
int_type to_int_type(char c) BOOST_NOEXCEPT
{
return int_type((unsigned char)c);
}
BOOST_CONSTEXPR static
int_type to_int_type(wchar_t c) BOOST_NOEXCEPT
{
return int_type((wchar_t)c);
}
BOOST_CONSTEXPR static
bool eq_int_type(int_type c1, int_type c2) BOOST_NOEXCEPT
{
return to_lower(c1, char_type()) == to_lower(c2, char_type());
}
BOOST_CONSTEXPR static inline int_type eof() BOOST_NOEXCEPT
{
return int_type(EOF);
}
};
namespace detail
{
template<typename Char>
std::size_t hash_step(std::size_t prev, Char c, key_char_traits<Char>)
{
return prev ^ (key_char_traits<Char>::to_lower(c) << 1);
}
}
template<typename Char>
using value_char_traits = std::char_traits<Char>;
BOOST_CONSTEXPR static char_type equality_sign = L'=';
BOOST_CONSTEXPR static char_type delimiter = L';';
using native_handle_type = wchar_t*;
using native_iterator = const wchar_t*;
namespace detail
{
BOOST_PROCESS_V2_DECL
std::basic_string<wchar_t, value_char_traits<wchar_t>> get(
basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
basic_cstring_ref<wchar_t, value_char_traits<wchar_t>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
std::basic_string<char, value_char_traits<char>> get(
basic_cstring_ref<char, key_char_traits<char>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<char, key_char_traits<char>> key,
basic_cstring_ref<char, value_char_traits<char>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<char, key_char_traits<char>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
struct native_handle_deleter
{
native_handle_deleter() = default;
native_handle_deleter(const native_handle_deleter& ) = default;
BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const;
};
inline const char_type * dereference(native_iterator iterator) {return iterator;}
BOOST_PROCESS_V2_DECL native_iterator next(native_iterator nh);
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
BOOST_PROCESS_V2_DECL bool is_exec_type(const wchar_t * pth);
inline bool is_executable(const filesystem::path & pth, error_code & ec)
{
return filesystem::is_regular_file(pth, ec) && is_exec_type(pth.c_str());
}
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP

View File

@@ -0,0 +1,22 @@
// 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_DETAIL_LAST_ERROR_HPP
#define BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP
#include <boost/process/v2/detail/config.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL error_code get_last_error();
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP

View File

@@ -0,0 +1,355 @@
// 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_DETAIL_PROCESS_HANDLE_FD_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/compose.hpp>
#include <asio/posix/basic_stream_descriptor.hpp>
#include <asio/post.hpp>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/posix/basic_stream_descriptor.hpp>
#include <boost/asio/post.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_fd
{
using native_handle_type = int;
typedef Executor executor_type;
executor_type get_executor()
{ return descriptor_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_fd<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_fd(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value>::type * = nullptr)
: pid_(-1), descriptor_(context)
{
}
basic_process_handle_fd(executor_type executor)
: pid_(-1), descriptor_(executor)
{
}
basic_process_handle_fd(executor_type executor, pid_type pid)
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
{
if (descriptor_.native_handle() == -1)
detail::throw_error(detail::get_last_error(), "wait(pid)");
}
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), descriptor_(executor, process_handle)
{
}
basic_process_handle_fd(basic_process_handle_fd &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
template<typename Executor1>
basic_process_handle_fd(basic_process_handle_fd<Executor1> &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
basic_process_handle_fd& operator=(basic_process_handle_fd &&handle)
{
pid_ = handle.pid_;
descriptor_ = std::move(handle.descriptor_);
handle.pid_ = -1;
return *this;
}
pid_type id() const
{ return pid_; }
native_handle_type native_handle() {return pid_;}
void terminate_if_running(error_code &)
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return ;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return ;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
template<typename>
friend
struct basic_process_handle_fd;
pid_type pid_ = -1;
net::posix::basic_stream_descriptor<Executor> descriptor_;
struct async_wait_op_
{
net::posix::basic_descriptor<Executor> &descriptor;
pid_type pid_;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
error_code ec;
int wait_res = -1;
if (pid_ <= 0) // error, complete early
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::bad_descriptor);
else if (process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
descriptor.async_wait(net::posix::descriptor_base::wait_read, std::move(self));
return;
}
struct completer
{
error_code ec;
typename std::decay<Self>::type self;
void operator()()
{
self.complete(ec);
}
};
net::dispatch(
net::get_associated_immediate_executor(self, descriptor.get_executor()),
completer{ec, std::move(self)});
}
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
if (!ec && process_is_running(exit_code))
if (::waitpid(pid_, &exit_code, 0) == -1)
ec = get_last_error();
std::move(self).complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP

View File

@@ -0,0 +1,413 @@
// 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_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#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>
#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
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_fd_or_signal
{
using native_handle_type = int;
typedef Executor executor_type;
executor_type get_executor()
{ return descriptor_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_fd_or_signal<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(-1), descriptor_(context)
{
}
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
pid_type pid,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(pid), descriptor_(context)
{
}
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
pid_type pid, native_handle_type process_handle,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(pid), descriptor_(context, process_handle)
{
}
basic_process_handle_fd_or_signal(Executor executor)
: pid_(-1), descriptor_(executor)
{
}
basic_process_handle_fd_or_signal(Executor executor, pid_type pid)
: pid_(pid), descriptor_(executor)
{
}
basic_process_handle_fd_or_signal(Executor executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), descriptor_(executor, process_handle)
{
}
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
// Warn: does not change the executor of the signal-set.
basic_process_handle_fd_or_signal& operator=(basic_process_handle_fd_or_signal &&handle)
{
pid_ = handle.pid_;
descriptor_ = std::move(handle.descriptor_);
handle.pid_ = -1;
return *this;
}
template<typename Executor1>
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal<Executor1> &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
pid_type id() const
{ return pid_; }
native_handle_type native_handle() {return pid_;}
void terminate_if_running(error_code &)
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return ;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return ;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
template<typename>
friend
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_;
native_exit_code_type & exit_code;
bool needs_post = true;
template<typename Self>
void operator()(Self && self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
(*this)(std::move(self), error_code{});
}
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else if (process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
if (descriptor.is_open())
{
needs_post = false;
descriptor.async_wait(
net::posix::descriptor_base::wait_read,
std::move(self));
return;
}
else
{
#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
}
}
if (needs_post)
{
auto exec = net::get_associated_immediate_executor(self, descriptor.get_executor());
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
}
else
{
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);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP

View File

@@ -0,0 +1,387 @@
// 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_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#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
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_signal
{
struct native_handle_type
{
native_handle_type() = delete;
native_handle_type(const native_handle_type & ) = delete;
~native_handle_type() = default;
};
typedef Executor executor_type;
executor_type get_executor()
{ return signal_set_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_signal<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_signal(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(-1), signal_set_(context, SIGCHLD)
{
}
basic_process_handle_signal(Executor executor)
: pid_(-1), signal_set_(executor, SIGCHLD)
{
}
basic_process_handle_signal(Executor executor, pid_type pid)
: pid_(pid), signal_set_(executor, SIGCHLD)
{
}
basic_process_handle_signal(basic_process_handle_signal && handle)
: pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD)
{
handle.pid_ = -1;
}
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;
}
template<typename Executor1>
basic_process_handle_signal(basic_process_handle_signal<Executor1> && handle)
: pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD)
{
handle.pid_ = -1;
}
pid_type id() const { return pid_; }
native_handle_type native_handle() {return {};}
void terminate_if_running(error_code &)
{
terminate_if_running();
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
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_;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
handle.async_wait(std::move(self));
handle.cancel();
// we cancel so we end up on the signal-sets executor
}
template<typename Self>
void operator()(Self &&self, error_code ec, int /*sig*/)
{
if (ec == net::error::operation_aborted &&
self.get_cancellation_state().cancelled()
== net::cancellation_type::none)
ec.clear();
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else if (!ec && process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
handle.async_wait(std::move(self));
return;
}
const auto exec = self.get_executor();
net::dispatch(exec, net::append(std::move(self), 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, error_code ec)
{
self.complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP

View File

@@ -0,0 +1,326 @@
// 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_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/compose.hpp>
#include <asio/windows/basic_object_handle.hpp>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/windows/basic_object_handle.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL void get_exit_code_( void * handle, native_exit_code_type & exit_code, error_code & ec);
BOOST_PROCESS_V2_DECL void * open_process_(pid_type pid);
BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle);
BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec);
BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void suspend_(void * handle, error_code & ec);
BOOST_PROCESS_V2_DECL void resume_(void * handle, error_code & ec);
BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code);
BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status);
template<typename Executor = net::any_io_executor>
struct basic_process_handle_win
{
typedef net::windows::basic_object_handle<Executor> handle_type;
typedef typename handle_type::native_handle_type native_handle_type;
typedef Executor executor_type;
executor_type get_executor()
{ return handle_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_win<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_win(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type = 0)
: pid_(0), handle_(context)
{
}
basic_process_handle_win(Executor executor)
: pid_(0), handle_(executor)
{
}
basic_process_handle_win(Executor executor, pid_type pid)
: pid_(pid), handle_(executor, detail::open_process_(pid))
{
}
basic_process_handle_win(Executor executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), handle_(executor, process_handle)
{
}
template<typename Executor1>
basic_process_handle_win(basic_process_handle_win<Executor1> && other)
: pid_(other.pid_), handle_(std::move(other.handle_))
{
other.pid_ = static_cast<DWORD>(-1);
}
basic_process_handle_win(basic_process_handle_win && other)
: pid_(other.pid_), handle_(std::move(other.handle_))
{
other.pid_ = static_cast<DWORD>(-1);
}
basic_process_handle_win& operator=(basic_process_handle_win && other)
{
pid_ = other.pid_;
handle_ = std::move(other.handle_);
other.pid_ = static_cast<DWORD>(-1);
return *this;
}
template<typename Executor1>
basic_process_handle_win& operator=(basic_process_handle_win<Executor1> && other)
{
pid_ = other.pid_;
handle_ = std::move(other.handle_);
other.pid_ = static_cast<DWORD>(-1);
return *this;
}
~basic_process_handle_win()
{
if (handle_.is_open())
{
error_code ec;
handle_.close(ec);
}
}
native_handle_type native_handle()
{ return handle_.native_handle(); }
pid_type id() const
{ return pid_; }
void terminate_if_running(error_code &)
{
detail::terminate_if_running_(handle_.native_handle());
}
void terminate_if_running()
{
detail::terminate_if_running_(handle_.native_handle());
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return;
handle_.wait(ec);
if (!ec)
detail::get_exit_code_(handle_.native_handle(), exit_status, ec);
}
void wait(native_exit_code_type &exit_status)
{
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (!detail::check_pid_(pid_, ec))
return;
detail::interrupt_(pid_, ec);
}
void interrupt()
{
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (!detail::check_pid_(pid_, ec))
return;
detail::request_exit_(pid_, ec);
}
void request_exit()
{
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend(error_code &ec)
{
detail::suspend_(handle_.native_handle(), ec);
}
void suspend()
{
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void resume(error_code &ec)
{
detail::resume_(handle_.native_handle(), ec);
}
void resume()
{
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return;
detail::terminate_(handle_.native_handle(), ec, exit_status);
if (!ec)
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return false;
native_exit_code_type code;
//single value, not needed in the winapi.
detail::check_running_(handle_.native_handle(), ec, code);
if (ec)
return false;
if (process_is_running(code))
return true;
else
exit_code = code;
return false;
}
bool running(native_exit_code_type &exit_code)
{
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return handle_.is_open();
}
template<typename>
friend struct basic_process_handle_win;
private:
pid_type pid_;
handle_type handle_;
struct async_wait_op_
{
handle_type &handle;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
auto sl = self.get_cancellation_state().slot();
auto & h = handle;
if (sl.is_connected())
sl.assign(
[&h](asio::cancellation_type ct)
{
error_code ec;
h.cancel(ec);
});
handle.async_wait(std::move(self));
}
template<typename Self>
void operator()(Self &&self, error_code ec)
{
if (ec == asio::error::operation_aborted && !self.get_cancellation_state().cancelled())
return handle.async_wait(std::move(self));
if (!ec && process_is_running(exit_code)) // exit_code could be set by another call to wait.
detail::get_exit_code_(handle.native_handle(), exit_code, ec);
std::move(self).complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{handle_, exit_code}, handler, handle_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{handle_, exit_code}, handler, handle_
);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP

View File

@@ -0,0 +1,33 @@
// 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_DETAIL_THROW_ERROR_HPP
#define BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP
#include <boost/process/v2/detail/config.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err);
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err, const char* location);
inline void throw_error(const error_code& err)
{
if (err)
do_throw_error(err);
}
inline void throw_error(const error_code& err, const char* location)
{
if (err)
do_throw_error(err, location);
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP

View File

@@ -0,0 +1,39 @@
// 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_DETAIL_THROW_EXCEPTION_HPP
#define BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP
#include <boost/process/v2/detail/config.hpp>
#if !defined(BOOST_PROCESS_V2_STANDALONE)
#include <boost/throw_exception.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
#if defined(BOOST_PROCESS_V2_STANDALONE)
template <typename Exception>
inline void throw_exception(const Exception& e)
{
throw e;
}
#else
using boost::throw_exception;
#endif
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP

View File

@@ -0,0 +1,94 @@
// 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_DETAIL_UTF8_HPP
#define BOOST_PROCESS_V2_DETAIL_UTF8_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t convert_to_utf8(const wchar_t * in, std::size_t size,
char * out, std::size_t max_size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t convert_to_wide(const char * in, std::size_t size,
wchar_t * out, std::size_t max_size, error_code & ec);
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>, typename CharIn,
typename = typename std::enable_if<std::is_same<CharOut, CharIn>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const CharIn * data, std::size_t size,
const Allocator allocator = Allocator{})
{
return std::basic_string<CharOut, Traits, Allocator>(data, size, allocator);
}
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>,
typename = typename std::enable_if<std::is_same<CharOut, char>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const wchar_t * data, std::size_t size,
const Allocator allocator = Allocator{})
{
error_code ec;
const auto req_size = size_as_utf8(data, size, ec);
if (ec)
detail::throw_error(ec, "size_as_utf8");
std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
if (ec)
detail::throw_error(ec, "convert_to_utf8");
res.resize(res_size);
return res;
}
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>,
typename = typename std::enable_if<std::is_same<CharOut, wchar_t>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const char * data, std::size_t size,
const Allocator allocator = Allocator{})
{
error_code ec;
const auto req_size = size_as_wide(data, size, ec);
if (ec)
detail::throw_error(ec, "size_as_wide");
std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
if (ec)
detail::throw_error(ec, "convert_to_wide");
res.resize(res_size);
return res;
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_UTF8_HPP