mirror of
https://github.com/boostorg/process.git
synced 2026-01-19 16:32:15 +00:00
Compare commits
57 Commits
boost-1.70
...
sfinae_dyn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83c2a986cb | ||
|
|
41fff78e00 | ||
|
|
e85f0d0816 | ||
|
|
af54484bc2 | ||
|
|
ebcb30e4bd | ||
|
|
5371c9813b | ||
|
|
9a833c610d | ||
|
|
c99ebfee7a | ||
|
|
34861366e0 | ||
|
|
e6722c452c | ||
|
|
b1e38842fb | ||
|
|
bcc9826e67 | ||
|
|
b6c6753b87 | ||
|
|
0d008a88fc | ||
|
|
f92ec53968 | ||
|
|
00a87d0a67 | ||
|
|
462334639c | ||
|
|
7129182044 | ||
|
|
03fbed44ad | ||
|
|
0277c4fcec | ||
|
|
4bc1ae6ff8 | ||
|
|
266c4503aa | ||
|
|
80479b6b70 | ||
|
|
823a346a08 | ||
|
|
8f3a7b9c63 | ||
|
|
2ebcc07892 | ||
|
|
a673cd9643 | ||
|
|
12971db132 | ||
|
|
94be279992 | ||
|
|
12cded5995 | ||
|
|
0c16a0e5b3 | ||
|
|
e4a6fde7b9 | ||
|
|
92d2bebaaf | ||
|
|
7e7a8cbc1d | ||
|
|
4f767f4bfe | ||
|
|
63f714ae2f | ||
|
|
1b3b9b707c | ||
|
|
f64bc8a6d4 | ||
|
|
102834130d | ||
|
|
fdc6a11cbc | ||
|
|
19b20f55ce | ||
|
|
60d072ce46 | ||
|
|
34f05b9276 | ||
|
|
1b476b0430 | ||
|
|
b294710e60 | ||
|
|
f8cd325d1b | ||
|
|
2a6c23e173 | ||
|
|
d7768b7221 | ||
|
|
b8821eac57 | ||
|
|
d20b64cf37 | ||
|
|
f0ddd6ca29 | ||
|
|
574d9e09d6 | ||
|
|
7085a50f36 | ||
|
|
0485459da2 | ||
|
|
318439af2e | ||
|
|
4d52ff362f | ||
|
|
6abce365c5 |
76
.circleci/config.yml
Normal file
76
.circleci/config.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
environment:
|
||||
- BOOST_LIBRARY=process
|
||||
- CXX_STANDARD=gnu++11
|
||||
docker:
|
||||
- image: gcc:7
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Setting up Environment
|
||||
command: |
|
||||
echo 'export BOOST="$HOME/boost-local"' >> $BASH_ENV
|
||||
if [ $CIRCLE_BRANCH = "master" ]; then
|
||||
echo 'export BOOST_BRANCH="master"' >> $BASH_ENV;
|
||||
else
|
||||
echo 'export BOOST_BRANCH="develop"' >> $BASH_ENV;
|
||||
fi
|
||||
echo 'export BOOST_REMOVE="$BOOST/libs/$BOOST_LIBRARY"' >> $BASH_ENV
|
||||
HOME_SED_=$(echo $HOME | sed -e 's/\//\\\//g')
|
||||
echo 'export HOME_SED=$HOME_SED_' >> $BASH_ENV
|
||||
- run:
|
||||
name: install pre dependencies
|
||||
command: |
|
||||
apt-get update -yqq
|
||||
apt-get install git curl valgrind -y
|
||||
- run:
|
||||
name: Initializing git repo for boost
|
||||
command: |
|
||||
git init $BOOST
|
||||
cd $BOOST
|
||||
echo Testing $BRANCH_TO_TEST
|
||||
git remote add --no-tags -t $BOOST_BRANCH origin https://github.com/boostorg/boost.git
|
||||
git fetch --depth=1
|
||||
git checkout $BOOST_BRANCH
|
||||
git submodule update --init --merge
|
||||
git remote set-branches --add origin $BOOST_BRANCH
|
||||
git pull --recurse-submodules
|
||||
git submodule update --init
|
||||
git checkout $BOOST_BRANCH
|
||||
git submodule foreach "git reset --quiet --hard; git clean -fxd"
|
||||
git reset --hard; git clean -fxd
|
||||
git status
|
||||
rm -rf $BOOST_REMOVE
|
||||
mv $HOME/project $BOOST_REMOVE
|
||||
- run:
|
||||
name: Bootstrapping boost-build
|
||||
command: |
|
||||
cd $BOOST
|
||||
./bootstrap.sh
|
||||
./b2 headers
|
||||
- run:
|
||||
name: Building examples
|
||||
command: |
|
||||
cd $BOOST_REMOVE/example
|
||||
../../../b2 -j2 address-model=64 architecture=x86 toolset=gcc cxxflags="-std=gnu++14" -sBOOST_BUILD_PATH=. | tee example.log || FAILED=1
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" example.log
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --name "Circle CI Gcc Build" --input example.log
|
||||
exit $FAILED
|
||||
- run:
|
||||
name: Running Unit tests
|
||||
command: |
|
||||
cd $BOOST_REMOVE/test
|
||||
../../../b2 -j2 with-valgrind address-model=64 architecture=x86 testing.launcher=valgrind valgrind=on toolset=gcc cxxflags="--coverage -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. | tee test.log || FAILED=1
|
||||
../../../b2 -j2 without-valgrind address-model=64 architecture=x86 toolset=gcc cxxflags="--coverage -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. | tee no-valgrind.log || FAILED=1
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" test.log
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" no-valgrind.log
|
||||
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --input test.log
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --input no-valgrind.log
|
||||
python <(curl -s https://report.ci/upload.py) --name "Circle CI Gcc Tests" --framework boost
|
||||
bash <(curl -s https://codecov.io/bash) -x gcov || true
|
||||
echo "BUILD_RESULT: $FAILED"
|
||||
exit $FAILED
|
||||
@@ -134,3 +134,5 @@ after_success:
|
||||
|
||||
after_script:
|
||||
- curl -s https://report.ci/upload.py | python - --token=$REPORT_CI_TOKEN --name="$BADGE test run"
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
@@ -145,11 +145,12 @@ public:
|
||||
|
||||
bool running(std::error_code & ec) noexcept
|
||||
{
|
||||
if (valid() && !_exited())
|
||||
ec.clear();
|
||||
if (valid() && !_exited() && !ec)
|
||||
{
|
||||
int exit_code = 0;
|
||||
auto res = boost::process::detail::api::is_running(_child_handle, exit_code, ec);
|
||||
if (!res && !_exited())
|
||||
if (!ec && !res && !_exited())
|
||||
_exit_status->store(exit_code);
|
||||
|
||||
return res;
|
||||
@@ -159,10 +160,11 @@ public:
|
||||
|
||||
void terminate(std::error_code & ec) noexcept
|
||||
{
|
||||
if (valid() && running(ec))
|
||||
if (valid() && running(ec) && !ec)
|
||||
boost::process::detail::api::terminate(_child_handle, ec);
|
||||
|
||||
_terminated = true;
|
||||
if (!ec)
|
||||
_terminated = true;
|
||||
}
|
||||
|
||||
void wait(std::error_code & ec) noexcept
|
||||
@@ -171,7 +173,8 @@ public:
|
||||
{
|
||||
int exit_code = 0;
|
||||
boost::process::detail::api::wait(_child_handle, exit_code, ec);
|
||||
_exit_status->store(exit_code);
|
||||
if (!ec)
|
||||
_exit_status->store(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +191,7 @@ public:
|
||||
{
|
||||
int exit_code = 0;
|
||||
auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time, ec);
|
||||
if (!b)
|
||||
if (!b || ec)
|
||||
return false;
|
||||
_exit_status->store(exit_code);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
|
||||
namespace boost { namespace asio {
|
||||
|
||||
class mutable_buffer;
|
||||
class mutable_buffers_1;
|
||||
template<typename T>
|
||||
struct is_mutable_buffer_sequence;
|
||||
|
||||
template<typename T>
|
||||
struct is_const_buffer_sequence;
|
||||
|
||||
|
||||
class const_buffer;
|
||||
class const_buffers_1;
|
||||
|
||||
template<typename Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
@@ -22,47 +22,12 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__GLIBC__)
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
inline int execvpe(const char* filename, char * const arg_list[], char* env[])
|
||||
{
|
||||
#if defined(__GLIBC__)
|
||||
return ::execvpe(filename, arg_list, env);
|
||||
#else
|
||||
//use my own implementation
|
||||
std::string fn = filename;
|
||||
if ((fn.find('/') == std::string::npos) && ::access(fn.c_str(), X_OK))
|
||||
{
|
||||
auto e = ::environ;
|
||||
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
|
||||
e++;
|
||||
|
||||
if (e != nullptr)
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, *e, boost::is_any_of(":"));
|
||||
|
||||
for (const std::string & pp : path)
|
||||
{
|
||||
auto p = pp + "/" + filename;
|
||||
if (!::access(p.c_str(), X_OK))
|
||||
{
|
||||
fn = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ::execve(fn.c_str(), arg_list, env);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
struct on_setup_t
|
||||
{
|
||||
@@ -295,15 +260,41 @@ class executor
|
||||
return;
|
||||
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
|
||||
else if ((err != EAGAIN ) && (err != EINTR))
|
||||
{
|
||||
::close(source);
|
||||
set_error(std::error_code(err, std::system_category()), "Error read pipe");
|
||||
}
|
||||
}
|
||||
::close(source);
|
||||
set_error(ec, std::move(msg));
|
||||
}
|
||||
|
||||
std::string prepare_cmd_style_fn; //buffer
|
||||
|
||||
inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations.
|
||||
{
|
||||
//use my own implementation
|
||||
prepare_cmd_style_fn = exe;
|
||||
if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
|
||||
{
|
||||
auto e = ::environ;
|
||||
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
|
||||
e++;
|
||||
|
||||
if (e != nullptr)
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, *e, boost::is_any_of(":"));
|
||||
|
||||
for (const std::string & pp : path)
|
||||
{
|
||||
auto p = pp + "/" + exe;
|
||||
if (!::access(p.c_str(), X_OK))
|
||||
{
|
||||
prepare_cmd_style_fn = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exe = prepare_cmd_style_fn.c_str();
|
||||
}
|
||||
|
||||
std::error_code _ec;
|
||||
std::string _msg;
|
||||
@@ -342,6 +333,8 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
if (_ec)
|
||||
return child();
|
||||
if (cmd_style)
|
||||
prepare_cmd_style();
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
@@ -353,10 +346,7 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
else if (pid == 0)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
::execve(exe, cmd_line, env);
|
||||
auto ec = boost::process::detail::get_last_error();
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
|
||||
_exit(EXIT_FAILURE);
|
||||
@@ -372,71 +362,85 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
template<typename Sequence>
|
||||
child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
|
||||
{
|
||||
int p[2];
|
||||
if (::pipe(p) == -1)
|
||||
{
|
||||
set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
|
||||
return child();
|
||||
}
|
||||
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
auto err = ::boost::process::detail::get_last_error();
|
||||
::close(p[0]);
|
||||
::close(p[1]);
|
||||
set_error(err, "fcntl(2) failed");
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
struct pipe_guard
|
||||
{
|
||||
int p[2];
|
||||
pipe_guard() : p{-1,-1} {}
|
||||
|
||||
~pipe_guard()
|
||||
{
|
||||
if (p[0] != -1)
|
||||
::close(p[0]);
|
||||
if (p[1] != -1)
|
||||
::close(p[1]);
|
||||
}
|
||||
} p{};
|
||||
|
||||
if (::pipe(p.p) == -1)
|
||||
{
|
||||
set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
|
||||
return child();
|
||||
}
|
||||
if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
auto err = ::boost::process::detail::get_last_error();
|
||||
set_error(err, "fcntl(2) failed");//this might throw, so we need to be sure our pipe is safe.
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
|
||||
if (cmd_style)
|
||||
prepare_cmd_style();
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "fork() failed";
|
||||
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
_pipe_sink = p.p[1];
|
||||
::close(p.p[0]);
|
||||
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
::execve(exe, cmd_line, env);
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
|
||||
|
||||
_write_error(p.p[1]);
|
||||
::close(p.p[1]);
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
return child();
|
||||
}
|
||||
|
||||
::close(p.p[1]);
|
||||
p.p[1] = -1;
|
||||
_read_error(p.p[0]);
|
||||
|
||||
}
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "fork() failed";
|
||||
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
|
||||
return child();
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
_pipe_sink = p[1];
|
||||
::close(p[0]);
|
||||
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
|
||||
|
||||
_write_error(p[1]);
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
return child();
|
||||
}
|
||||
|
||||
child c(child_handle(pid), exit_status);
|
||||
|
||||
::close(p[1]);
|
||||
_read_error(p[0]);
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
else
|
||||
boost::fusion::for_each(seq, call_on_success(*this));
|
||||
boost::fusion::for_each(seq, call_on_success(*this));
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
@@ -489,6 +493,8 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
if (cmd_style)
|
||||
this->prepare_cmd_style();
|
||||
|
||||
this->pid = ::vfork();
|
||||
if (pid == -1)
|
||||
@@ -504,10 +510,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
::execve(exe, cmd_line, env);
|
||||
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
|
||||
@@ -137,7 +137,6 @@ inline bool wait_until(
|
||||
|
||||
do
|
||||
{
|
||||
int ret_sig = 0;
|
||||
int status;
|
||||
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
|
||||
&& (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
|
||||
namespace boost { namespace asio {
|
||||
|
||||
class mutable_buffer;
|
||||
class mutable_buffers_1;
|
||||
class const_buffer;
|
||||
class const_buffers_1;
|
||||
template<typename T>
|
||||
struct is_mutable_buffer_sequence;
|
||||
|
||||
template<typename T>
|
||||
struct is_const_buffer_sequence;
|
||||
|
||||
|
||||
template<typename Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
@@ -55,8 +55,6 @@ public:
|
||||
inline async_pipe(const async_pipe& rhs);
|
||||
async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
|
||||
{
|
||||
rhs._source.assign (::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
rhs._sink .assign (::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
}
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
explicit async_pipe(::boost::asio::io_context & ios_source,
|
||||
@@ -191,13 +189,15 @@ public:
|
||||
handle_type source(::boost::asio::io_context& ios) &&
|
||||
{
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _source.native_handle());
|
||||
_source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
boost::system::error_code ec;
|
||||
_source.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
|
||||
return stolen;
|
||||
}
|
||||
handle_type sink (::boost::asio::io_context& ios) &&
|
||||
{
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _sink.native_handle());
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
boost::system::error_code ec;
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
|
||||
return stolen;
|
||||
}
|
||||
|
||||
@@ -235,12 +235,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
async_pipe::async_pipe(const async_pipe& p) :
|
||||
_source(const_cast<handle_type&>(p._source).get_executor()),
|
||||
_sink (const_cast<handle_type&>(p._sink).get_executor())
|
||||
{
|
||||
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
|
||||
::boost::winapi::HANDLE_ source;
|
||||
@@ -266,8 +265,10 @@ async_pipe::async_pipe(const async_pipe& p) :
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
_source.assign(source);
|
||||
_sink. assign(sink);
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source.assign(source);
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink. assign(sink);
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +311,44 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
_sink.assign(sink);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
async_pipe& async_pipe::operator=(const basic_pipe<CharT, Traits> & p)
|
||||
{
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
|
||||
::boost::winapi::HANDLE_ source;
|
||||
::boost::winapi::HANDLE_ sink;
|
||||
|
||||
//cannot get the handle from a const object.
|
||||
auto source_in = p.native_source();
|
||||
auto sink_in = p.native_sink();
|
||||
|
||||
if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
source = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
proc, source_in.native_handle(), proc, &source, 0,
|
||||
static_cast<::boost::winapi::BOOL_>(true),
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
if (sink_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
proc, sink_in.native_handle(), proc, &sink, 0,
|
||||
static_cast<::boost::winapi::BOOL_>(true),
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
//so we also assign the io_context
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source.assign(source);
|
||||
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink.assign(sink);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
{
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
@@ -321,6 +360,8 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
auto &source_in = const_cast<::boost::asio::windows::stream_handle &>(p._source);
|
||||
auto &sink_in = const_cast<::boost::asio::windows::stream_handle &>(p._sink);
|
||||
|
||||
source_in.get_executor();
|
||||
|
||||
if (source_in.native_handle() == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
source = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
@@ -338,24 +379,23 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
//so we also assign the io_context
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
|
||||
else
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor());
|
||||
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
|
||||
else
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
async_pipe& async_pipe::operator=(async_pipe && rhs)
|
||||
{
|
||||
if (_source.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::winapi::CloseHandle(_source.native_handle());
|
||||
|
||||
if (_sink.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::winapi::CloseHandle(_sink.native_handle());
|
||||
|
||||
_source.assign(rhs._source.native_handle());
|
||||
_sink .assign(rhs._sink .native_handle());
|
||||
rhs._source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
rhs._sink .assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
_source = std::move(rhs._source);
|
||||
_sink = std::move(rhs._sink);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
|
||||
return false; //correct, nothing left.
|
||||
}
|
||||
//reduce the remaining wait time -> in case interrupted by something else
|
||||
if (wait_time != ::boost::winapi::infinite)
|
||||
if (wait_time != static_cast<int>(::boost::winapi::infinite))
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
|
||||
wait_time -= diff.count();
|
||||
wait_time -= static_cast<std::chrono::system_clock::rep>(diff.count());
|
||||
start_time = now;
|
||||
if (wait_time <= 0)
|
||||
return true; //timeout with other source
|
||||
|
||||
@@ -122,20 +122,6 @@ system("b2", std_out > null);
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
|
||||
template<typename T> using is_streambuf = typename std::is_same<T, boost::asio::streambuf>::type;
|
||||
template<typename T> using is_const_buffer =
|
||||
std::integral_constant<bool,
|
||||
std::is_same< boost::asio::const_buffer, T>::value |
|
||||
std::is_base_of<boost::asio::const_buffer, T>::value
|
||||
>;
|
||||
template<typename T> using is_mutable_buffer =
|
||||
std::integral_constant<bool,
|
||||
std::is_same< boost::asio::mutable_buffer, T>::value |
|
||||
std::is_base_of<boost::asio::mutable_buffer, T>::value
|
||||
>;
|
||||
|
||||
|
||||
struct null_t {constexpr null_t() {}};
|
||||
struct close_t;
|
||||
|
||||
@@ -177,19 +163,25 @@ struct std_in_
|
||||
api::async_pipe_in operator=(async_pipe & p) const {return p;}
|
||||
api::async_pipe_in operator<(async_pipe & p) const {return p;}
|
||||
|
||||
template<typename T, typename = typename std::enable_if<
|
||||
is_const_buffer<T>::value || is_mutable_buffer<T>::value
|
||||
>::type>
|
||||
api::async_in_buffer<const T> operator=(const T & buf) const {return buf;}
|
||||
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
|
||||
api::async_in_buffer<T> operator=(T & buf) const {return buf;}
|
||||
template<typename T>
|
||||
auto operator=(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
|
||||
template<typename T>
|
||||
auto operator<(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
|
||||
|
||||
template<typename T, typename = typename std::enable_if<
|
||||
is_const_buffer<T>::value || is_mutable_buffer<T>::value
|
||||
>::type>
|
||||
api::async_in_buffer<const T> operator<(const T & buf) const {return buf;}
|
||||
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
|
||||
api::async_in_buffer<T> operator<(T & buf) const {return buf;}
|
||||
template<typename T>
|
||||
auto operator=(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
|
||||
template<typename T>
|
||||
auto operator<(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
|
||||
|
||||
template<typename Allocator>
|
||||
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
template<typename Allocator>
|
||||
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
|
||||
template<typename Allocator>
|
||||
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
template<typename Allocator>
|
||||
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
|
||||
};
|
||||
|
||||
@@ -234,13 +226,24 @@ struct std_out_
|
||||
api::async_pipe_out<p1, p2> operator=(async_pipe & p) const {return p;}
|
||||
api::async_pipe_out<p1, p2> operator>(async_pipe & p) const {return p;}
|
||||
|
||||
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator=(const asio::mutable_buffer & buf) const {return buf;}
|
||||
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator=(const asio::mutable_buffers_1 & buf) const {return buf;}
|
||||
api::async_out_buffer<p1, p2, asio::streambuf> operator=(asio::streambuf & os) const {return os ;}
|
||||
template<typename Allocator>
|
||||
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
template<typename Allocator>
|
||||
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator>(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
|
||||
|
||||
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator>(const asio::mutable_buffer & buf) const {return buf;}
|
||||
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator>(const asio::mutable_buffers_1 & buf) const {return buf;}
|
||||
api::async_out_buffer<p1, p2, asio::streambuf> operator>(asio::streambuf & os) const {return os ;}
|
||||
template<typename Buffer>
|
||||
auto operator=(const Buffer & buf) const
|
||||
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<typename Buffer>
|
||||
auto operator>(const Buffer & buf) const
|
||||
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
api::async_out_future<p1,p2, std::string> operator=(std::future<std::string> & fut) const { return fut;}
|
||||
api::async_out_future<p1,p2, std::string> operator>(std::future<std::string> & fut) const { return fut;}
|
||||
|
||||
@@ -56,6 +56,7 @@ exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem :
|
||||
rule test-options ( name )
|
||||
{
|
||||
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
|
||||
#return --log_level=error --report_level=detailed -- ;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +67,9 @@ test-suite bare :
|
||||
[ compile no_ansi_apps.cpp ]
|
||||
[ compile-fail spawn_fail.cpp ]
|
||||
[ compile-fail async_system_fail.cpp ]
|
||||
[ compile asio_no_deprecated.cpp ]
|
||||
;
|
||||
|
||||
echo [ test-options foo ] ;
|
||||
|
||||
test-suite with-valgrind :
|
||||
[ run async.cpp system thread filesystem : [ test-options async ] : sparring_partner ]
|
||||
[ run async_fut.cpp system thread filesystem : [ test-options async_fut ] : sparring_partner ]
|
||||
|
||||
13
test/asio_no_deprecated.cpp
Normal file
13
test/asio_no_deprecated.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by kleme on 26.02.2018.
|
||||
//
|
||||
|
||||
#define BOOST_ASIO_NO_DEPRECATED 1
|
||||
#include <boost/process.hpp>
|
||||
int main() {}
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/posix.hpp>
|
||||
#else
|
||||
#include <boost/process/windows.hpp>
|
||||
#endif
|
||||
@@ -376,4 +376,41 @@ BOOST_AUTO_TEST_CASE(async_error, *boost::unit_test::timeout(3))
|
||||
BOOST_CHECK(!exit_called);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(2)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
bool exit_called = false;
|
||||
std::error_code ec;
|
||||
|
||||
bp::child c(master_test_suite().argv[1],
|
||||
"--wait", "1", "--exit-code", "42",
|
||||
ec,
|
||||
io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
timeout.cancel();
|
||||
exit_called=true;
|
||||
BOOST_CHECK_EQUAL(exit, 42);
|
||||
})
|
||||
);
|
||||
|
||||
BOOST_REQUIRE(!ec);
|
||||
std::thread thr([&]{c.wait();});
|
||||
io_context.run();
|
||||
|
||||
BOOST_CHECK(exit_called);
|
||||
BOOST_CHECK_EQUAL(c.exit_code(), 42);
|
||||
thr.join();
|
||||
|
||||
}*/
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -87,4 +87,56 @@ BOOST_AUTO_TEST_CASE(multithreaded_async_pipe)
|
||||
t.join();
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(move_pipe)
|
||||
{
|
||||
asio::io_context ios;
|
||||
|
||||
bp::async_pipe ap{ios};
|
||||
BOOST_TEST_CHECKPOINT("First move");
|
||||
bp::async_pipe ap2{std::move(ap)};
|
||||
BOOST_CHECK_EQUAL(ap.native_source(), ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
BOOST_CHECK_EQUAL(ap.native_sink (), ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
|
||||
BOOST_TEST_CHECKPOINT("Second move");
|
||||
ap = std::move(ap2);
|
||||
|
||||
{
|
||||
BOOST_TEST_CHECKPOINT("Third move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
ap = std::move(ap_inv);
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
const auto ap3 = std::move(ap_inv);
|
||||
}
|
||||
|
||||
{
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
ap = ap_inv; //copy an invalid pipe
|
||||
}
|
||||
|
||||
{
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
BOOST_TEST_CHECKPOINT("Copy construct");
|
||||
bp::async_pipe ap4{ap_inv};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
#include <boost/asio/yield.hpp>
|
||||
|
||||
@@ -46,8 +47,9 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
BOOST_CHECK(did_something_else);
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
boost::asio::io_context::strand str{ios};
|
||||
boost::asio::post(str, [&]{boost::asio::spawn(ios, stackful);});
|
||||
boost::asio::post(str, [&]{did_something_else = true;});
|
||||
|
||||
ios.run();
|
||||
BOOST_CHECK(did_something_else);
|
||||
|
||||
@@ -132,8 +132,28 @@ BOOST_AUTO_TEST_CASE(async_wait)
|
||||
windows::object_handle handle(io_context.get_executor(), c.native_handle());
|
||||
handle.async_wait(wait_handler(handle.native_handle(), wh_called));
|
||||
#endif
|
||||
std::cout << "async_wait 1" << std::endl;
|
||||
io_context.run();
|
||||
std::cout << "async_wait 2" << std::endl;
|
||||
BOOST_CHECK_MESSAGE(wh_called, "Wait handler not called");
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_nowait)
|
||||
{
|
||||
// No need to call wait when passing an io_context
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
|
||||
std::error_code ec;
|
||||
boost::asio::io_context io_context;
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "221",
|
||||
ec,
|
||||
bp::on_exit=[](int exit_code, std::error_code) mutable {},
|
||||
io_context
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
io_context.run();
|
||||
BOOST_CHECK_EQUAL(221, c.exit_code());
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
|
||||
using namespace std;
|
||||
namespace bp = boost::process;
|
||||
@@ -38,7 +39,8 @@ BOOST_AUTO_TEST_CASE(named, *boost::unit_test::timeout(2))
|
||||
#if defined( BOOST_WINDOWS_API )
|
||||
bp::pipe pipe("\\\\.\\pipe\\pipe_name");
|
||||
#elif defined( BOOST_POSIX_API )
|
||||
bp::pipe pipe("./test_pipe");
|
||||
const auto home_path = boost::this_process::environment()["HOME"].to_string();
|
||||
bp::pipe pipe(home_path + "/.boost_process_test_pipe");
|
||||
#endif
|
||||
|
||||
std::string in = "xyz";
|
||||
|
||||
@@ -51,6 +51,8 @@ BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(5))
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
BOOST_TEST_INFO("Launching child 2");
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
bp::args={"test", "--prefix-once", "hello "},
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/process/posix.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
|
||||
@@ -21,6 +23,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bind_fd, *boost::unit_test::timeout(2))
|
||||
@@ -99,3 +102,67 @@ BOOST_AUTO_TEST_CASE(execve_throw_on_error, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(e.code().value(), ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
const auto pid = boost::this_process::get_id();
|
||||
const auto fd_path = fs::path("/proc") / std::to_string(pid) / "fd";
|
||||
|
||||
auto get_fds = [&]{
|
||||
std::vector<int> fds;
|
||||
for (auto && fd : fs::directory_iterator(fd_path))
|
||||
fds.push_back(std::stoi(fd.path().filename().string()));
|
||||
return fds;
|
||||
};
|
||||
|
||||
std::vector<int> fd_list = get_fds();
|
||||
if (fd_list.empty()) //then there's no /proc in the current linux distribution.
|
||||
return;
|
||||
|
||||
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDOUT_FILENO) != fd_list.end());
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDIN_FILENO) != fd_list.end());
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDERR_FILENO) != fd_list.end());
|
||||
|
||||
bp::pipe p; //should add two descriptors.
|
||||
|
||||
auto fd_list_new = get_fds();
|
||||
|
||||
BOOST_CHECK_EQUAL(fd_list_new.size(), fd_list.size() + 2);
|
||||
fd_list.push_back(p.native_source());
|
||||
fd_list.push_back(p.native_sink());
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "123", ec), 123);
|
||||
|
||||
fd_list_new = get_fds();
|
||||
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
|
||||
|
||||
|
||||
const int native_source = p.native_source();
|
||||
BOOST_CHECK_EQUAL(
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
bp::std_in < p,
|
||||
"test", "--exit-code", "123", ec), 123);
|
||||
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
////now, p.source should be closed, so we remove it from fd_list
|
||||
|
||||
const auto itr = std::find(fd_list.begin(), fd_list.end(), native_source);
|
||||
if (itr != fd_list.end())
|
||||
fd_list.erase(itr);
|
||||
|
||||
fd_list_new = get_fds();
|
||||
|
||||
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user