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

Merge branch 'develop' into limit_fd

# Conflicts:
#	include/boost/process/detail/posix/pipe_out.hpp
#	test/Jamfile.jam
This commit is contained in:
Klemens David Morgenstern
2019-05-12 17:43:40 +07:00
11 changed files with 194 additions and 116 deletions

View File

@@ -70,7 +70,6 @@ jobs:
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
bash <(curl -s https://codecov.io/bash) -x gcov > /dev/null || true
echo "BUILD_RESULT: $FAILED"
exit $FAILED

View File

@@ -23,8 +23,6 @@ os:
- linux
- osx
secure: "vs7qgXb0lQg8CTyDPSi3RQtOIOtssaCkBIx86UoEvTXwJCTOLPe7ZufQ0lobn0OVWo261AMx9GbumBBzqfsvJc1G6ixGBVwymiGli/R8DZDvvg9UdljsEk65s/XbujE/9qh97zKGGioFyCn1Bmf5+SdDAxsuXTZm/cBny5VxYaaCR7s2cFUmp4up/djqg1GI7uwBh3ceodT3OL1X3dlMV59gOJWWNsB+RO9b9DPhTW7nOlMNRiEFik4rweecQB0JS8LaHDjYwzIRrGYHX+lR9cE/O8GCCHcUOmq9jCozDdxx+HZRu4rb1ST1RiDbvYaoeTif0Df1fVXHWOoO2D4NlXB6tJPXw2mkop00j6zkcydUJYid6T1lwfEpXAhd5A9FvOIXO5hoju1wlqfkU2eFQ9Na8z8bCIX2niZmveZWp4Ag52gEPzJMFx9hHGT8J4FWMvkqTWezux1sPZrjZjc0kXdJrIp84D9MsBc1sKrxOAOb5ekSfIK5n4JDkgUtuwMSTvEdWqNJXFPZq1rEu4GTwX99z3/XF+pM5XaCDQtZ/zUA5SPHhy0dKLH/BvceUqLJt53+lMcpsltJDB+XxQ/CFL7IdgR91OKGus/z4dbVWiSdkoNvcuZqjQLFLOMVNxoqC6PRvDAEhpy21j/5GUPvM5baQS7IEin0NF7bOTtXJdY="
env:
matrix:
- BADGE=linux
@@ -73,7 +71,7 @@ before_install:
# Set this to the name of the library
- PROJECT_TO_TEST=`basename $TRAVIS_BUILD_DIR`
- echo "Testing $PROJECT_TO_TEST"
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; REPORT_CI=--boost-process-report-ci USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
# Cloning Boost libraries (fast nondeep cloning)
- BOOST=$HOME/boost-local
- git init $BOOST
@@ -103,8 +101,8 @@ before_install:
- echo BOOST_TEST_CATCH_SYSTEM_ERRORS $BOOST_TEST_CATCH_SYSTEM_ERRORS
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=.
- ../../../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=. $REPORT_CI
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
after_success:
# Copying Coveralls data to a separate folder
- mkdir -p $TRAVIS_BUILD_DIR/coverals
@@ -133,6 +131,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

@@ -14,25 +14,15 @@
#include <boost/process/pipe.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <unistd.h>
#include <array>
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct pipe_out : handler_base_ext, ::boost::process::detail::uses_handles
struct pipe_out : handler_base_ext
{
int sink;
int source; //opposite end
std::array<int, 4> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {source, sink, pp1, pp2};
}
pipe_out(int sink, int source) : sink(sink), source(source) {}
template<typename T>
@@ -63,7 +53,9 @@ 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(), "dup2() failed");
::close(sink);
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
@@ -73,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);
}
@@ -85,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

@@ -27,7 +27,7 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
ec.clear();
int status;
::waitpid(p.pid, &status, 0); //just to clean it up
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
}
inline void terminate(const child_handle &p)

View File

@@ -54,11 +54,18 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
auto get_timespec =
[](const Duration & dur)
@@ -69,8 +76,8 @@ inline bool wait_until(
return ts;
};
pid_t ret;
int status;
int ret;
int status{0};
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -113,9 +120,17 @@ inline bool wait_until(
{
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);
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
::exit(0);
}
@@ -125,27 +140,28 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, SIGTERM);
::waitpid(pid, &res, WNOHANG);
::kill(pid, SIGKILL);
::waitpid(pid, &res, 0);
}
};
child_cleaner_t child_cleaner{timeout_pid};
do
{
int ret_sig = 0;
int sig_;
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret_sig = ::sigwait(&sigset, nullptr);
&& (WIFEXITED(status) || WIFSIGNALED(status)))
return false;
ret = ::sigwait(&sigset, &sig_);
errno = 0;
ret = ::waitpid(p.pid, &status, WNOHANG);
if ((ret_sig == SIGCHLD) &&
if ((ret == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
if (ret <= 0)
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
{
timed_out = Clock::now() >= time_out;
if (timed_out)

View File

@@ -59,15 +59,11 @@ inline bool wait_until(
std::error_code & ec) noexcept
{
::sigset_t sigset;
::siginfo_t siginfo;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
auto get_timespec =
[](const Duration & dur)
+[](const Duration & dur)
{
::timespec ts;
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
@@ -79,6 +75,21 @@ inline bool wait_until(
bool timed_out = false;
int ret;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
::sigset_t sigset;
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
{
@@ -86,7 +97,6 @@ inline bool wait_until(
return false;
}
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
auto ts = get_timespec(time_out - Clock::now());
@@ -102,63 +112,10 @@ inline bool wait_until(
return false;
}
//check if we're done
//check if we're done ->
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
}
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
#else
//if we do not have sigtimedwait, we fork off a child process to get the signal in time
pid_t timeout_pid = ::fork();
if (timeout_pid == -1)
{
ec = boost::process::detail::get_last_error();
return true;
}
else if (timeout_pid == 0)
{
auto ts = get_timespec(time_out - Clock::now());
::setpgid(0, p.grp);
::nanosleep(&ts, nullptr);
::exit(0);
}
struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, SIGTERM);
::waitpid(pid, &res, WNOHANG);
}
};
child_cleaner_t child_cleaner{timeout_pid};
do
{
int status;
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret = ::sigwait(&sigset, nullptr);
errno = 0;
if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
if (ret == -1)
{
ec = get_last_error();
return false;
}
//check if we're done
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
}
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
#endif
while (((ret != -1) || ((errno != ECHILD) && (errno != ESRCH))) && !(timed_out = (Clock::now() > time_out)));
if (errno != ECHILD)
{
@@ -171,6 +128,103 @@ inline bool wait_until(
return true; //even if timed out, there are no child proccessess left
}
#else
//if we do not have sigtimedwait, we fork off a child process to get the signal in time
static ::gid_t gid = 0;
gid = p.grp;
static auto sig_handler =
+[](int sig)
{
errno = 0;
if (sig == SIGUSR1)
::setpgid(0, 0);
else if (sig == SIGUSR2)
::setpgid(0, gid);
};
auto sigusr1 = ::signal(SIGUSR1, sig_handler);
auto sigusr2 = ::signal(SIGUSR2, sig_handler);
pid_t timeout_pid = ::fork();
if (timeout_pid == -1)
{
ec = boost::process::detail::get_last_error();
::signal(SIGUSR1, sigusr1);
::signal(SIGUSR2, sigusr2);
return true;
}
else if (timeout_pid == 0)
{
::setpgid(0, p.grp);
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
::exit(0);
}
::signal(SIGUSR1, sigusr1);
::signal(SIGUSR2, sigusr2);
struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, SIGKILL);
::waitpid(pid, &res, 0);
}
};
child_cleaner_t child_cleaner{timeout_pid};
while (!(timed_out = (Clock::now() > time_out)))
{
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED);
if (ret == -1)
{
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
ec = boost::process::detail::get_last_error();
return false;
}
::kill(timeout_pid, SIGUSR1);
//if it is not, we gotta check who the signal came from, so let's remove the process from the group
//check if the group is empty -> will give an error of ECHILD
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WCONTINUED | WNOWAIT | WNOHANG);
if (ret == -1)
{
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
else
{
ec = boost::process::detail::get_last_error();
return false;
}
}
else
::kill(timeout_pid, SIGUSR2);
}
return !timed_out;
#endif
}
template< class Clock, class Duration >

View File

@@ -16,7 +16,6 @@ if [ os.name ] = NT
lib ws2_32 ;
lib shell32 ;
lib Advapi32 ;
lib Ntdll ;
}
project : requirements
@@ -45,7 +44,7 @@ alias coroutine : /boost//coroutine : <link>static ;
lib multi_ref : multi_ref1.cpp multi_ref2.cpp system : <target-os>windows:<source>shell32 ;
exe sparring_partner : sparring_partner.cpp program_options system filesystem iostreams :
<warnings>off <target-os>windows:<source>shell32 <target-os>windows:<source>Ntdll
<warnings>off <target-os>windows:<source>shell32
;
exe exit_argc : exit_argc.cpp :
@@ -56,8 +55,15 @@ 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 -- ;
if "--boost-process-report-ci" in [ modules.peek : ARGV ]
{
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
}
else
{
return --log_level=error --report_level=detailed -- ;
}
}
@@ -91,7 +97,6 @@ test-suite with-valgrind :
[ run exit_code.cpp program_options system filesystem : [ test-options exit_code ] : sparring_partner ]
[ run extensions.cpp system filesystem : [ test-options extensions ] : sparring_partner ]
[ run env.cpp program_options system filesystem : [ test-options env ] : sparring_partner ]
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
[ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner ]

View File

@@ -67,7 +67,7 @@ build_script:
after_build:
before_test:
test_script:
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=. --boost-process-report-ci
after_test:
on_success:

View File

@@ -58,10 +58,15 @@ BOOST_AUTO_TEST_CASE(group_test, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "group_test out" << std::endl;
}
BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
{
std::cout << "attached" << std::endl;
using boost::unit_test::framework::master_test_suite;
bp::ipstream is;
@@ -89,10 +94,11 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
#if defined( BOOST_POSIX_API )
::waitpid(sub_c.id(), nullptr, WNOHANG);
BOOST_CHECK(kill(sub_c.id(), 0) == 0);
#else
BOOST_CHECK(sub_c.running());
@@ -101,23 +107,27 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE_NO_THROW(g.terminate());
BOOST_CHECK(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
BOOST_CHECK(!c.running());
#if defined( BOOST_POSIX_API )
bool still_runs = kill(sub_c.id(), 0) == 0;
#else
errno = 0;
::waitpid(sub_c.id(), nullptr, WNOHANG);
bool still_runs = (kill(sub_c.id(), 0) == 0) && (errno != ECHILD) && (errno != ESRCH);
#else
bool still_runs = sub_c.running();
#endif
BOOST_CHECK_MESSAGE(!still_runs, boost::process::detail::get_last_error().message());
BOOST_CHECK(!still_runs);
if (still_runs)
sub_c.terminate();
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "attached out" << std::endl;
}
@@ -176,4 +186,6 @@ BOOST_AUTO_TEST_CASE(detached, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cerr << "detached out" << std::endl;
}

View File

@@ -10,7 +10,7 @@
#define BOOST_TEST_MAIN
#define BOOST_TEST_IGNORE_SIGCHLD
#include <boost/test/included/unit_test.hpp>
#include <fstream>
#include <boost/system/error_code.hpp>
#include <boost/asio.hpp>
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
bp::child c2(
master_test_suite().argv[1],
"--wait", "3",
"--wait", "4",
g,
ec
);