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

Compare commits

...

65 Commits

Author SHA1 Message Date
Klemens David Morgenstern
0edff5449a Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
2019-05-08 16:53:45 +07:00
Klemens David Morgenstern
417ea77f2f changed SIGTERM to SIGKILL 2019-05-08 16:50:49 +07:00
Klemens David Morgenstern
66c2867371 removed travis token 2019-05-08 16:30:39 +07:00
Klemens David Morgenstern
caa7b2fcc8 two minor fixes 2019-05-08 16:28:54 +07:00
Klemens David Morgenstern
2314e19f12 restructured osx wait
Check if it solves klemens-morgenstern/boost-process#200
2019-05-07 12:01:04 +07:00
Klemens David Morgenstern
2aa5e1461c Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	test/Jamfile.jam
2019-04-20 16:41:31 +08:00
Klemens Morgenstern
61fa15fa48 Merge pull request #197 from toonetown/fix-wait_for-fork-macOS
Use SIGTERM instead of `-15`.
2019-04-20 15:39:54 +07:00
Nathan Toone
92508e06a1 Use SIGTERM instead of -15. 2019-04-19 23:44:54 -06: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
8d48d9cbfa dumb workaround -> fix report.ci detection! 2019-03-21 16:21:40 +08:00
Klemens David Morgenstern
f25c31c847 added test by @grives as recommended in #69 2019-03-21 12:51:32 +08:00
Klemens David Morgenstern
6abce365c5 started on asio_no_deprecated.cpp 2018-03-11 20:57:18 +01:00
20 changed files with 510 additions and 156 deletions

77
.circleci/config.yml Normal file
View File

@@ -0,0 +1,77 @@
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
cd ../../..
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

@@ -105,6 +105,8 @@ script:
# `--coverage` flags required to generate coverage info for Coveralls
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- cat /home/travis/boost-local/libs/boost-process/test/report_wait_for.xml
- cat /home/travis/boost-local/libs/boost-process/test/log_wait_for.xml
after_success:
# Copying Coveralls data to a separate folder
- mkdir -p $TRAVIS_BUILD_DIR/coverals
@@ -133,4 +135,6 @@ after_success:
- coveralls-lcov coverals/coverage.info
after_script:
- curl -s https://report.ci/upload.py | python - --token=$REPORT_CI_TOKEN --name="$BADGE test run"
- curl -s https://report.ci/upload.py | python - --name="$BADGE test run"
- bash <(curl -s https://codecov.io/bash)

View File

@@ -16,7 +16,7 @@ In that it is different than other facilities (like sockets) and provides anothe
Pipes are typically used for interprocess communication. The main reason is, that pipes can be directly assigned to the process stdio, i.e. stderr, stdin and stdout.
Additionally, half of the pipe can be inherited to the child process and closed in the father process. This will cause the pipe to be broken when the child process exits.
Though please not, that if the the same thread reads and write to a pipe, it will only talk to itself.
Though please note, that if the the same thread reads and write to a pipe, it will only talk to itself.
[section:anonymous Anonymous Pipes]

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

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

@@ -52,8 +52,10 @@ template<typename Executor>
void pipe_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDOUT_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup3() failed");
::close(sink);
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
@@ -63,7 +65,9 @@ void pipe_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(sink);
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
@@ -75,8 +79,8 @@ void pipe_out<1,2>::on_exec_setup(Executor &e) const
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(sink);
::close(source);
if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
::close(sink);
}
class async_pipe;

View File

@@ -111,11 +111,14 @@ inline bool wait_until(
}
else if (timeout_pid == 0)
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
while (rem.tv_sec > 0 || rem.tv_nsec > 0)
::nanosleep(&rem, &rem);
do
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
}
while (rem.tv_sec > 0 || rem.tv_nsec > 0);
::exit(0);
}
@@ -125,8 +128,8 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, -15);
::waitpid(pid, &res, WNOHANG);
::kill(pid, SIGTERM);
::waitpid(pid, &res, 0);
}
};
child_cleaner_t child_cleaner{timeout_pid};

View File

@@ -117,9 +117,13 @@ inline bool wait_until(
}
else if (timeout_pid == 0)
{
auto ts = get_timespec(time_out - Clock::now());
::setpgid(0, p.grp);
::nanosleep(&ts, nullptr);
do
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
}
while (rem.tv_sec > 0 || rem.tv_nsec > 0);
::exit(0);
}
@@ -129,7 +133,7 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, -15);
::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
}
};
@@ -137,7 +141,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

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

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

View File

@@ -111,4 +111,26 @@ BOOST_AUTO_TEST_CASE(wait_until_ec)
BOOST_CHECK_MESSAGE(!ec, ec.message());
}
BOOST_AUTO_TEST_CASE(wait_for_exit_before_timeout)
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
auto launch_time = std::chrono::system_clock::now();
bp::child c(
master_test_suite().argv[1],
bp::args+={"test", "--wait", "1"},
ec
);
BOOST_REQUIRE(!ec);
BOOST_CHECK(c.wait_for(std::chrono::seconds(20)));
auto timeout_t = std::chrono::system_clock::now();
// check that we didn't wait the entire timeout period
BOOST_CHECK_LT(std::chrono::duration_cast<std::chrono::seconds>(timeout_t - launch_time).count(), 20);
}
BOOST_AUTO_TEST_SUITE_END();