2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-19 16:32:15 +00:00

Compare commits

...

57 Commits

Author SHA1 Message Date
Klemens David Morgenstern
83c2a986cb trait & fwd decl fixes 2019-04-10 13:41:08 +08:00
Klemens David Morgenstern
41fff78e00 Trying to add dynamic_buffer with sfinae as proposed in boostorg/process#56 2019-04-10 12:51:46 +08:00
Klemens David Morgenstern
e85f0d0816 Closes klemens-morgenstern/boost-process#139 2019-04-10 12:18:02 +08:00
Klemens David Morgenstern
af54484bc2 sed typo fix 2019-04-07 21:40:28 +08:00
Klemens David Morgenstern
ebcb30e4bd reduced amount of threads for build 2019-04-07 13:31:46 +08:00
Klemens David Morgenstern
5371c9813b typo fix 2019-04-07 13:07:11 +08:00
Klemens David Morgenstern
9a833c610d trying to fix the exit code 2019-04-07 12:53:02 +08:00
Klemens David Morgenstern
c99ebfee7a flipping typos 2019-04-07 12:34:37 +08:00
Klemens David Morgenstern
34861366e0 added sed for annotations 2019-04-07 12:19:17 +08:00
Klemens David Morgenstern
e6722c452c faulty log name fix (attempt) 2019-04-07 11:47:18 +08:00
Klemens David Morgenstern
b1e38842fb renamed the no-valgrind log 2019-04-07 03:15:38 +08:00
Klemens David Morgenstern
bcc9826e67 still doing the ci thing here 2019-04-07 03:09:17 +08:00
Klemens David Morgenstern
b6c6753b87 added valgrind to circle Ci 2019-04-07 02:57:14 +08:00
Klemens David Morgenstern
0d008a88fc dammit circle 2019-04-07 02:49:14 +08:00
Klemens David Morgenstern
f92ec53968 circle ci example job fix 2019-04-07 02:36:14 +08:00
Klemens David Morgenstern
00a87d0a67 report-ci update for circleci 2019-04-07 02:29:52 +08:00
Klemens David Morgenstern
462334639c typo fix 2019-04-07 02:08:31 +08:00
Klemens David Morgenstern
7129182044 ci fixes - the similtaneous wait doen't work on osx 2019-04-07 02:03:08 +08:00
Klemens David Morgenstern
03fbed44ad typo fix 2019-04-07 01:23:10 +08:00
Klemens David Morgenstern
0277c4fcec CI fixes 2019-04-07 01:19:19 +08:00
Klemens David Morgenstern
4bc1ae6ff8 added async fix & fixed dangling develop workaroudn for async_pipe copy 2019-04-07 00:21:27 +08:00
Klemens David Morgenstern
266c4503aa Merge branch 'develop' into asio_no_deprecated 2019-04-06 23:58:47 +08:00
Klemens David Morgenstern
80479b6b70 removed errornous global variable 2019-04-06 23:58:27 +08:00
Klemens David Morgenstern
823a346a08 added another exit_code test 2019-04-06 23:53:11 +08:00
Klemens David Morgenstern
8f3a7b9c63 Merge branch 'develop' into asio_no_deprecated 2019-04-06 22:35:45 +08:00
Klemens David Morgenstern
2ebcc07892 Merge branch 'circleci' into develop 2019-04-06 22:33:22 +08:00
Klemens Morgenstern
a673cd9643 Update config.yml 2019-04-06 22:25:52 +08:00
Klemens Morgenstern
12971db132 Update config.yml 2019-04-06 22:19:11 +08:00
Klemens Morgenstern
94be279992 Update config.yml 2019-04-06 22:15:38 +08:00
Klemens David Morgenstern
12cded5995 switched test to strand 2019-04-06 22:15:12 +08:00
Klemens David Morgenstern
0c16a0e5b3 set params back to support report.ci 2019-04-06 22:11:04 +08:00
Klemens Morgenstern
e4a6fde7b9 Update config.yml 2019-04-06 18:28:40 +08:00
Klemens David Morgenstern
92d2bebaaf added test and fixed #189 and #157 as well as #93 2019-04-06 18:25:08 +08:00
Klemens David Morgenstern
7e7a8cbc1d fixed #194 2019-04-06 17:29:52 +08:00
Klemens Morgenstern
4f767f4bfe Update config.yml 2019-04-06 17:25:03 +08:00
Klemens Morgenstern
63f714ae2f Update config.yml 2019-04-06 16:54:07 +08:00
Klemens Morgenstern
1b3b9b707c Update config.yml 2019-04-06 16:41:44 +08:00
Klemens Morgenstern
f64bc8a6d4 Update config.yml 2019-04-06 16:34:26 +08:00
Klemens Morgenstern
102834130d Update config.yml 2019-04-06 16:28:48 +08:00
Klemens Morgenstern
fdc6a11cbc Update config.yml 2019-04-06 16:09:16 +08:00
Klemens Morgenstern
19b20f55ce Update config.yml 2019-04-06 16:04:28 +08:00
Klemens David Morgenstern
60d072ce46 Merge branch 'develop' into circleci 2019-04-06 15:29:22 +08:00
Klemens David Morgenstern
34f05b9276 Merge branch 'develop' into asio_no_deprecated 2019-04-06 15:22:10 +08:00
Klemens David Morgenstern
1b476b0430 Closes #65 2019-04-06 15:21:52 +08:00
Klemens David Morgenstern
b294710e60 fixed self-pipe issue 2019-04-06 14:42:08 +08:00
Klemens David Morgenstern
f8cd325d1b trying circleci 2019-04-05 15:13:14 +08:00
Klemens David Morgenstern
2a6c23e173 typo fix 2019-04-04 18:11:33 +08:00
Klemens David Morgenstern
d7768b7221 Merge branch 'develop' into asio_no_deprecated 2019-04-04 17:57:28 +08:00
Klemens David Morgenstern
b8821eac57 should close #78 2019-03-30 23:14:25 +08:00
Klemens David Morgenstern
d20b64cf37 removed unnecessary int to remove warning 2019-03-29 01:39:00 +08:00
Klemens David Morgenstern
f0ddd6ca29 typo & structure fix 2019-03-29 00:57:11 +08:00
Klemens David Morgenstern
574d9e09d6 should close #193 2019-03-29 00:32:12 +08:00
Klemens Morgenstern
7085a50f36 Merge pull request #77 from SimonEbner/develop
Fix leaking pipe
2019-03-23 00:01:27 +08:00
Klemens Morgenstern
0485459da2 Merge pull request #76 from orgads/patch-1
Fix signed/unsigned comparison
2019-03-22 23:56:13 +08:00
Simon Ebner
318439af2e Fix Issue 62
Fixes a leaking pipe. See https://github.com/boostorg/process/issues/62
2019-03-22 15:27:35 +01:00
Orgad Shaneh
4d52ff362f Fix signed/unsigned comparison 2019-03-21 23:39:27 +02:00
Klemens David Morgenstern
6abce365c5 started on asio_no_deprecated.cpp 2018-03-11 20:57:18 +01:00
19 changed files with 503 additions and 178 deletions

76
.circleci/config.yml Normal file
View 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

View File

@@ -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)

View File

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

View File

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

View File

@@ -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";

View File

@@ -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)))

View File

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

View File

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

View File

@@ -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

View File

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

View File

@@ -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 ]

View 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

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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());
}

View File

@@ -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";

View File

@@ -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 "},

View File

@@ -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());
}