mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
260 Commits
boost-1.68
...
boost-1.71
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a554c92b5 | ||
|
|
fa81cecffc | ||
|
|
977b76f6aa | ||
|
|
fc1acb82d9 | ||
|
|
db7af9f87d | ||
|
|
a350cc346b | ||
|
|
9fa86d3d65 | ||
|
|
e426f2bfac | ||
|
|
6b173117aa | ||
|
|
ecbc93408f | ||
|
|
6ba9a48d15 | ||
|
|
519c0a636a | ||
|
|
82195c61af | ||
|
|
c604e3a20e | ||
|
|
b27d0170ba | ||
|
|
98fd4eecf0 | ||
|
|
3799315ce7 | ||
|
|
b9431ba492 | ||
|
|
c0dca35615 | ||
|
|
790d79db9c | ||
|
|
5de0a795d1 | ||
|
|
cbaa913e3d | ||
|
|
5786162fb5 | ||
|
|
78c44dd560 | ||
|
|
23ff67d83d | ||
|
|
476c6ccd95 | ||
|
|
28126b3432 | ||
|
|
ed8388d091 | ||
|
|
43c402a5da | ||
|
|
9ff2f6f3ef | ||
|
|
b2a0fadaca | ||
|
|
3c4057204e | ||
|
|
717ac47510 | ||
|
|
4f6f4eb391 | ||
|
|
99e04036c7 | ||
|
|
519e339365 | ||
|
|
d2c930470f | ||
|
|
d5709ae747 | ||
|
|
d2ab81b1b9 | ||
|
|
885557fe01 | ||
|
|
b5b758f89a | ||
|
|
2a954eb809 | ||
|
|
0edff5449a | ||
|
|
417ea77f2f | ||
|
|
9e3fdc9669 | ||
|
|
66c2867371 | ||
|
|
caa7b2fcc8 | ||
|
|
6263e74bcd | ||
|
|
faae08ee64 | ||
|
|
2314e19f12 | ||
|
|
cfd0fc055c | ||
|
|
849b5d0f30 | ||
|
|
d13df2a194 | ||
|
|
86fc3b0b4d | ||
|
|
4733ca719f | ||
|
|
296f12eb64 | ||
|
|
96d3470e37 | ||
|
|
2265c98d81 | ||
|
|
060e5c2526 | ||
|
|
b4894807f1 | ||
|
|
2aa5e1461c | ||
|
|
61fa15fa48 | ||
|
|
92508e06a1 | ||
|
|
e85f0d0816 | ||
|
|
5e90c8de9b | ||
|
|
a486a25a07 | ||
|
|
f8c0dd4da5 | ||
|
|
ee6870bfbc | ||
|
|
0422b6bfb8 | ||
|
|
7fc41b2815 | ||
|
|
a49f1f6e2d | ||
|
|
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 | ||
|
|
8d48d9cbfa | ||
|
|
f25c31c847 | ||
|
|
3eacae5e38 | ||
|
|
14b294c10c | ||
|
|
881da4f9e2 | ||
|
|
2758e8d438 | ||
|
|
b5a6b4b945 | ||
|
|
ac94f81b77 | ||
|
|
06747d7dd3 | ||
|
|
fb9b9215e0 | ||
|
|
eed0505f6e | ||
|
|
8f6aa8bcff | ||
|
|
6f9cabbd04 | ||
|
|
355d20faf3 | ||
|
|
15c4a64a81 | ||
|
|
49ec65a735 | ||
|
|
7cdea96caf | ||
|
|
8bf7da3b6b | ||
|
|
c1ad9d1227 | ||
|
|
e2e2b5ddb2 | ||
|
|
b4584dd0b0 | ||
|
|
55e5d178bd | ||
|
|
f778702257 | ||
|
|
b4ff07cfcb | ||
|
|
d43858078f | ||
|
|
f2cf918be5 | ||
|
|
54dd027a64 | ||
|
|
1ea894ab30 | ||
|
|
92c45f4b61 | ||
|
|
03cba8f5bd | ||
|
|
97f08ba0b3 | ||
|
|
c843815c6c | ||
|
|
b5785b3370 | ||
|
|
a6e5a5a619 | ||
|
|
2d627f633b | ||
|
|
ef486764ac | ||
|
|
fce3962376 | ||
|
|
5a40a0ee9c | ||
|
|
4bf795922d | ||
|
|
1646b240c7 | ||
|
|
d4f7bfaa6c | ||
|
|
bac06d2bc7 | ||
|
|
579499d9e8 | ||
|
|
b8c46bfc47 | ||
|
|
d234ce4f63 | ||
|
|
1be807e748 | ||
|
|
f4db2613eb | ||
|
|
bae6098e91 | ||
|
|
c40153fab1 | ||
|
|
e5eec4a5dc | ||
|
|
887bb8c482 | ||
|
|
0642bf5e70 | ||
|
|
513208a117 | ||
|
|
49247f0a83 | ||
|
|
ccb7dd482a | ||
|
|
08f40ff46b | ||
|
|
956f7b5fdf | ||
|
|
40d0f8c401 | ||
|
|
cbc7580fcd | ||
|
|
f96dfd6e8f | ||
|
|
ea24ebaf92 | ||
|
|
504e760760 | ||
|
|
c5e7cfb4f5 | ||
|
|
9904e3a5e8 | ||
|
|
191673b049 | ||
|
|
3a6d11f9b8 | ||
|
|
e022ad3742 | ||
|
|
16d9350992 | ||
|
|
5e1d7b6901 | ||
|
|
82d4cef182 | ||
|
|
5a112ea4bb | ||
|
|
b058e1cadf | ||
|
|
a8b28ef262 | ||
|
|
080f3fb074 | ||
|
|
997f10c7e9 | ||
|
|
4ced4d0933 | ||
|
|
eafe8e327a | ||
|
|
a4c89a3dec | ||
|
|
902390d57a | ||
|
|
5c55590922 | ||
|
|
0485932309 | ||
|
|
e1a3aded4e | ||
|
|
5b2d5c76c8 | ||
|
|
2c3c9e84a5 | ||
|
|
ad90ca6366 | ||
|
|
88952f0ab2 | ||
|
|
29cd54ea8c | ||
|
|
c2ee6da367 | ||
|
|
317801ca5e | ||
|
|
3923da14f7 | ||
|
|
5aa691cc3a | ||
|
|
04712d57f4 | ||
|
|
ed4c861e78 | ||
|
|
587eaa8054 | ||
|
|
78b5ea1f6b | ||
|
|
6412aa3ece | ||
|
|
68a3ba0c41 | ||
|
|
ecaba9efc1 | ||
|
|
fb682944d9 | ||
|
|
9c60e4987c | ||
|
|
59b361fb46 | ||
|
|
f3b2c0a67e | ||
|
|
69c04d5e29 | ||
|
|
78f4115a32 | ||
|
|
9b1b83f5e7 | ||
|
|
bd859e98d3 | ||
|
|
d7accdcf0c | ||
|
|
d159fea7b8 | ||
|
|
b5b91d578d | ||
|
|
498055bc8d | ||
|
|
502169a5ad | ||
|
|
42fbbbd8a6 | ||
|
|
dd0b26de4c | ||
|
|
10665bfaff | ||
|
|
2576ed166f | ||
|
|
9ad7413189 | ||
|
|
9cd405a66f | ||
|
|
fc6773d7d3 | ||
|
|
c3b707b709 | ||
|
|
57e9dfb705 | ||
|
|
4fd8887601 | ||
|
|
3cf4bf6480 | ||
|
|
256523d36e | ||
|
|
6ba8e88def | ||
|
|
f139f863a0 | ||
|
|
0938103427 | ||
|
|
e72127f9f8 | ||
|
|
eea73753b5 | ||
|
|
4f3b425073 | ||
|
|
dcb8a0266a | ||
|
|
99285a9de6 | ||
|
|
6cc31b93d8 | ||
|
|
d1ce19d848 | ||
|
|
f00895a9fc | ||
|
|
8d2bd87707 | ||
|
|
6abce365c5 |
75
.circleci/config.yml
Normal file
75
.circleci/config.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
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
|
||||
bash <(curl -s https://codecov.io/bash) -x gcov > /dev/null || true
|
||||
echo "BUILD_RESULT: $FAILED"
|
||||
exit $FAILED
|
||||
185
.travis.yml
185
.travis.yml
@@ -10,28 +10,6 @@
|
||||
#
|
||||
# File revision #6
|
||||
|
||||
env:
|
||||
global:
|
||||
# Autodetect Boost branch by using the following code: - BRANCH_TO_TEST=$TRAVIS_BRANCH
|
||||
# or just directly specify it
|
||||
- BRANCH_TO_TEST=$TRAVIS_BRANCH
|
||||
|
||||
# Files, which coverage results must be ignored (files from other projects).
|
||||
# Example: - IGNORE_COVERAGE='*/boost/progress.hpp */filesystem/src/*'
|
||||
- IGNORE_COVERAGE=''
|
||||
|
||||
# Explicitly remove the following library from Boost. This may be usefull, if you're for example running Travis
|
||||
# from `Boost.DLL` repo, while Boost already has `dll`.
|
||||
#
|
||||
# By default is eaual to - BOOST_REMOVE=`basename $TRAVIS_BUILD_DIR`
|
||||
# This will force to use local repo content, instead of the Boost's default
|
||||
# not needed because process is not yet in boost.
|
||||
- BOOST_REMOVE=process
|
||||
|
||||
matrix:
|
||||
- CXX_STANDARD=c++11 TOOLSET=gcc-5
|
||||
- CXX_STANDARD=c++1y TOOLSET=gcc-5
|
||||
|
||||
|
||||
###############################################################################################################
|
||||
# From this point and below code is same for all the Boost libs
|
||||
@@ -39,81 +17,120 @@ env:
|
||||
sudo: false
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
- gcc
|
||||
|
||||
os:
|
||||
- linux
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- BADGE=linux
|
||||
- BADGE=osx
|
||||
global:
|
||||
# Autodetect Boost branch by using the following code: - BRANCH_TO_TEST=$TRAVIS_BRANCH
|
||||
# or just directly specify it
|
||||
- BRANCH_TO_TEST=$TRAVIS_BRANCH
|
||||
|
||||
# Files, which coverage results must be ignored (files from other projects).
|
||||
# Example: - IGNORE_COVERAGE='*/boost/progress.hpp */filesystem/src/*'
|
||||
- IGNORE_COVERAGE=''
|
||||
|
||||
# Explicitly remove the following library from Boost. This may be usefull, if you're for example running Travis
|
||||
# from `Boost.DLL` repo, while Boost already has `dll`.
|
||||
#
|
||||
# By default is eaual to - BOOST_REMOVE=`basename $TRAVIS_BUILD_DIR`
|
||||
# This will force to use local repo content, instead of the Boost's default
|
||||
# not needed because process is not yet in boost.
|
||||
- BOOST_REMOVE=process
|
||||
- CXX_STANDARD=gnu++11
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: linux
|
||||
env: BADGE=osx
|
||||
- os: osx
|
||||
env: BADGE=linux
|
||||
|
||||
|
||||
|
||||
# Installing additional tools
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- valgrind
|
||||
- python-yaml
|
||||
- gcc-5
|
||||
- g++-5
|
||||
# - lcov
|
||||
- clang
|
||||
- valgrind
|
||||
- python-yaml
|
||||
- gcc-5
|
||||
- g++-5
|
||||
# - lcov
|
||||
- clang
|
||||
|
||||
before_install:
|
||||
# Set this to the name of the library
|
||||
- PROJECT_TO_TEST=`basename $TRAVIS_BUILD_DIR`
|
||||
- echo "Testing $PROJECT_TO_TEST"
|
||||
# Cloning Boost libraries (fast nondeep cloning)
|
||||
- BOOST=$HOME/boost-local
|
||||
- git init $BOOST
|
||||
- cd $BOOST
|
||||
- if [ $BRANCH_TO_TEST = "master" ]; then
|
||||
BOOST_BRANCH=master;
|
||||
else BOOST_BRANCH=develop; fi
|
||||
- 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
|
||||
- echo "Removing $BOOST/libs/$BOOST_REMOVE"
|
||||
- rm -rf $BOOST/libs/$BOOST_REMOVE
|
||||
- mv $TRAVIS_BUILD_DIR/../$PROJECT_TO_TEST/ $BOOST/libs/$PROJECT_TO_TEST
|
||||
- TRAVIS_BUILD_DIR=$BOOST/libs/$PROJECT_TO_TEST
|
||||
- ./bootstrap.sh
|
||||
- ./b2 headers
|
||||
- cd $BOOST/libs/$PROJECT_TO_TEST/test
|
||||
|
||||
# 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; 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
|
||||
- cd $BOOST
|
||||
- echo Branch to test $BRANCH_TO_TEST
|
||||
- if [ $BRANCH_TO_TEST = "master" ]; then
|
||||
BOOST_BRANCH=master;
|
||||
else BOOST_BRANCH=develop; fi
|
||||
- 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
|
||||
- echo "Removing $BOOST/libs/$BOOST_REMOVE"
|
||||
- rm -rf $BOOST/libs/$BOOST_REMOVE
|
||||
- mv $TRAVIS_BUILD_DIR/../$PROJECT_TO_TEST/ $BOOST/libs/$PROJECT_TO_TEST
|
||||
- TRAVIS_BUILD_DIR=$BOOST/libs/$PROJECT_TO_TEST
|
||||
- ./bootstrap.sh
|
||||
- ./b2 headers
|
||||
- cd $BOOST/libs/$PROJECT_TO_TEST/test
|
||||
- echo BOOST_TEST_CATCH_SYSTEM_ERRORS $BOOST_TEST_CATCH_SYSTEM_ERRORS
|
||||
script:
|
||||
# `--coverage` flags required to generate coverage info for Coveralls
|
||||
- ../../../b2 with-valgrind address-model=64 architecture=x86 testing.launcher=valgrind valgrind=on toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
|
||||
- ../../../b2 without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
|
||||
# `--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=. $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
|
||||
- find ../../../bin.v2/ -name "*.gcda" -exec cp "{}" $TRAVIS_BUILD_DIR/coverals/ \;
|
||||
- find ../../../bin.v2/ -name "*.gcno" -exec cp "{}" $TRAVIS_BUILD_DIR/coverals/ \;
|
||||
# Copying Coveralls data to a separate folder
|
||||
- mkdir -p $TRAVIS_BUILD_DIR/coverals
|
||||
- find ../../../bin.v2/ -name "*.gcda" -exec cp "{}" $TRAVIS_BUILD_DIR/coverals/ \;
|
||||
- find ../../../bin.v2/ -name "*.gcno" -exec cp "{}" $TRAVIS_BUILD_DIR/coverals/ \;
|
||||
|
||||
# Preparing Coveralls data by changind data format to a readable one
|
||||
- git clone https://github.com/linux-test-project/lcov.git lcov_dir
|
||||
- GCOV_VERSION=""
|
||||
- if [[ "$TOOLSET" == *"-"* ]]; then GCOV_VERSION="--gcov-tool gcov-${TOOLSET#*-}"; fi
|
||||
- LCOV="$BOOST/libs/$PROJECT_TO_TEST/test/lcov_dir/bin/lcov $GCOV_VERSION"
|
||||
- $LCOV --directory $TRAVIS_BUILD_DIR/coverals --base-directory ./ --capture --output-file $TRAVIS_BUILD_DIR/coverals/coverage.info
|
||||
# Preparing Coveralls data by changind data format to a readable one
|
||||
- git clone https://github.com/linux-test-project/lcov.git lcov_dir
|
||||
- GCOV_VERSION=""
|
||||
- if [[ "$TOOLSET" == *"-"* ]]; then GCOV_VERSION="--gcov-tool gcov-${TOOLSET#*-}"; fi
|
||||
- LCOV="$BOOST/libs/$PROJECT_TO_TEST/test/lcov_dir/bin/lcov $GCOV_VERSION"
|
||||
- $LCOV --directory $TRAVIS_BUILD_DIR/coverals --base-directory ./ --capture --output-file $TRAVIS_BUILD_DIR/coverals/coverage.info
|
||||
|
||||
# ... erasing /test/ /example/ folder data
|
||||
- cd $BOOST
|
||||
- $LCOV --remove $TRAVIS_BUILD_DIR/coverals/coverage.info "/usr*" "*/$PROJECT_TO_TEST/test/*" $IGNORE_COVERAGE "*/$PROJECT_TO_TEST/tests/*" "*/$PROJECT_TO_TEST/examples/*" "*/$PROJECT_TO_TEST/example/*" -o $TRAVIS_BUILD_DIR/coverals/coverage.info
|
||||
# ... erasing /test/ /example/ folder data
|
||||
- cd $BOOST
|
||||
- $LCOV --remove $TRAVIS_BUILD_DIR/coverals/coverage.info "*.cpp" "/usr*" "*/$PROJECT_TO_TEST/test/*" $IGNORE_COVERAGE "*/$PROJECT_TO_TEST/tests/*" "*/$PROJECT_TO_TEST/examples/*" "*/$PROJECT_TO_TEST/example/*" -o $TRAVIS_BUILD_DIR/coverals/coverage.info
|
||||
|
||||
# ... erasing data that is not related to this project directly
|
||||
- OTHER_LIBS=`grep "submodule .*" .gitmodules | sed 's/\[submodule\ "\(.*\)"\]/"\*\/boost\/\1\.hpp" "\*\/boost\/\1\/\*"/g'| sed "/\"\*\/boost\/$PROJECT_TO_TEST\/\*\"/d" | sed ':a;N;$!ba;s/\n/ /g'`
|
||||
- echo $OTHER_LIBS
|
||||
- eval "$LCOV --remove $TRAVIS_BUILD_DIR/coverals/coverage.info $OTHER_LIBS -o $TRAVIS_BUILD_DIR/coverals/coverage.info"
|
||||
# ... erasing data that is not related to this project directly
|
||||
- OTHER_LIBS=`grep "submodule .*" .gitmodules | sed 's/\[submodule\ "\(.*\)"\]/"\*\/boost\/\1\.hpp" "\*\/boost\/\1\/\*"/g'| sed "/\"\*\/boost\/process\/\*\"/d" | sed ':a;N;$!ba;s/\n/ /g'`
|
||||
- echo $OTHER_LIBS
|
||||
- eval "$LCOV --remove $TRAVIS_BUILD_DIR/coverals/coverage.info $OTHER_LIBS -o $TRAVIS_BUILD_DIR/coverals/coverage.info" > /dev/null
|
||||
|
||||
# Sending data to Coveralls
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- gem install coveralls-lcov
|
||||
- coveralls-lcov coverals/coverage.info
|
||||
|
||||
after_script:
|
||||
- curl -s https://report.ci/upload.py | python - --name="$BADGE test run"
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
# Sending data to Coveralls
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- gem install coveralls-lcov
|
||||
- coveralls-lcov coverals/coverage.info
|
||||
|
||||
@@ -4,10 +4,11 @@ Boost.process is a library for comfortable management of processes, released wit
|
||||
|
||||
### Test results
|
||||
|
||||
Branches | Build | Tests coverage |
|
||||
----------------|-------------- | -------------- |
|
||||
Develop: | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://ci.appveyor.com/project/klemens-morgenstern/boost-process) | [](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=develop) |
|
||||
Master: | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/master) | [](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=master) |
|
||||
Branches | Linux | OSX | Windows | Code coverage | Matrix |
|
||||
----------------|-------|-----|---------| ------------- |--------|
|
||||
Develop: | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=linux) | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=osx) | [](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/develop) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=windows) | [](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=develop) | [](http://www.boost.org/development/tests/develop/developer/process.html)
|
||||
Master: | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=linux) | [](https://travis-ci.org/klemens-morgenstern/boost-process) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=osx) | [](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/master) [](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=windows) | [](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=master) | [](http://www.boost.org/development/tests/master/developer/process.html)
|
||||
|
||||
|
||||
[Open Issues](https://github.com/klemens-morgenstern/boost-process/issues)
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
[def __on_success__ [memberref boost::process::extend::handler::on_success on_success]]
|
||||
[def __posix_executor__ [classref boost::process::extend::posix_executor ex::posix_executor]]
|
||||
[def __windows_executor__ [classref boost::process::extend::windows_executor ex::windows_executor]]
|
||||
[def io_service [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service.html boost::asio::io_service]]
|
||||
[def __require_io_service__ [classref boost::process::extend::require_io_service ex::require_io_service]]
|
||||
[def __io_context__ [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_context.html boost::asio::io_context]]
|
||||
[def __require_io_context__ [classref boost::process::extend::require_io_context ex::require_io_context]]
|
||||
[def __async_handler__ [classref boost::process::extend::async_handler ex::async_handler]]
|
||||
[def __get_io_service__ [funcref boost::process::extend::get_io_service ex::get_io_service]]
|
||||
[def __get_io_context__ [funcref boost::process::extend::get_io_context ex::get_io_context]]
|
||||
|
||||
[section:extend Extensions]
|
||||
To extend the library, the header [headerref boost/process/extend.hpp extend] is provided.
|
||||
@@ -50,7 +50,7 @@ So let's start with a simple hello-world example, while we use a C++14 generic l
|
||||
using namespace boost::process;
|
||||
namespace ex = bp::extend;
|
||||
|
||||
__child__ c("foo", ex::__on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;});
|
||||
__child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;});
|
||||
```
|
||||
|
||||
Considering that lambda can also capture values, data can easily be shared between handlers.
|
||||
@@ -95,24 +95,24 @@ Every handler not implemented dafaults to [classref boost::process::extend::hand
|
||||
[section:async Asynchronous Functionality]
|
||||
|
||||
Since `boost.process` provides an interface for [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio],
|
||||
this functionality is also available for extensions. If the class needs the io_service for some reason, the following code will do that.
|
||||
this functionality is also available for extensions. If the class needs the __io_context__ for some reason, the following code will do that.
|
||||
|
||||
```
|
||||
struct async_foo : __handler__, __require_io_service__
|
||||
struct async_foo : __handler__, __require_io_context__
|
||||
{
|
||||
tempalte<typename Executor>
|
||||
void on_setup(Executor & exec)
|
||||
{
|
||||
io_service & ios = __get_io_service__(exec.seq); //gives us a reference and a compiler error if not present.
|
||||
__io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present.
|
||||
//do something with ios
|
||||
}
|
||||
};
|
||||
```
|
||||
[note Inheriting [globalref boost::process::extend::require_io_service require_io_service] is necessary, so [funcref boost::process::system system] provides one.]
|
||||
[note Inheriting [globalref boost::process::extend::require_io_context require_io_context] is necessary, so [funcref boost::process::system system] provides one.]
|
||||
|
||||
Additionally the handler can provide a function that is invoked when the child process exits. This is done through __async_handler__.
|
||||
|
||||
[note [globalref boost::process::extend::async_handler async_handler] implies [globalref boost::process::extend::require_io_service require_io_service] .]
|
||||
[note [globalref boost::process::extend::async_handler async_handler] implies [globalref boost::process::extend::require_io_context require_io_context] .]
|
||||
|
||||
```
|
||||
struct async_bar : __handler, __async_handler__
|
||||
@@ -120,8 +120,8 @@ struct async_bar : __handler, __async_handler__
|
||||
template<typename Executor>
|
||||
std::function<void(int, const std::error_code&)> on_exit_handler(Executor & exec)
|
||||
{
|
||||
auto handler = this->handler;
|
||||
return [handler](int exit_code, const std::error_code & ec)
|
||||
auto handler_ = this->handler;
|
||||
return [handler_](int exit_code, const std::error_code & ec)
|
||||
{
|
||||
std::cout << "hello world, I exited with " << exit_code << std::endl;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
[def asio_async_read [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/async_read.html boost::asio::async_read]]
|
||||
[def bp::environment [classref boost::process::basic_environment bp::environment]]
|
||||
[def bp::native_environment [classref boost::process::basic_native_environment bp::native_environment]]
|
||||
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process::environment]]
|
||||
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process:deadlock :environment]]
|
||||
[def std::chrono::seconds [@http://en.cppreference.com/w/cpp/chrono/duration std::chrono::seconds]]
|
||||
[def std::vector [@http://en.cppreference.com/w/cpp/container/vector std::vector]]
|
||||
|
||||
@@ -158,7 +158,7 @@ will change the behaviour, so that instead of throwing an exception, the error w
|
||||
|
||||
```
|
||||
std::error_code ec;
|
||||
bp::system c("g++ main.cpp", ec);
|
||||
bp::system("g++ main.cpp", ec);
|
||||
```
|
||||
[endsect]
|
||||
[section:io Synchronous I/O]
|
||||
@@ -217,8 +217,7 @@ std::vector<std::string> read_outline(std::string & file)
|
||||
What this does is redirect the `stdout` of the process into a pipe and we read this
|
||||
synchronously.
|
||||
|
||||
[warning The pipe will cause a deadlock if you try to read after nm exited]
|
||||
[note You can do the same thing with [globalref boost::process::std_err std_err]]
|
||||
[note You can do the same thing with [globalref boost::process::std_err std_err]]
|
||||
|
||||
Now we get the name from `nm` and we might want to demangle it, so we use input and output.
|
||||
`nm` has a demangle option, but for the sake of the example, we'll use
|
||||
|
||||
14
filter_section_warning.py
Normal file
14
filter_section_warning.py
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
for line in sys.stdin:
|
||||
# If line is a 'noisy' warning, don't print it or the following two lines.
|
||||
if ('warning: section' in line and 'is deprecated' in line
|
||||
or 'note: change section name to' in line):
|
||||
next(sys.stdin)
|
||||
next(sys.stdin)
|
||||
else:
|
||||
sys.stdout.write(line)
|
||||
sys.stdout.flush()
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <boost/process/error.hpp>
|
||||
#include <boost/process/exe.hpp>
|
||||
#include <boost/process/group.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/shell.hpp>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <tuple>
|
||||
|
||||
@@ -64,11 +65,12 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
|
||||
errored = true;
|
||||
#endif
|
||||
auto & h = init.completion_handler;
|
||||
ios.post(
|
||||
[h, ec]() mutable
|
||||
{
|
||||
h(boost::system::error_code(ec.value(), boost::system::system_category()), -1);
|
||||
});
|
||||
boost::asio::post(
|
||||
ios.get_executor(),
|
||||
[h, ec]() mutable
|
||||
{
|
||||
h(boost::system::error_code(ec.value(), boost::system::system_category()), -1);
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int))
|
||||
|
||||
@@ -92,6 +92,10 @@ class child
|
||||
/** Get the Process Identifier. */
|
||||
pid_t id() const;
|
||||
|
||||
/** Get the native, uninterpreted exit code. The return value is without any meaning if the child wasn't waited
|
||||
* for or if it was terminated. */
|
||||
int native_exit_code() const;
|
||||
|
||||
/** Check if the child process is running. */
|
||||
bool running();
|
||||
/** \overload void running() */
|
||||
|
||||
@@ -101,6 +101,8 @@ public:
|
||||
int exit_code() const {return ::boost::process::detail::api::eval_exit_status(_exit_status->load());}
|
||||
pid_t id() const {return _child_handle.id(); }
|
||||
|
||||
int native_exit_code() const {return _exit_status->load();}
|
||||
|
||||
bool running()
|
||||
{
|
||||
std::error_code ec;
|
||||
@@ -143,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;
|
||||
@@ -157,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
|
||||
@@ -169,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,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);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ inline std::error_code get_last_error() noexcept
|
||||
#define BOOST_POSIX_HAS_VFORK 1
|
||||
#endif
|
||||
|
||||
#if (_POSIX_C_SOURCE >= 199309L)
|
||||
#define BOOST_POSIX_HAS_SIGTIMEDWAIT 1
|
||||
#endif
|
||||
|
||||
#elif defined(BOOST_WINDOWS_API)
|
||||
namespace windows {namespace extensions {}}
|
||||
namespace api = windows;
|
||||
|
||||
@@ -22,6 +22,8 @@ class basic_streambuf;
|
||||
typedef basic_streambuf<std::allocator<char>> streambuf;
|
||||
class io_context;
|
||||
|
||||
class executor;
|
||||
|
||||
|
||||
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
|
||||
class signal_set_service;
|
||||
@@ -30,7 +32,9 @@ template <typename SignalSetService>
|
||||
class basic_signal_set;
|
||||
typedef basic_signal_set<signal_set_service> signal_set;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
class signal_set;
|
||||
template <typename Executor>
|
||||
class basic_signal_set;
|
||||
typedef basic_signal_set<executor> signal_set;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
|
||||
template <typename Handler>
|
||||
@@ -45,7 +49,9 @@ template <typename StreamDesscriptorService>
|
||||
class basic_stream_descriptor;
|
||||
typedef basic_stream_descriptor<stream_descriptor_service> stream_descriptor;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
class stream_descriptor;
|
||||
template <typename Executor>
|
||||
class basic_stream_descriptor;
|
||||
typedef basic_stream_descriptor<executor> stream_descriptor;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
|
||||
} //posix
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
::boost::process::detail::posix::require_io_context
|
||||
::boost::process::detail::posix::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
@@ -33,6 +36,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
fut = promise->get_future(); return std::move(*this);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
async_in_buffer(Buffer & buf) : buf(buf)
|
||||
@@ -41,28 +45,28 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
auto pipe_ = this->pipe;
|
||||
if (this->promise)
|
||||
{
|
||||
auto promise = this->promise;
|
||||
auto promise_ = this->promise;
|
||||
|
||||
boost::asio::async_write(*pipe, buf,
|
||||
[pipe, promise](const boost::system::error_code & ec, std::size_t)
|
||||
boost::asio::async_write(*pipe_, buf,
|
||||
[pipe_, promise_](const boost::system::error_code & ec, std::size_t)
|
||||
{
|
||||
if (ec && (ec.value() != EBADF) && (ec.value() != EPERM) && (ec.value() != ENOENT))
|
||||
{
|
||||
std::error_code e(ec.value(), std::system_category());
|
||||
promise->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
promise_->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
}
|
||||
else
|
||||
promise->set_value();
|
||||
promise_->set_value();
|
||||
});
|
||||
}
|
||||
else
|
||||
boost::asio::async_write(*pipe, buf,
|
||||
[pipe](const boost::system::error_code&, std::size_t){});
|
||||
boost::asio::async_write(*pipe_, buf,
|
||||
[pipe_](const boost::system::error_code&, std::size_t){});
|
||||
|
||||
std::move(*pipe).source().close();
|
||||
std::move(*pipe_).source().close();
|
||||
|
||||
this->pipe = nullptr;
|
||||
}
|
||||
@@ -76,9 +80,19 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec)
|
||||
{
|
||||
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
|
||||
if (!pipe)
|
||||
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
|
||||
}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
if (pipe)
|
||||
return {STDIN_FILENO, pipe->native_source(), pipe->native_sink()};
|
||||
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
|
||||
return {STDIN_FILENO, STDIN_FILENO, STDIN_FILENO};
|
||||
}
|
||||
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &exec)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <array>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
@@ -45,12 +47,24 @@ inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::in
|
||||
|
||||
template<int p1, int p2, typename Buffer>
|
||||
struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
::boost::process::detail::posix::require_io_context
|
||||
::boost::process::detail::posix::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
std::array<int, 4> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
if (pipe)
|
||||
return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
|
||||
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
|
||||
return {pp1, pp2, pp1, pp2};
|
||||
}
|
||||
|
||||
|
||||
async_out_buffer(Buffer & buf) : buf(buf)
|
||||
{
|
||||
@@ -114,30 +128,30 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor &)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
auto pipe_ = this->pipe;
|
||||
|
||||
auto buffer = this->buffer;
|
||||
auto promise = this->promise;
|
||||
auto buffer_ = this->buffer;
|
||||
auto promise_ = this->promise;
|
||||
|
||||
boost::asio::async_read(*pipe, *buffer,
|
||||
[pipe, buffer, promise](const boost::system::error_code& ec, std::size_t)
|
||||
boost::asio::async_read(*pipe_, *buffer_,
|
||||
[pipe_, buffer_, promise_](const boost::system::error_code& ec, std::size_t)
|
||||
{
|
||||
if (ec && (ec.value() != ENOENT))
|
||||
{
|
||||
std::error_code e(ec.value(), std::system_category());
|
||||
promise->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
promise_->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::istream is (buffer.get());
|
||||
std::istream is (buffer_.get());
|
||||
Type arg;
|
||||
arg.resize(buffer->size());
|
||||
is.read(&*arg.begin(), buffer->size());
|
||||
promise->set_value(std::move(arg));
|
||||
arg.resize(buffer_->size());
|
||||
is.read(&*arg.begin(), buffer_->size());
|
||||
promise_->set_value(std::move(arg));
|
||||
}
|
||||
});
|
||||
|
||||
std::move(*pipe).sink().close();
|
||||
std::move(*pipe_).sink().close();
|
||||
this->pipe = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <boost/process/detail/posix/basic_pipe.hpp>
|
||||
#include <boost/asio/posix/stream_descriptor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <system_error>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -109,9 +110,9 @@ public:
|
||||
void async_close()
|
||||
{
|
||||
if (_sink.is_open())
|
||||
_sink.get_io_context(). post([this]{_sink.close();});
|
||||
boost::asio::post(_sink.get_executor(), [this]{_sink.close();});
|
||||
if (_source.is_open())
|
||||
_source.get_io_context().post([this]{_source.close();});
|
||||
boost::asio::post(_source.get_executor(), [this]{_source.close();});
|
||||
}
|
||||
|
||||
template<typename MutableBufferSequence>
|
||||
@@ -218,8 +219,8 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
}
|
||||
|
||||
async_pipe::async_pipe(const async_pipe & p) :
|
||||
_source(const_cast<async_pipe&>(p)._source.get_io_context()),
|
||||
_sink( const_cast<async_pipe&>(p)._sink.get_io_context())
|
||||
_source(const_cast<async_pipe&>(p)._source.get_executor()),
|
||||
_sink( const_cast<async_pipe&>(p)._sink.get_executor())
|
||||
{
|
||||
|
||||
//cannot get the handle from a const object.
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
return read_len;
|
||||
}
|
||||
|
||||
bool is_open()
|
||||
bool is_open() const
|
||||
{
|
||||
return (_source != -1) ||
|
||||
(_sink != -1);
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct close_in : handler_base_ext
|
||||
struct close_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
template <class Executor>
|
||||
void on_exec_setup(Executor &e) const
|
||||
@@ -23,6 +24,9 @@ struct close_in : handler_base_ext
|
||||
if (::close(STDIN_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
}
|
||||
|
||||
int get_used_handles() {return STDIN_FILENO;}
|
||||
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
|
||||
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
@@ -20,6 +21,8 @@ struct close_out : handler_base_ext
|
||||
{
|
||||
template <class Executor>
|
||||
inline void on_exec_setup(Executor &e) const;
|
||||
|
||||
std::array<int, 2> get_used_handles() {return {p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1};}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -85,7 +50,7 @@ struct on_error_t
|
||||
template<typename T>
|
||||
void operator()(T & t) const
|
||||
{
|
||||
t.on_error(exec, error);
|
||||
t.on_error(exec, error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -157,13 +122,13 @@ struct on_fork_success_t
|
||||
};
|
||||
|
||||
template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
|
||||
template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
|
||||
template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
|
||||
{
|
||||
return on_error_t<Executor> (exec, ec);
|
||||
}
|
||||
template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
|
||||
|
||||
template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
|
||||
template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
|
||||
{
|
||||
return on_fork_error_t<Executor> (exec, ec);
|
||||
}
|
||||
@@ -293,13 +258,43 @@ class executor
|
||||
auto err = errno;
|
||||
if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return.
|
||||
return;
|
||||
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
|
||||
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
|
||||
else if ((err != EAGAIN ) && (err != EINTR))
|
||||
set_error(std::error_code(err, std::system_category()), "Error read pipe");
|
||||
}
|
||||
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;
|
||||
@@ -338,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)
|
||||
@@ -349,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);
|
||||
@@ -368,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)
|
||||
{
|
||||
set_error(::boost::process::detail::get_last_error(), "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(_pipe_sink);
|
||||
::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]);
|
||||
::close(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)
|
||||
{
|
||||
@@ -485,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)
|
||||
@@ -500,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";
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <unistd.h>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
struct close_fd_ : handler_base_ext
|
||||
struct close_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
close_fd_(int fd) : fd_(fd) {}
|
||||
|
||||
@@ -27,12 +29,15 @@ struct close_fd_ : handler_base_ext
|
||||
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
}
|
||||
|
||||
int get_used_handles() {return fd_;}
|
||||
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
template <class Range>
|
||||
struct close_fds_ : handler_base_ext
|
||||
struct close_fds_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
public:
|
||||
close_fds_(const Range &fds) : fds_(fds) {}
|
||||
@@ -48,6 +53,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Range& get_used_handles() {return fds_;}
|
||||
|
||||
private:
|
||||
Range fds_;
|
||||
};
|
||||
@@ -55,7 +62,7 @@ private:
|
||||
|
||||
|
||||
template <class FileDescriptor>
|
||||
struct bind_fd_ : handler_base_ext
|
||||
struct bind_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
public:
|
||||
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
|
||||
@@ -67,6 +74,9 @@ public:
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
}
|
||||
|
||||
std::array<int, 2> get_used_handles() {return {id_, fd_};}
|
||||
|
||||
|
||||
private:
|
||||
int id_;
|
||||
FileDescriptor fd_;
|
||||
|
||||
@@ -13,16 +13,22 @@
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct file_in : handler_base_ext
|
||||
struct file_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
int handle = file.handle();
|
||||
|
||||
std::array<int, 2> get_used_handles()
|
||||
{
|
||||
return {STDIN_FILENO, handle};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
file_in(T&& t) : file(std::forward<T>(t)) {}
|
||||
file_in(FILE * f) : handle(fileno(f)) {}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct file_out : handler_base_ext
|
||||
struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
int handle = file.handle();
|
||||
@@ -27,6 +28,13 @@ struct file_out : handler_base_ext
|
||||
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write), handle(file.handle()) {}
|
||||
file_out(FILE * f) : handle(fileno(f)) {}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
return {handle, pp1, pp2};
|
||||
}
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &e) const;
|
||||
|
||||
146
include/boost/process/detail/posix/handles.hpp
Normal file
146
include/boost/process/detail/posix/handles.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <algorithm>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
using native_handle_type = int;
|
||||
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
|
||||
{
|
||||
std::vector<native_handle_type> res;
|
||||
|
||||
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
|
||||
if (!dir)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return {};
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
auto my_fd = ::dirfd(dir.get());
|
||||
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
while ((ent_p = readdir(dir.get())) != nullptr)
|
||||
{
|
||||
if (ent_p->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
const auto conv = std::atoi(ent_p->d_name);
|
||||
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
|
||||
continue;
|
||||
|
||||
if (conv == my_fd)
|
||||
continue;
|
||||
|
||||
res.push_back(conv);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
auto res = get_handles(ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
|
||||
{
|
||||
struct ::stat stat_;
|
||||
|
||||
if (::fstat(handle, &stat_) != 0)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
|
||||
|| S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
|
||||
|| S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
|
||||
|| S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
|
||||
|| S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
std::error_code ec;
|
||||
auto res = is_stream_handle(handle, ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "fstat() failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct limit_handles_ : handler_base_ext
|
||||
{
|
||||
limit_handles_() {}
|
||||
~limit_handles_() {}
|
||||
mutable std::vector<int> used_handles;
|
||||
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec) const
|
||||
{
|
||||
used_handles = get_used_handles(exec);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_exec_setup(Executor & exec) const
|
||||
{
|
||||
auto dir = ::opendir("/dev/fd");
|
||||
if (!dir)
|
||||
{
|
||||
exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")");
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_fd = ::dirfd(dir);
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
while ((ent_p = readdir(dir)) != nullptr)
|
||||
{
|
||||
if (ent_p->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
const auto conv = std::atoi(ent_p->d_name);
|
||||
|
||||
if ((conv == my_fd) || (conv == -1))
|
||||
continue;
|
||||
|
||||
if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
|
||||
continue;
|
||||
|
||||
if (::close(conv) != 0)
|
||||
{
|
||||
exec.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
::closedir(dir);
|
||||
}
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
#endif //PROCESS_HANDLES_HPP
|
||||
@@ -64,7 +64,7 @@ struct async_handler_collector
|
||||
void operator()(T & t) const
|
||||
{
|
||||
handlers.push_back(t.on_exit_handler(exec));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
//Also set's up waiting for the exit, so it can close async stuff.
|
||||
@@ -79,6 +79,7 @@ struct io_context_ref : handler_base_ext
|
||||
template <class Executor>
|
||||
void on_success(Executor& exec)
|
||||
{
|
||||
ios.notify_fork(boost::asio::io_context::fork_parent);
|
||||
//must be on the heap so I can move it into the lambda.
|
||||
auto asyncs = boost::fusion::filter_if<
|
||||
is_async_handler<
|
||||
@@ -105,6 +106,15 @@ struct io_context_ref : handler_base_ext
|
||||
sigchld_service.async_wait(exec.pid, std::move(wh));
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_setup (Executor &) const {/*ios.notify_fork(boost::asio::io_context::fork_prepare);*/}
|
||||
|
||||
template<typename Executor>
|
||||
void on_exec_setup (Executor &) const {/*ios.notify_fork(boost::asio::io_context::fork_child);*/}
|
||||
|
||||
template <class Executor>
|
||||
void on_error(Executor&, const std::error_code &) const {/*ios.notify_fork(boost::asio::io_context::fork_parent);*/}
|
||||
|
||||
private:
|
||||
boost::asio::io_context &ios;
|
||||
boost::process::detail::posix::sigchld_service &sigchld_service = boost::asio::use_service<boost::process::detail::posix::sigchld_service>(ios);
|
||||
|
||||
@@ -16,8 +16,11 @@ namespace boost { namespace process { namespace detail { namespace posix {
|
||||
// Use the "stopped" state (WIFSTOPPED) to indicate "not terminated".
|
||||
// This bit arrangement of status codes is not guaranteed by POSIX, but (according to comments in
|
||||
// the glibc <bits/waitstatus.h> header) is the same across systems in practice.
|
||||
constexpr int still_active = 0x7F;
|
||||
static_assert(!WIFEXITED(still_active) && !WIFSIGNALED(still_active), "Internal Error");
|
||||
constexpr int still_active = 0x017f;
|
||||
static_assert(WIFSTOPPED(still_active), "Expected still_active to indicate WIFSTOPPED");
|
||||
static_assert(!WIFEXITED(still_active), "Expected still_active to not indicate WIFEXITED");
|
||||
static_assert(!WIFSIGNALED(still_active), "Expected still_active to not indicate WIFSIGNALED");
|
||||
static_assert(!WIFCONTINUED(still_active), "Expected still_active to not indicate WIFCONTINUED");
|
||||
|
||||
inline bool is_running(int code)
|
||||
{
|
||||
|
||||
@@ -14,13 +14,21 @@
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
#include <unistd.h>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct null_in : handler_base_ext
|
||||
struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor source{"/dev/null", file_descriptor::read};
|
||||
|
||||
std::array<int, 2> get_used_handles()
|
||||
{
|
||||
return {STDIN_FILENO, source.handle()};
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
template <class Executor>
|
||||
void on_exec_setup(Executor &e) const
|
||||
|
||||
@@ -13,17 +13,27 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct null_out : handler_base_ext
|
||||
struct null_out : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor sink{"/dev/null", file_descriptor::write};
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &e) const;
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
return {sink.handle(), pp1, pp2};
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -27,7 +27,7 @@ struct on_exit_ : boost::process::detail::posix::async_handler
|
||||
{
|
||||
return handler;
|
||||
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -13,17 +13,23 @@
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct pipe_in : handler_base_ext
|
||||
struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
int source;
|
||||
int sink; //opposite end
|
||||
|
||||
pipe_in(int sink, int source) : source(source), sink(sink) {}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
return {STDIN_FILENO, source, sink};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
pipe_in(T & p) : source(p.native_source()), sink(p.native_sink())
|
||||
@@ -48,7 +54,9 @@ struct pipe_in : handler_base_ext
|
||||
{
|
||||
if (::dup2(source, STDIN_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
::close(source);
|
||||
if (source != STDIN_FILENO)
|
||||
::close(source);
|
||||
|
||||
::close(sink);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@@ -36,22 +38,34 @@ public:
|
||||
{
|
||||
boost::asio::async_completion<
|
||||
SignalHandler, void(boost::system::error_code)> init{handler};
|
||||
|
||||
auto & h = init.completion_handler;
|
||||
_strand.post(
|
||||
boost::asio::dispatch(
|
||||
_strand,
|
||||
[this, pid, h]
|
||||
{
|
||||
if (_receivers.empty())
|
||||
_signal_set.async_wait(
|
||||
[this](const boost::system::error_code & ec, int)
|
||||
{
|
||||
_strand.post([this,ec]{this->_handle_signal(ec);});
|
||||
});
|
||||
_receivers.emplace_back(pid, h);
|
||||
//check if the child actually is running first
|
||||
int status;
|
||||
auto pid_res = ::waitpid(pid, &status, WNOHANG);
|
||||
if (pid_res < 0)
|
||||
h(-1, get_last_error());
|
||||
else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
h(status, {}); //successfully exited already
|
||||
else //still running
|
||||
{
|
||||
if (_receivers.empty())
|
||||
_signal_set.async_wait(
|
||||
[this](const boost::system::error_code &ec, int)
|
||||
{
|
||||
boost::asio::dispatch(_strand, [this, ec]{this->_handle_signal(ec);});
|
||||
});
|
||||
_receivers.emplace_back(pid, h);
|
||||
}
|
||||
});
|
||||
|
||||
return init.result.get();
|
||||
}
|
||||
void shutdown_service() override
|
||||
void shutdown() override
|
||||
{
|
||||
_receivers.clear();
|
||||
}
|
||||
@@ -104,7 +118,7 @@ void sigchld_service::_handle_signal(const boost::system::error_code & ec)
|
||||
_signal_set.async_wait(
|
||||
[this](const boost::system::error_code & ec, int)
|
||||
{
|
||||
_strand.post([this, ec]{this->_handle_signal(ec);});
|
||||
boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -28,7 +28,8 @@ inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) no
|
||||
{
|
||||
ret = ::waitpid(p.pid, &status, 0);
|
||||
}
|
||||
while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
|
||||
while (((ret == -1) && (errno == EINTR)) ||
|
||||
(ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
|
||||
|
||||
if (ret == -1)
|
||||
ec = boost::process::detail::get_last_error();
|
||||
@@ -53,14 +54,68 @@ inline bool wait_until(
|
||||
const std::chrono::time_point<Clock, Duration>& time_out,
|
||||
std::error_code & ec) noexcept
|
||||
{
|
||||
pid_t ret;
|
||||
int status;
|
||||
::sigset_t sigset;
|
||||
|
||||
//I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
|
||||
|
||||
using _signal_t = void(*)(int);
|
||||
static thread_local _signal_t sigchld_handler = SIG_DFL;
|
||||
|
||||
struct signal_interceptor_t
|
||||
{
|
||||
static void handler_func(int val)
|
||||
{
|
||||
if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
|
||||
sigchld_handler(val);
|
||||
}
|
||||
signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
|
||||
~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
|
||||
|
||||
} signal_interceptor{};
|
||||
|
||||
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)
|
||||
{
|
||||
::timespec ts;
|
||||
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
|
||||
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
|
||||
return ts;
|
||||
};
|
||||
|
||||
int ret;
|
||||
int status{0};
|
||||
|
||||
struct ::sigaction old_sig;
|
||||
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool timed_out;
|
||||
|
||||
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
|
||||
do
|
||||
{
|
||||
auto ts = get_timespec(time_out - Clock::now());
|
||||
auto ret_sig = ::sigtimedwait(&sigset, nullptr, &ts);
|
||||
errno = 0;
|
||||
ret = ::waitpid(p.pid, &status, WNOHANG);
|
||||
|
||||
if ((ret_sig == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
|
||||
old_sig.sa_handler(ret);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
timed_out = Clock::now() >= time_out;
|
||||
@@ -71,6 +126,70 @@ inline bool wait_until(
|
||||
while ((ret == 0) ||
|
||||
(((ret == -1) && errno == EINTR) ||
|
||||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
|
||||
#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());
|
||||
::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);
|
||||
}
|
||||
|
||||
struct child_cleaner_t
|
||||
{
|
||||
pid_t pid;
|
||||
~child_cleaner_t()
|
||||
{
|
||||
int res;
|
||||
::kill(pid, SIGKILL);
|
||||
::waitpid(pid, &res, WNOHANG);
|
||||
}
|
||||
};
|
||||
child_cleaner_t child_cleaner{timeout_pid};
|
||||
|
||||
do
|
||||
{
|
||||
int sig_{0};
|
||||
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
|
||||
&& (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
|
||||
return false;
|
||||
|
||||
ret = ::sigwait(&sigset, &sig_);
|
||||
errno = 0;
|
||||
|
||||
if ((sig_ == SIGCHLD) &&
|
||||
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
|
||||
old_sig.sa_handler(ret);
|
||||
|
||||
ret = ::waitpid(p.pid, &status, WNOHANG);
|
||||
if (ret == 0) // == > is running
|
||||
{
|
||||
timed_out = Clock::now() >= time_out;
|
||||
if (timed_out)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while ((ret == 0) ||
|
||||
(((ret == -1) && errno == EINTR) ||
|
||||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
ec = boost::process::detail::get_last_error();
|
||||
|
||||
@@ -16,21 +16,30 @@
|
||||
#include <system_error>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
inline void wait(const group_handle &p, std::error_code &ec) noexcept
|
||||
{
|
||||
pid_t ret;
|
||||
int status;
|
||||
siginfo_t status;
|
||||
|
||||
do
|
||||
{
|
||||
ret = ::waitpid(-p.grp, &status, 0);
|
||||
}
|
||||
while (((ret == -1) && (errno == EINTR)) || (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
|
||||
ret = ::waitpid(-p.grp, &status.si_status, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
ec = get_last_error();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
//ECHILD --> no child processes left.
|
||||
ret = ::waitid(P_PGID, p.grp, &status, WEXITED | WNOHANG);
|
||||
}
|
||||
while ((ret != -1) || (errno != ECHILD));
|
||||
|
||||
if (errno != ECHILD)
|
||||
ec = boost::process::detail::get_last_error();
|
||||
else
|
||||
ec.clear();
|
||||
@@ -49,31 +58,105 @@ inline bool wait_until(
|
||||
const std::chrono::time_point<Clock, Duration>& time_out,
|
||||
std::error_code & ec) noexcept
|
||||
{
|
||||
pid_t ret;
|
||||
int status;
|
||||
|
||||
bool timed_out;
|
||||
::siginfo_t siginfo;
|
||||
|
||||
bool timed_out = false;
|
||||
int ret;
|
||||
|
||||
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
|
||||
auto get_timespec =
|
||||
+[](const Duration & dur)
|
||||
{
|
||||
::timespec ts;
|
||||
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
|
||||
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
|
||||
return ts;
|
||||
};
|
||||
|
||||
::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))
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ret = ::waitpid(-p.grp, &status, WNOHANG);
|
||||
if (ret == 0)
|
||||
auto ts = get_timespec(time_out - Clock::now());
|
||||
ret = ::sigtimedwait(&sigset, nullptr, &ts);
|
||||
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)
|
||||
{
|
||||
timed_out = Clock::now() >= time_out;
|
||||
if (timed_out)
|
||||
return false;
|
||||
if ((errno == ECHILD) || (errno == ESRCH))
|
||||
{
|
||||
ec.clear();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//check if we're done ->
|
||||
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
|
||||
}
|
||||
while ((ret == 0) ||
|
||||
(((ret == -1) && errno == EINTR) ||
|
||||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
|
||||
while (((ret != -1) || ((errno != ECHILD) && (errno != ESRCH))) && !(timed_out = (Clock::now() > time_out)));
|
||||
|
||||
if (ret == -1)
|
||||
if (errno != ECHILD)
|
||||
{
|
||||
ec = boost::process::detail::get_last_error();
|
||||
return !timed_out;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
return true; //even if timed out, there are no child proccessess left
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
::timespec sleep_interval;
|
||||
sleep_interval.tv_sec = 0;
|
||||
sleep_interval.tv_nsec = 1000000;
|
||||
|
||||
|
||||
while (!(timed_out = (Clock::now() > time_out)))
|
||||
{
|
||||
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
|
||||
if (ret == -1)
|
||||
{
|
||||
if ((errno == ECHILD) || (errno == ESRCH))
|
||||
{
|
||||
ec.clear();
|
||||
return true;
|
||||
}
|
||||
ec = boost::process::detail::get_last_error();
|
||||
return false;
|
||||
}
|
||||
//we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
|
||||
::nanosleep(&sleep_interval, nullptr);
|
||||
}
|
||||
return !timed_out;
|
||||
#endif
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
|
||||
81
include/boost/process/detail/used_handles.hpp
Normal file
81
include/boost/process/detail/used_handles.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2016 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
#include <boost/fusion/include/filter_if.hpp>
|
||||
#include <boost/fusion/include/for_each.hpp>
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/detail/posix/handles.hpp>
|
||||
#include <boost/process/detail/posix/asio_fwd.hpp>
|
||||
#else
|
||||
#include <boost/process/detail/windows/handles.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
struct uses_handles
|
||||
{
|
||||
//If you get an error here, you must add a `get_handles` function that returns a range or a single handle value
|
||||
void get_used_handles() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle: std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle<T&> : std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle<const T&> : std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename Char, typename Sequence>
|
||||
class executor;
|
||||
|
||||
template<typename Func>
|
||||
struct foreach_handle_invocator
|
||||
{
|
||||
Func & func;
|
||||
foreach_handle_invocator(Func & func) : func(func) {}
|
||||
|
||||
|
||||
template<typename Range>
|
||||
void invoke(const Range & range) const
|
||||
{
|
||||
for (auto handle_ : range)
|
||||
func(handle_);
|
||||
|
||||
}
|
||||
void invoke(::boost::process::detail::api::native_handle_type handle) const {func(handle);};
|
||||
|
||||
template<typename T>
|
||||
void operator()(T & val) const {invoke(val.get_used_handles());}
|
||||
};
|
||||
|
||||
template<typename Executor, typename Function>
|
||||
void foreach_used_handle(Executor &exec, Function &&func)
|
||||
{
|
||||
boost::fusion::for_each(boost::fusion::filter_if<does_use_handle<boost::mpl::_>>(exec.seq),
|
||||
foreach_handle_invocator<Function>(func));
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
std::vector<::boost::process::detail::api::native_handle_type>
|
||||
get_used_handles(Executor &exec)
|
||||
{
|
||||
std::vector<::boost::process::detail::api::native_handle_type> res;
|
||||
foreach_used_handle(exec, [&](::boost::process::detail::api::native_handle_type handle){res.push_back(handle);});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_ */
|
||||
@@ -21,6 +21,9 @@ class basic_streambuf;
|
||||
typedef basic_streambuf<std::allocator<char>> streambuf;
|
||||
class io_context;
|
||||
|
||||
class executor;
|
||||
|
||||
|
||||
template <typename Handler>
|
||||
class basic_yield_context;
|
||||
|
||||
@@ -34,7 +37,10 @@ class basic_stream_handle;
|
||||
|
||||
typedef basic_stream_handle<stream_handle_service> stream_handle;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
class stream_handle;
|
||||
template <typename Executor>
|
||||
class basic_stream_handle;
|
||||
typedef basic_stream_handle<executor> stream_handle;
|
||||
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
|
||||
|
||||
@@ -46,7 +52,9 @@ class basic_object_handle;
|
||||
|
||||
typedef basic_object_handle<object_handle_service> object_handle;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
class object_handle;
|
||||
template <typename Executor>
|
||||
class basic_object_handle;
|
||||
typedef basic_object_handle<executor> object_handle;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
|
||||
} //windows
|
||||
|
||||
@@ -17,19 +17,20 @@
|
||||
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/async_handler.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
::boost::process::detail::windows::require_io_context
|
||||
::boost::process::detail::windows::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
@@ -42,34 +43,39 @@ struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const
|
||||
{
|
||||
return std::move(*pipe).source().native_handle();
|
||||
}
|
||||
|
||||
async_in_buffer(Buffer & buf) : buf(buf)
|
||||
{
|
||||
}
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor&)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
auto pipe_ = this->pipe;
|
||||
|
||||
if (this->promise)
|
||||
{
|
||||
auto promise = this->promise;
|
||||
auto promise_ = this->promise;
|
||||
|
||||
boost::asio::async_write(*pipe, buf,
|
||||
[promise](const boost::system::error_code & ec, std::size_t)
|
||||
boost::asio::async_write(*pipe_, buf,
|
||||
[promise_](const boost::system::error_code & ec, std::size_t)
|
||||
{
|
||||
if (ec && (ec.value() != ::boost::winapi::ERROR_BROKEN_PIPE_))
|
||||
{
|
||||
std::error_code e(ec.value(), std::system_category());
|
||||
promise->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
promise_->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
}
|
||||
promise->set_value();
|
||||
promise_->set_value();
|
||||
});
|
||||
}
|
||||
else
|
||||
boost::asio::async_write(*pipe, buf,
|
||||
[pipe](const boost::system::error_code&, std::size_t){});
|
||||
boost::asio::async_write(*pipe_, buf,
|
||||
[pipe_](const boost::system::error_code&, std::size_t){});
|
||||
|
||||
std::move(*pipe).source().close();
|
||||
std::move(*pipe_).source().close();
|
||||
|
||||
|
||||
this->pipe = nullptr;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
|
||||
#include <istream>
|
||||
@@ -80,10 +81,10 @@ struct async_out_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor&)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
boost::asio::async_read(*pipe, buf,
|
||||
[pipe](const boost::system::error_code&, std::size_t){});
|
||||
std::move(*pipe).sink().close();
|
||||
auto pipe_ = this->pipe;
|
||||
boost::asio::async_read(*pipe_, buf,
|
||||
[pipe_](const boost::system::error_code&, std::size_t){});
|
||||
std::move(*pipe_).sink().close();
|
||||
this->pipe = nullptr;
|
||||
|
||||
}
|
||||
@@ -108,12 +109,18 @@ struct async_out_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
|
||||
template<int p1, int p2, typename Type>
|
||||
struct async_out_future : ::boost::process::detail::windows::handler_base_ext,
|
||||
::boost::process::detail::windows::require_io_context
|
||||
::boost::process::detail::windows::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
|
||||
std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const
|
||||
{
|
||||
return std::move(*pipe).sink().native_handle();
|
||||
}
|
||||
|
||||
|
||||
async_out_future(std::future<Type> & fut)
|
||||
{
|
||||
@@ -122,34 +129,34 @@ struct async_out_future : ::boost::process::detail::windows::handler_base_ext,
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor&)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
auto buffer = this->buffer;
|
||||
auto promise = this->promise;
|
||||
std::move(*pipe).sink().close();
|
||||
boost::asio::async_read(*pipe, *buffer,
|
||||
[pipe, buffer, promise](const boost::system::error_code& ec, std::size_t)
|
||||
auto pipe_ = this->pipe;
|
||||
auto buffer_ = this->buffer;
|
||||
auto promise_ = this->promise;
|
||||
std::move(*pipe_).sink().close();
|
||||
boost::asio::async_read(*pipe_, *buffer_,
|
||||
[pipe_, buffer_, promise_](const boost::system::error_code& ec, std::size_t)
|
||||
{
|
||||
if (ec && (ec.value() != ::boost::winapi::ERROR_BROKEN_PIPE_))
|
||||
{
|
||||
std::error_code e(ec.value(), std::system_category());
|
||||
promise->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
promise_->set_exception(std::make_exception_ptr(process_error(e)));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::istream is (buffer.get());
|
||||
std::istream is (buffer_.get());
|
||||
Type arg;
|
||||
if (buffer->size() > 0)
|
||||
if (buffer_->size() > 0)
|
||||
{
|
||||
arg.resize(buffer->size());
|
||||
is.read(&*arg.begin(), buffer->size());
|
||||
arg.resize(buffer_->size());
|
||||
is.read(&*arg.begin(), buffer_->size());
|
||||
}
|
||||
|
||||
promise->set_value(std::move(arg));
|
||||
promise_->set_value(std::move(arg));
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
this->pipe = nullptr;
|
||||
this->pipe = nullptr;
|
||||
this->buffer = nullptr;
|
||||
this->promise = nullptr;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <boost/winapi/access_rights.hpp>
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/process/detail/windows/basic_pipe.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/windows/stream_handle.hpp>
|
||||
#include <atomic>
|
||||
#include <system_error>
|
||||
@@ -39,23 +40,30 @@ class async_pipe
|
||||
{
|
||||
::boost::asio::windows::stream_handle _source;
|
||||
::boost::asio::windows::stream_handle _sink ;
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name, bool private_);
|
||||
|
||||
public:
|
||||
typedef ::boost::winapi::HANDLE_ native_handle_type;
|
||||
typedef ::boost::asio::windows::stream_handle handle_type;
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios,
|
||||
const std::string & name = make_pipe_name())
|
||||
: async_pipe(ios, ios, name) {}
|
||||
async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios, make_pipe_name(), true) {}
|
||||
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink)
|
||||
: async_pipe(ios_source, ios_sink, make_pipe_name(), true) {}
|
||||
|
||||
async_pipe(boost::asio::io_context & ios, const std::string & name)
|
||||
: async_pipe(ios, ios, name, false) {}
|
||||
|
||||
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name)
|
||||
: async_pipe(ios_source, ios_sink, name, false) {}
|
||||
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name = make_pipe_name());
|
||||
|
||||
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,
|
||||
@@ -99,12 +107,12 @@ public:
|
||||
if (_sink.is_open())
|
||||
{
|
||||
_sink.close();
|
||||
_sink = handle_type(_sink.get_io_context());
|
||||
_sink = handle_type(_sink.get_executor());
|
||||
}
|
||||
if (_source.is_open())
|
||||
{
|
||||
_source.close();
|
||||
_source = handle_type(_source.get_io_context());
|
||||
_source = handle_type(_source.get_executor());
|
||||
}
|
||||
}
|
||||
void close(boost::system::error_code & ec)
|
||||
@@ -112,12 +120,12 @@ public:
|
||||
if (_sink.is_open())
|
||||
{
|
||||
_sink.close(ec);
|
||||
_sink = handle_type(_sink.get_io_context());
|
||||
_sink = handle_type(_sink.get_executor());
|
||||
}
|
||||
if (_source.is_open())
|
||||
{
|
||||
_source.close(ec);
|
||||
_source = handle_type(_source.get_io_context());
|
||||
_source = handle_type(_source.get_executor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +136,9 @@ public:
|
||||
void async_close()
|
||||
{
|
||||
if (_sink.is_open())
|
||||
_sink.get_io_context(). post([this]{_sink.close();});
|
||||
boost::asio::post(_sink.get_executor(), [this]{_sink.close();});
|
||||
if (_source.is_open())
|
||||
_source.get_io_context().post([this]{_source.close();});
|
||||
boost::asio::post(_source.get_executor(), [this]{_source.close();});
|
||||
}
|
||||
|
||||
template<typename MutableBufferSequence>
|
||||
@@ -189,14 +197,16 @@ public:
|
||||
|
||||
handle_type source(::boost::asio::io_context& ios) &&
|
||||
{
|
||||
::boost::asio::windows::stream_handle stolen(ios, _source.native_handle());
|
||||
_source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _source.native_handle());
|
||||
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, _sink.native_handle());
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _sink.native_handle());
|
||||
boost::system::error_code ec;
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
|
||||
return stolen;
|
||||
}
|
||||
|
||||
@@ -214,7 +224,7 @@ public:
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
return ::boost::asio::windows::stream_handle(ios, source);
|
||||
return ::boost::asio::windows::stream_handle(ios.get_executor(), source);
|
||||
}
|
||||
handle_type sink (::boost::asio::io_context& ios) const &
|
||||
{
|
||||
@@ -230,16 +240,15 @@ public:
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
return ::boost::asio::windows::stream_handle(ios, sink);
|
||||
return ::boost::asio::windows::stream_handle(ios.get_executor(), sink);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
async_pipe::async_pipe(const async_pipe& p) :
|
||||
_source(const_cast<handle_type&>(p._source).get_io_context()),
|
||||
_sink (const_cast<handle_type&>(p._sink).get_io_context())
|
||||
_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;
|
||||
@@ -265,14 +274,16 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name) : _source(ios_source), _sink(ios_sink)
|
||||
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
|
||||
{
|
||||
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
|
||||
|
||||
@@ -284,7 +295,7 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
#endif
|
||||
::boost::winapi::PIPE_ACCESS_INBOUND_
|
||||
| FILE_FLAG_OVERLAPPED_, //write flag
|
||||
0, 1, 8192, 8192, 0, nullptr);
|
||||
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
|
||||
|
||||
|
||||
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
@@ -309,6 +320,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();
|
||||
@@ -320,6 +369,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(
|
||||
@@ -337,24 +388,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_io_context(), source);
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_io_context(), 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
return static_cast<int_type>(read_len);
|
||||
}
|
||||
|
||||
bool is_open()
|
||||
bool is_open() const
|
||||
{
|
||||
return (_source != ::boost::winapi::INVALID_HANDLE_VALUE_) ||
|
||||
(_sink != ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
@@ -143,11 +143,16 @@ basic_pipe<Char, Traits>::basic_pipe(const std::string & name)
|
||||
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
|
||||
//static constexpr int FILE_ATTRIBUTE_NORMAL_ = 0x00000080; //temporary
|
||||
|
||||
#if BOOST_NO_ANSI_APIS
|
||||
std::wstring name_ = boost::process::detail::convert(name);
|
||||
#else
|
||||
auto &name_ = name;
|
||||
#endif
|
||||
::boost::winapi::HANDLE_ source = ::boost::winapi::create_named_pipe(
|
||||
name.c_str(),
|
||||
name_.c_str(),
|
||||
::boost::winapi::PIPE_ACCESS_INBOUND_
|
||||
| FILE_FLAG_OVERLAPPED_, //write flag
|
||||
0, 1, 8192, 8192, 0, nullptr);
|
||||
0, ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
|
||||
|
||||
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::process::detail::throw_last_error("create_named_pipe() failed");
|
||||
|
||||
@@ -84,7 +84,7 @@ struct startup_info_impl
|
||||
void set_startup_info_ex()
|
||||
{
|
||||
startup_info.cb = sizeof(startup_info_ex_t);
|
||||
creation_flags = ::boost::winapi::EXTENDED_STARTUPINFO_PRESENT_;
|
||||
creation_flags |= ::boost::winapi::EXTENDED_STARTUPINFO_PRESENT_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,16 +13,20 @@
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
#include <io.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct file_in : public ::boost::process::detail::handler_base
|
||||
struct file_in : public ::boost::process::detail::handler_base,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
::boost::winapi::HANDLE_ handle = file.handle();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
template<typename T>
|
||||
file_in(T&& t) : file(std::forward<T>(t), file_descriptor::read) {}
|
||||
file_in(FILE * f) : handle(reinterpret_cast<::boost::winapi::HANDLE_>(_get_osfhandle(_fileno(f)))) {}
|
||||
|
||||
@@ -14,16 +14,21 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct file_out : public ::boost::process::detail::handler_base
|
||||
struct file_out : public ::boost::process::detail::handler_base,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
::boost::winapi::HANDLE_ handle = file.handle();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
|
||||
template<typename T>
|
||||
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write) {}
|
||||
file_out(FILE * f) : handle(reinterpret_cast<void*>(_get_osfhandle(_fileno(f)))) {}
|
||||
|
||||
@@ -84,22 +84,37 @@ inline void enable_break_away(::boost::winapi::HANDLE_ h, std::error_code & ec)
|
||||
ec = get_last_error();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
inline void associate_completion_port(::boost::winapi::HANDLE_ job,
|
||||
::boost::winapi::HANDLE_ io_port)
|
||||
{
|
||||
workaround::JOBOBJECT_ASSOCIATE_COMPLETION_PORT_ port;
|
||||
port.CompletionKey = job;
|
||||
port.CompletionPort = io_port;
|
||||
|
||||
if (!workaround::set_information_job_object(
|
||||
job,
|
||||
workaround::JobObjectAssociateCompletionPortInformation_,
|
||||
static_cast<void*>(&port),
|
||||
sizeof(port)))
|
||||
throw_last_error("SetInformationJobObject() failed");
|
||||
}
|
||||
|
||||
struct group_handle
|
||||
{
|
||||
::boost::winapi::HANDLE_ _job_object;
|
||||
::boost::winapi::HANDLE_ _io_port;
|
||||
|
||||
typedef ::boost::winapi::HANDLE_ handle_t;
|
||||
handle_t handle() const { return _job_object; }
|
||||
|
||||
explicit group_handle(handle_t h) :
|
||||
_job_object(h)
|
||||
_job_object(h),
|
||||
_io_port(::CreateIoCompletionPort(::boost::winapi::INVALID_HANDLE_VALUE_, nullptr, 0, 1))
|
||||
{
|
||||
enable_break_away(_job_object);
|
||||
associate_completion_port(_job_object, _io_port);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,15 +125,21 @@ struct group_handle
|
||||
~group_handle()
|
||||
{
|
||||
::boost::winapi::CloseHandle(_job_object);
|
||||
::boost::winapi::CloseHandle(_io_port);
|
||||
}
|
||||
group_handle(const group_handle & c) = delete;
|
||||
group_handle(group_handle && c) : _job_object(c._job_object)
|
||||
group_handle(group_handle && c) : _job_object(c._job_object),
|
||||
_io_port(c._io_port)
|
||||
{
|
||||
c._job_object = ::boost::winapi::invalid_handle_value;
|
||||
c._io_port = ::boost::winapi::invalid_handle_value;
|
||||
}
|
||||
group_handle &operator=(const group_handle & c) = delete;
|
||||
group_handle &operator=(group_handle && c)
|
||||
{
|
||||
::boost::winapi::CloseHandle(_io_port);
|
||||
_io_port = c._io_port;
|
||||
c._io_port = ::boost::winapi::invalid_handle_value;
|
||||
|
||||
::boost::winapi::CloseHandle(_job_object);
|
||||
_job_object = c._job_object;
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/process/detail/config.hpp>
|
||||
#include <boost/process/detail/windows/group_handle.hpp>
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/handler.hpp>
|
||||
|
||||
namespace boost { namespace process {
|
||||
@@ -17,10 +18,12 @@ namespace detail { namespace windows {
|
||||
|
||||
|
||||
|
||||
struct group_ref : handler_base_ext
|
||||
struct group_ref : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
explicit group_ref(group_handle &g) :
|
||||
handle(g.handle())
|
||||
{}
|
||||
|
||||
262
include/boost/process/detail/windows/handle_workaround.hpp
Normal file
262
include/boost/process/detail/windows/handle_workaround.hpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright (c) 2018 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
|
||||
|
||||
#include <boost/winapi/basic_types.hpp>
|
||||
#include <boost/winapi/dll.hpp>
|
||||
#include <boost/winapi/access_rights.hpp>
|
||||
//#define BOOST_USE_WINDOWS_H 1
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
#include <Winternl.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows { namespace workaround
|
||||
{
|
||||
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_ENTRY_
|
||||
{
|
||||
::boost::winapi::ULONG_ OwnerPid;
|
||||
::boost::winapi::BYTE_ ObjectType;
|
||||
::boost::winapi::BYTE_ HandleFlags;
|
||||
::boost::winapi::USHORT_ HandleValue;
|
||||
::boost::winapi::PVOID_ ObjectPointer;
|
||||
::boost::winapi::ULONG_ AccessMask;
|
||||
} SYSTEM_HANDLE_ENTRY_, *PSYSTEM_HANDLE_ENTRY_;
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION_
|
||||
{
|
||||
::boost::winapi::ULONG_ Count;
|
||||
SYSTEM_HANDLE_ENTRY_ Handle[1];
|
||||
} SYSTEM_HANDLE_INFORMATION_, *PSYSTEM_HANDLE_INFORMATION_;
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
|
||||
using UNICODE_STRING_ = ::UNICODE_STRING;
|
||||
using GENERIC_MAPPING_ = ::GENERIC_MAPPING;
|
||||
using OBJECT_INFORMATION_CLASS_ = ::OBJECT_INFORMATION_CLASS;
|
||||
|
||||
constexpr static OBJECT_INFORMATION_CLASS_ ObjectTypeInformation = ::OBJECT_INFORMATION_CLASS::ObjectTypeInformation;
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION_ {
|
||||
UNICODE_STRING TypeName;
|
||||
ULONG TotalNumberOfObjects;
|
||||
ULONG TotalNumberOfHandles;
|
||||
ULONG TotalPagedPoolUsage;
|
||||
ULONG TotalNonPagedPoolUsage;
|
||||
ULONG TotalNamePoolUsage;
|
||||
ULONG TotalHandleTableUsage;
|
||||
ULONG HighWaterNumberOfObjects;
|
||||
ULONG HighWaterNumberOfHandles;
|
||||
ULONG HighWaterPagedPoolUsage;
|
||||
ULONG HighWaterNonPagedPoolUsage;
|
||||
ULONG HighWaterNamePoolUsage;
|
||||
ULONG HighWaterHandleTableUsage;
|
||||
ULONG InvalidAttributes;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
ULONG ValidAccessMask;
|
||||
BOOLEAN SecurityRequired;
|
||||
BOOLEAN MaintainHandleCount;
|
||||
UCHAR TypeIndex;
|
||||
CHAR ReservedByte;
|
||||
ULONG PoolType;
|
||||
ULONG DefaultPagedPoolCharge;
|
||||
ULONG DefaultNonPagedPoolCharge;
|
||||
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
|
||||
|
||||
#else
|
||||
|
||||
typedef enum _OBJECT_INFORMATION_CLASS_
|
||||
{
|
||||
ObjectBasicInformation,
|
||||
ObjectNameInformation,
|
||||
ObjectTypeInformation,
|
||||
ObjectAllInformation,
|
||||
ObjectDataInformation
|
||||
} OBJECT_INFORMATION_CLASS_, *POBJECT_INFORMATION_CLASS_;
|
||||
|
||||
typedef struct _UNICODE_STRING_ {
|
||||
::boost::winapi::USHORT_ Length;
|
||||
::boost::winapi::USHORT_ MaximumLength;
|
||||
::boost::winapi::LPWSTR_ Buffer;
|
||||
} UNICODE_STRING_, *PUNICODE_STRING_;
|
||||
|
||||
typedef struct _GENERIC_MAPPING_ {
|
||||
::boost::winapi::ACCESS_MASK_ GenericRead;
|
||||
::boost::winapi::ACCESS_MASK_ GenericWrite;
|
||||
::boost::winapi::ACCESS_MASK_ GenericExecute;
|
||||
::boost::winapi::ACCESS_MASK_ GenericAll;
|
||||
} GENERIC_MAPPING_;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _OBJECT_BASIC_INFORMATION {
|
||||
::boost::winapi::ULONG_ Attributes;
|
||||
::boost::winapi::ACCESS_MASK_ GrantedAccess;
|
||||
::boost::winapi::ULONG_ HandleCount;
|
||||
::boost::winapi::ULONG_ PointerCount;
|
||||
::boost::winapi::ULONG_ PagedPoolUsage;
|
||||
::boost::winapi::ULONG_ NonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ Reserved[3];
|
||||
::boost::winapi::ULONG_ NameInformationLength;
|
||||
::boost::winapi::ULONG_ TypeInformationLength;
|
||||
::boost::winapi::ULONG_ SecurityDescriptorLength;
|
||||
::boost::winapi::LARGE_INTEGER_ CreateTime;
|
||||
} OBJECT_BASIC_INFORMATION_, *POBJECT_BASIC_INFORMATION_;
|
||||
|
||||
typedef struct _OBJECT_NAME_INFORMATION {
|
||||
UNICODE_STRING_ Name;
|
||||
} OBJECT_NAME_INFORMATION_, *POBJECT_NAME_INFORMATION_;
|
||||
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
using SYSTEM_INFORMATION_CLASS_ = ::SYSTEM_INFORMATION_CLASS;
|
||||
constexpr static SYSTEM_INFORMATION_CLASS_ SystemHandleInformation_ = static_cast<SYSTEM_INFORMATION_CLASS_>(16);
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
|
||||
SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
void * SystemInformation,
|
||||
::boost::winapi::ULONG_ SystemInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
return ::NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_query_object(
|
||||
::boost::winapi::HANDLE_ Handle,
|
||||
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
|
||||
::boost::winapi::PVOID_ ObjectInformation,
|
||||
::boost::winapi::ULONG_ ObjectInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength
|
||||
)
|
||||
{
|
||||
return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi.
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
typedef enum _SYSTEM_INFORMATION_CLASS_
|
||||
{
|
||||
SystemBasicInformation_ = 0,
|
||||
SystemProcessorInformation_ = 1,
|
||||
SystemPerformanceInformation_ = 2,
|
||||
SystemTimeOfDayInformation_ = 3,
|
||||
SystemProcessInformation_ = 5,
|
||||
SystemProcessorPerformanceInformation_ = 8,
|
||||
SystemHandleInformation_ = 16,
|
||||
SystemPagefileInformation_ = 18,
|
||||
SystemInterruptInformation_ = 23,
|
||||
SystemExceptionInformation_ = 33,
|
||||
SystemRegistryQuotaInformation_ = 37,
|
||||
SystemLookasideInformation_ = 45
|
||||
} SYSTEM_INFORMATION_CLASS_;
|
||||
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION_ {
|
||||
UNICODE_STRING_ TypeName;
|
||||
::boost::winapi::ULONG_ TotalNumberOfObjects;
|
||||
::boost::winapi::ULONG_ TotalNumberOfHandles;
|
||||
::boost::winapi::ULONG_ TotalPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ TotalNonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ TotalNamePoolUsage;
|
||||
::boost::winapi::ULONG_ TotalHandleTableUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNumberOfObjects;
|
||||
::boost::winapi::ULONG_ HighWaterNumberOfHandles;
|
||||
::boost::winapi::ULONG_ HighWaterPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNamePoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterHandleTableUsage;
|
||||
::boost::winapi::ULONG_ InvalidAttributes;
|
||||
GENERIC_MAPPING_ GenericMapping;
|
||||
::boost::winapi::ULONG_ ValidAccessMask;
|
||||
::boost::winapi::BOOLEAN_ SecurityRequired;
|
||||
::boost::winapi::BOOLEAN_ MaintainHandleCount;
|
||||
::boost::winapi::UCHAR_ TypeIndex;
|
||||
::boost::winapi::CHAR_ ReservedByte;
|
||||
::boost::winapi::ULONG_ PoolType;
|
||||
::boost::winapi::ULONG_ DefaultPagedPoolCharge;
|
||||
::boost::winapi::ULONG_ DefaultNonPagedPoolCharge;
|
||||
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
__kernel_entry NTSTATUS NtQuerySystemInformation(
|
||||
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
OUT PVOID SystemInformation,
|
||||
IN ULONG SystemInformationLength,
|
||||
OUT PULONG ReturnLength
|
||||
);
|
||||
*/
|
||||
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
|
||||
SYSTEM_INFORMATION_CLASS_,
|
||||
void *,
|
||||
::boost::winapi::ULONG_,
|
||||
::boost::winapi::PULONG_);
|
||||
/*
|
||||
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
|
||||
HANDLE Handle,
|
||||
OBJECT_INFORMATION_CLASS ObjectInformationClass,
|
||||
PVOID ObjectInformation,
|
||||
ULONG ObjectInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
*/
|
||||
|
||||
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
|
||||
::boost::winapi::HANDLE_,
|
||||
OBJECT_INFORMATION_CLASS_,
|
||||
void *,
|
||||
::boost::winapi::ULONG_,
|
||||
::boost::winapi::PULONG_);
|
||||
|
||||
}
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
|
||||
SYSTEM_INFORMATION_CLASS_ SystemInformationClass,
|
||||
void *SystemInformation,
|
||||
::boost::winapi::ULONG_ SystemInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
|
||||
static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation"));
|
||||
|
||||
return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
|
||||
inline ::boost::winapi::BOOL_ nt_query_object(
|
||||
::boost::winapi::HANDLE_ Handle,
|
||||
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
|
||||
void *ObjectInformation,
|
||||
::boost::winapi::ULONG_ ObjectInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
|
||||
static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject"));
|
||||
|
||||
return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}}}}}
|
||||
|
||||
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */
|
||||
176
include/boost/process/detail/windows/handles.hpp
Normal file
176
include/boost/process/detail/windows/handles.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <boost/process/detail/windows/handle_workaround.hpp>
|
||||
#include <boost/process/detail/windows/handler.hpp>
|
||||
#include <boost/winapi/get_current_process_id.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
|
||||
template<typename Executor, typename Function>
|
||||
void foreach_used_handle(Executor &exec, Function &&func);
|
||||
|
||||
|
||||
namespace windows {
|
||||
|
||||
|
||||
using native_handle_type = ::boost::winapi::HANDLE_ ;
|
||||
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
|
||||
{
|
||||
auto pid = ::boost::winapi::GetCurrentProcessId();
|
||||
|
||||
std::vector<char> buffer(2048);
|
||||
constexpr static auto STATUS_INFO_LENGTH_MISMATCH_ = static_cast<::boost::winapi::NTSTATUS_>(0xC0000004l);
|
||||
auto info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
|
||||
|
||||
::boost::winapi::NTSTATUS_ nt_status = STATUS_INFO_LENGTH_MISMATCH_;
|
||||
|
||||
for (int cnt = 0;
|
||||
nt_status == STATUS_INFO_LENGTH_MISMATCH_;
|
||||
nt_status = workaround::nt_system_query_information(
|
||||
workaround::SystemHandleInformation_,
|
||||
info_pointer, buffer.size(),
|
||||
NULL))
|
||||
{
|
||||
buffer.resize(buffer.size() * 2);
|
||||
info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
|
||||
}
|
||||
|
||||
|
||||
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return {};
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
std::vector<native_handle_type> res;
|
||||
for (auto itr = info_pointer->Handle; itr != (info_pointer->Handle + info_pointer->Count); itr++)
|
||||
{
|
||||
if (itr->OwnerPid == pid)
|
||||
res.push_back(reinterpret_cast<native_handle_type>(static_cast<std::uintptr_t>(itr->HandleValue)));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
auto res = get_handles(ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "NtQuerySystemInformation failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
|
||||
{
|
||||
::boost::winapi::ULONG_ actual_size;
|
||||
auto nt_status = workaround::nt_query_object(
|
||||
handle,
|
||||
workaround::ObjectTypeInformation,
|
||||
NULL,
|
||||
0, &actual_size);
|
||||
|
||||
std::vector<char> vec;
|
||||
vec.resize(actual_size);
|
||||
|
||||
workaround::OBJECT_TYPE_INFORMATION_ * type_info_p = reinterpret_cast<workaround::OBJECT_TYPE_INFORMATION_*>(vec.data());
|
||||
nt_status = workaround::nt_query_object(
|
||||
handle,
|
||||
workaround::ObjectTypeInformation,
|
||||
type_info_p,
|
||||
actual_size, &actual_size);
|
||||
|
||||
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
auto &nm = type_info_p->TypeName.Buffer;
|
||||
return type_info_p->TypeName.Length >= 5 &&
|
||||
nm[0] == L'F' &&
|
||||
nm[1] == L'i' &&
|
||||
nm[2] == L'l' &&
|
||||
nm[3] == L'e' &&
|
||||
nm[4] == L'\0';
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
std::error_code ec;
|
||||
auto res = is_stream_handle(handle, ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "NtQueryObject failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
struct limit_handles_ : handler_base_ext
|
||||
{
|
||||
mutable std::vector<::boost::winapi::HANDLE_> handles_with_inherit_flag;
|
||||
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec) const
|
||||
{
|
||||
auto all_handles = get_handles();
|
||||
foreach_used_handle(exec,
|
||||
[&](::boost::winapi::HANDLE_ handle)
|
||||
{
|
||||
auto itr = std::find(all_handles.begin(), all_handles .end(), handle);
|
||||
DWORD flags = 0u;
|
||||
if (itr != all_handles.end())
|
||||
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if ((::boost::winapi::GetHandleInformation(*itr, &flags) != 0)
|
||||
&&((flags & ::boost::winapi::HANDLE_FLAG_INHERIT_) == 0)) //it is NOT inherited anyhow, so ignore too
|
||||
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
});
|
||||
|
||||
auto part_itr = std::partition(all_handles.begin(), all_handles.end(),
|
||||
[](::boost::winapi::HANDLE_ handle) {return handle != ::boost::winapi::INVALID_HANDLE_VALUE_;});
|
||||
|
||||
all_handles.erase(part_itr, all_handles.end()); //remove invalid handles
|
||||
handles_with_inherit_flag = std::move(all_handles);
|
||||
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, 0);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_error(Executor & exec, const std::error_code & ec) const
|
||||
{
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_sucess(Executor & exec) const
|
||||
{
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}}}}
|
||||
|
||||
#endif //PROCESS_HANDLES_HPP
|
||||
@@ -3,8 +3,8 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_WINDOWS_IO_SERVICE_REF_HPP_
|
||||
#define BOOST_PROCESS_WINDOWS_IO_SERVICE_REF_HPP_
|
||||
#ifndef BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_
|
||||
#define BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_
|
||||
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/windows/async_handler.hpp>
|
||||
@@ -130,7 +130,7 @@ struct io_context_ref : boost::process::detail::handler_base
|
||||
boost::asio::io_context & ios, void * handle,
|
||||
const std::shared_ptr<std::atomic<int>> &exit_status)
|
||||
: funcs(std::move(funcs)),
|
||||
handle(new boost::asio::windows::object_handle(ios, handle)),
|
||||
handle(new boost::asio::windows::object_handle(ios.get_executor(), handle)),
|
||||
exit_status(exit_status)
|
||||
{
|
||||
|
||||
@@ -157,4 +157,4 @@ private:
|
||||
|
||||
}}}}
|
||||
|
||||
#endif /* BOOST_PROCESS_WINDOWS_IO_SERVICE_REF_HPP_ */
|
||||
#endif /* BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_ */
|
||||
|
||||
@@ -9,68 +9,184 @@
|
||||
#include <boost/winapi/config.hpp>
|
||||
#include <boost/winapi/basic_types.hpp>
|
||||
#include <boost/winapi/dll.hpp>
|
||||
#include <boost/winapi/overlapped.hpp>
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
#include <windows.h>
|
||||
#else
|
||||
extern "C"
|
||||
{
|
||||
BOOST_SYMBOL_IMPORT ::boost::winapi::HANDLE_ BOOST_WINAPI_WINAPI_CC CreateIoCompletionPort(
|
||||
::boost::winapi::HANDLE_ FileHandle,
|
||||
::boost::winapi::HANDLE_ ExistingCompletionPort,
|
||||
::boost::winapi::ULONG_PTR_ CompletionKey,
|
||||
::boost::winapi::DWORD_ NumberOfConcurrentThreads
|
||||
);
|
||||
|
||||
BOOST_SYMBOL_IMPORT ::boost::winapi::BOOL_ BOOST_WINAPI_WINAPI_CC GetQueuedCompletionStatus(
|
||||
::boost::winapi::HANDLE_ CompletionPort,
|
||||
::boost::winapi::LPDWORD_ lpNumberOfBytes,
|
||||
::boost::winapi::ULONG_PTR_ *lpCompletionKey,
|
||||
_OVERLAPPED **lpOverlapped,
|
||||
::boost::winapi::DWORD_ dwMilliseconds
|
||||
);
|
||||
|
||||
}
|
||||
#endif
|
||||
namespace boost { namespace process { namespace detail { namespace windows { namespace workaround {
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
struct JOBOBJECT_ASSOCIATE_COMPLETION_PORT_
|
||||
{
|
||||
::boost::winapi::PVOID_ CompletionKey;
|
||||
::boost::winapi::HANDLE_ CompletionPort;
|
||||
};
|
||||
|
||||
constexpr static int JOB_OBJECT_MSG_END_OF_JOB_TIME_ = 1;
|
||||
constexpr static int JOB_OBJECT_MSG_END_OF_PROCESS_TIME_ = 2;
|
||||
constexpr static int JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT_ = 3;
|
||||
constexpr static int JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_ = 4;
|
||||
constexpr static int JOB_OBJECT_MSG_NEW_PROCESS_ = 6;
|
||||
constexpr static int JOB_OBJECT_MSG_EXIT_PROCESS_ = 7;
|
||||
constexpr static int JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS_ = 8;
|
||||
constexpr static int JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT_ = 9;
|
||||
constexpr static int JOB_OBJECT_MSG_JOB_MEMORY_LIMIT_ = 10;
|
||||
constexpr static int JOB_OBJECT_MSG_NOTIFICATION_LIMIT_ = 11;
|
||||
constexpr static int JOB_OBJECT_MSG_JOB_CYCLE_TIME_LIMIT_ = 12;
|
||||
constexpr static int JOB_OBJECT_MSG_SILO_TERMINATED_ = 13;
|
||||
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE ::boost::winapi::BOOL_ get_queued_completion_status(
|
||||
::boost::winapi::HANDLE_ CompletionPort,
|
||||
::boost::winapi::LPDWORD_ lpNumberOfBytes,
|
||||
::boost::winapi::ULONG_PTR_ *lpCompletionKey,
|
||||
::boost::winapi::LPOVERLAPPED_ *lpOverlapped,
|
||||
::boost::winapi::DWORD_ dwMilliseconds)
|
||||
{
|
||||
return ::GetQueuedCompletionStatus(
|
||||
CompletionPort,
|
||||
lpNumberOfBytes,
|
||||
lpCompletionKey,
|
||||
reinterpret_cast<::_OVERLAPPED**>(lpOverlapped),
|
||||
dwMilliseconds);
|
||||
}
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
|
||||
constexpr auto static JobObjectExtendedLimitInformation_ = ::JobObjectExtendedLimitInformation;
|
||||
constexpr auto static JobObjectAssociateCompletionPortInformation_ = ::JobObjectAssociateCompletionPortInformation;
|
||||
constexpr auto static JobObjectBasicAccountingInformation_ = ::JobObjectBasicAccountingInformation;
|
||||
|
||||
using JOBOBJECT_BASIC_LIMIT_INFORMATION_ = ::JOBOBJECT_BASIC_LIMIT_INFORMATION;
|
||||
using JOBOBJECTINFOCLASS_ = ::JOBOBJECTINFOCLASS;
|
||||
using IO_COUNTERS_ = ::IO_COUNTERS;
|
||||
using JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ = ::JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
|
||||
using JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ = ::JOBOBJECT_BASIC_ACCOUNTING_INFORMATION;
|
||||
|
||||
inline ::boost::winapi::BOOL_ query_information_job_object(
|
||||
::boost::winapi::HANDLE_ hJob,
|
||||
JOBOBJECTINFOCLASS_ JobObjectInfoClass,
|
||||
void * lpJobObjectInfo,
|
||||
::boost::winapi::DWORD_ cbJobObjectInfoLength,
|
||||
::boost::winapi::DWORD_ *lpReturnLength)
|
||||
{
|
||||
return ::QueryInformationJobObject(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength, lpReturnLength);
|
||||
}
|
||||
|
||||
inline ::boost::winapi::BOOL_ set_information_job_object(
|
||||
::boost::winapi::HANDLE_ hJob,
|
||||
JOBOBJECTINFOCLASS_ JobObjectInfoClass,
|
||||
void * lpJobObjectInfo,
|
||||
::boost::winapi::DWORD_ cbJobObjectInfoLength)
|
||||
{
|
||||
return ::SetInformationJobObject(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
//this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi.
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
typedef enum _JOBOBJECTINFOCLASS_ {
|
||||
JobObjectBasicAccountingInformation_ = 1, JobObjectBasicLimitInformation_,
|
||||
JobObjectBasicProcessIdList_, JobObjectBasicUIRestrictions_,
|
||||
JobObjectSecurityLimitInformation_, JobObjectEndOfJobTimeInformation_,
|
||||
JobObjectAssociateCompletionPortInformation_, JobObjectBasicAndIoAccountingInformation_,
|
||||
JobObjectExtendedLimitInformation_, JobObjectJobSetInformation_,
|
||||
JobObjectGroupInformation_,
|
||||
JobObjectNotificationLimitInformation_,
|
||||
JobObjectLimitViolationInformation_,
|
||||
JobObjectGroupInformationEx_,
|
||||
JobObjectCpuRateControlInformation_,
|
||||
JobObjectCompletionFilter_,
|
||||
JobObjectCompletionCounter_,
|
||||
JobObjectReserved1Information_ = 18,
|
||||
JobObjectReserved2Information_,
|
||||
JobObjectReserved3Information_,
|
||||
JobObjectReserved4Information_,
|
||||
JobObjectReserved5Information_,
|
||||
JobObjectReserved6Information_,
|
||||
JobObjectReserved7Information_,
|
||||
JobObjectReserved8Information_,
|
||||
MaxJobObjectInfoClass_
|
||||
} JOBOBJECTINFOCLASS_;
|
||||
typedef enum _JOBOBJECTINFOCLASS_
|
||||
{
|
||||
JobObjectBasicAccountingInformation_ = 1,
|
||||
JobObjectBasicLimitInformation_,
|
||||
JobObjectBasicProcessIdList_,
|
||||
JobObjectBasicUIRestrictions_,
|
||||
JobObjectSecurityLimitInformation_,
|
||||
JobObjectEndOfJobTimeInformation_,
|
||||
JobObjectAssociateCompletionPortInformation_,
|
||||
JobObjectBasicAndIoAccountingInformation_,
|
||||
JobObjectExtendedLimitInformation_,
|
||||
JobObjectJobSetInformation_,
|
||||
JobObjectGroupInformation_,
|
||||
JobObjectNotificationLimitInformation_,
|
||||
JobObjectLimitViolationInformation_,
|
||||
JobObjectGroupInformationEx_,
|
||||
JobObjectCpuRateControlInformation_,
|
||||
JobObjectCompletionFilter_,
|
||||
JobObjectCompletionCounter_,
|
||||
JobObjectReserved1Information_ = 18,
|
||||
JobObjectReserved2Information_,
|
||||
JobObjectReserved3Information_,
|
||||
JobObjectReserved4Information_,
|
||||
JobObjectReserved5Information_,
|
||||
JobObjectReserved6Information_,
|
||||
JobObjectReserved7Information_,
|
||||
JobObjectReserved8Information_,
|
||||
MaxJobObjectInfoClass_
|
||||
} JOBOBJECTINFOCLASS_;
|
||||
|
||||
typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION_ {
|
||||
::boost::winapi::LARGE_INTEGER_ PerProcessUserTimeLimit;
|
||||
::boost::winapi::LARGE_INTEGER_ PerJobUserTimeLimit;
|
||||
::boost::winapi::DWORD_ LimitFlags;
|
||||
::boost::winapi::SIZE_T_ MinimumWorkingSetSize;
|
||||
::boost::winapi::SIZE_T_ MaximumWorkingSetSize;
|
||||
::boost::winapi::DWORD_ ActiveProcessLimit;
|
||||
::boost::winapi::ULONG_PTR_ Affinity;
|
||||
::boost::winapi::DWORD_ PriorityClass;
|
||||
::boost::winapi::DWORD_ SchedulingClass;
|
||||
typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION_
|
||||
{
|
||||
::boost::winapi::LARGE_INTEGER_ PerProcessUserTimeLimit;
|
||||
::boost::winapi::LARGE_INTEGER_ PerJobUserTimeLimit;
|
||||
::boost::winapi::DWORD_ LimitFlags;
|
||||
::boost::winapi::SIZE_T_ MinimumWorkingSetSize;
|
||||
::boost::winapi::SIZE_T_ MaximumWorkingSetSize;
|
||||
::boost::winapi::DWORD_ ActiveProcessLimit;
|
||||
::boost::winapi::ULONG_PTR_ Affinity;
|
||||
::boost::winapi::DWORD_ PriorityClass;
|
||||
::boost::winapi::DWORD_ SchedulingClass;
|
||||
} JOBOBJECT_BASIC_LIMIT_INFORMATION_;
|
||||
|
||||
|
||||
typedef struct _IO_COUNTERS_ {
|
||||
::boost::winapi::ULONGLONG_ ReadOperationCount;
|
||||
::boost::winapi::ULONGLONG_ WriteOperationCount;
|
||||
::boost::winapi::ULONGLONG_ OtherOperationCount;
|
||||
::boost::winapi::ULONGLONG_ ReadTransferCount;
|
||||
::boost::winapi::ULONGLONG_ WriteTransferCount;
|
||||
::boost::winapi::ULONGLONG_ OtherTransferCount;
|
||||
typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ {
|
||||
::boost::winapi::LARGE_INTEGER_ TotalUserTime;
|
||||
::boost::winapi::LARGE_INTEGER_ TotalKernelTime;
|
||||
::boost::winapi::LARGE_INTEGER_ ThisPeriodTotalUserTime;
|
||||
::boost::winapi::LARGE_INTEGER_ ThisPeriodTotalKernelTime;
|
||||
::boost::winapi::DWORD_ TotalPageFaultCount;
|
||||
::boost::winapi::DWORD_ TotalProcesses;
|
||||
::boost::winapi::DWORD_ ActiveProcesses;
|
||||
::boost::winapi::DWORD_ TotalTerminatedProcesses;
|
||||
} JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_;
|
||||
|
||||
typedef struct _IO_COUNTERS_
|
||||
{
|
||||
::boost::winapi::ULONGLONG_ ReadOperationCount;
|
||||
::boost::winapi::ULONGLONG_ WriteOperationCount;
|
||||
::boost::winapi::ULONGLONG_ OtherOperationCount;
|
||||
::boost::winapi::ULONGLONG_ ReadTransferCount;
|
||||
::boost::winapi::ULONGLONG_ WriteTransferCount;
|
||||
::boost::winapi::ULONGLONG_ OtherTransferCount;
|
||||
} IO_COUNTERS_;
|
||||
|
||||
|
||||
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ {
|
||||
JOBOBJECT_BASIC_LIMIT_INFORMATION_ BasicLimitInformation;
|
||||
IO_COUNTERS_ IoInfo;
|
||||
::boost::winapi::SIZE_T_ ProcessMemoryLimit;
|
||||
::boost::winapi::SIZE_T_ JobMemoryLimit;
|
||||
::boost::winapi::SIZE_T_ PeakProcessMemoryUsed;
|
||||
::boost::winapi::SIZE_T_ PeakJobMemoryUsed;
|
||||
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_
|
||||
{
|
||||
JOBOBJECT_BASIC_LIMIT_INFORMATION_ BasicLimitInformation;
|
||||
IO_COUNTERS_ IoInfo;
|
||||
::boost::winapi::SIZE_T_ ProcessMemoryLimit;
|
||||
::boost::winapi::SIZE_T_ JobMemoryLimit;
|
||||
::boost::winapi::SIZE_T_ PeakProcessMemoryUsed;
|
||||
::boost::winapi::SIZE_T_ PeakJobMemoryUsed;
|
||||
} JOBOBJECT_EXTENDED_LIMIT_INFORMATION_;
|
||||
|
||||
|
||||
@@ -82,7 +198,7 @@ typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_ {
|
||||
_Out_opt_ LPDWORD lpReturnLength
|
||||
);
|
||||
*/
|
||||
typedef ::boost::winapi::BOOL_ (*query_information_job_object_p)(
|
||||
typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *query_information_job_object_p)(
|
||||
::boost::winapi::HANDLE_,
|
||||
JOBOBJECTINFOCLASS_,
|
||||
void *,
|
||||
@@ -93,14 +209,17 @@ typedef ::boost::winapi::BOOL_ (*query_information_job_object_p)(
|
||||
inline ::boost::winapi::BOOL_ query_information_job_object(
|
||||
::boost::winapi::HANDLE_ hJob,
|
||||
JOBOBJECTINFOCLASS_ JobObjectInfoClass,
|
||||
void * lpJobObjectInfo,
|
||||
void *lpJobObjectInfo,
|
||||
::boost::winapi::DWORD_ cbJobObjectInfoLength,
|
||||
::boost::winapi::DWORD_ *lpReturnLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Kernel32.dll");
|
||||
static query_information_job_object_p f = reinterpret_cast<query_information_job_object_p>(::boost::winapi::get_proc_address(h, "QueryInformationJobObject"));
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(
|
||||
L"Kernel32.dll");
|
||||
static query_information_job_object_p f = reinterpret_cast<query_information_job_object_p>(::boost::winapi::get_proc_address(
|
||||
h, "QueryInformationJobObject"));
|
||||
|
||||
return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength, lpReturnLength);
|
||||
return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo,
|
||||
cbJobObjectInfoLength, lpReturnLength);
|
||||
}
|
||||
|
||||
/*BOOL WINAPI SetInformationJobObject(
|
||||
@@ -110,7 +229,7 @@ inline ::boost::winapi::BOOL_ query_information_job_object(
|
||||
_In_ DWORD cbJobObjectInfoLength
|
||||
);*/
|
||||
|
||||
typedef ::boost::winapi::BOOL_ (*set_information_job_object_p)(
|
||||
typedef ::boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *set_information_job_object_p)(
|
||||
::boost::winapi::HANDLE_,
|
||||
JOBOBJECTINFOCLASS_,
|
||||
void *,
|
||||
@@ -121,19 +240,22 @@ typedef ::boost::winapi::BOOL_ (*set_information_job_object_p)(
|
||||
inline ::boost::winapi::BOOL_ set_information_job_object(
|
||||
::boost::winapi::HANDLE_ hJob,
|
||||
JOBOBJECTINFOCLASS_ JobObjectInfoClass,
|
||||
void * lpJobObjectInfo,
|
||||
void *lpJobObjectInfo,
|
||||
::boost::winapi::DWORD_ cbJobObjectInfoLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Kernel32.dll");
|
||||
static set_information_job_object_p f = reinterpret_cast<set_information_job_object_p>(::boost::winapi::get_proc_address(h, "SetInformationJobObject"));
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(
|
||||
L"Kernel32.dll");
|
||||
static set_information_job_object_p f = reinterpret_cast<set_information_job_object_p>(::boost::winapi::get_proc_address(
|
||||
h, "SetInformationJobObject"));
|
||||
|
||||
return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo, cbJobObjectInfoLength);
|
||||
return (*f)(hJob, JobObjectInfoClass, lpJobObjectInfo,
|
||||
cbJobObjectInfoLength);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
constexpr static ::boost::winapi::DWORD_ JOB_OBJECT_LIMIT_BREAKAWAY_OK_ = 0x00000800;
|
||||
|
||||
}}}}}
|
||||
|
||||
|
||||
|
||||
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */
|
||||
|
||||
@@ -14,14 +14,18 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct null_in : public ::boost::process::detail::handler_base
|
||||
struct null_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor source{"NUL", file_descriptor::read};
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return source.handle(); }
|
||||
|
||||
|
||||
public:
|
||||
template <class WindowsExecutor>
|
||||
void on_setup(WindowsExecutor &e) const
|
||||
|
||||
@@ -14,15 +14,18 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct null_out : public ::boost::process::detail::handler_base
|
||||
struct null_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor sink {"NUL", file_descriptor::write}; //works because it gets destroyed AFTER launch.
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return sink.handle(); }
|
||||
|
||||
template <typename WindowsExecutor>
|
||||
void on_setup(WindowsExecutor &e) const;
|
||||
};
|
||||
|
||||
@@ -25,10 +25,10 @@ struct on_exit_ : boost::process::detail::windows::async_handler
|
||||
template<typename Executor>
|
||||
std::function<void(int, const std::error_code&)> on_exit_handler(Executor&)
|
||||
{
|
||||
auto handler = this->handler;
|
||||
return [handler](int exit_code, const std::error_code & ec)
|
||||
auto handler_ = this->handler;
|
||||
return [handler_](int exit_code, const std::error_code & ec)
|
||||
{
|
||||
handler(static_cast<int>(exit_code), ec);
|
||||
handler_(static_cast<int>(exit_code), ec);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -12,14 +12,17 @@
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct pipe_in : public ::boost::process::detail::handler_base
|
||||
struct pipe_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
pipe_in(::boost::winapi::HANDLE_ handle) : handle(handle) {}
|
||||
|
||||
template<typename T> //async_pipe
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
@@ -20,10 +21,12 @@ namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
template<int p1, int p2>
|
||||
struct pipe_out : public ::boost::process::detail::handler_base
|
||||
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
pipe_out(::boost::winapi::HANDLE_ handle) : handle(handle) {}
|
||||
template<typename T>
|
||||
pipe_out(T & p) : handle(p.native_sink())
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_WAIT_GROUP_HPP_
|
||||
|
||||
#include <boost/process/detail/config.hpp>
|
||||
#include <boost/process/detail/windows/group_handle.hpp>
|
||||
#include <boost/winapi/jobs.hpp>
|
||||
#include <boost/winapi/wait.hpp>
|
||||
#include <chrono>
|
||||
@@ -15,11 +16,61 @@ namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct group_handle;
|
||||
|
||||
|
||||
inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono::system_clock::rep wait_time)
|
||||
{
|
||||
::boost::winapi::DWORD_ completion_code;
|
||||
::boost::winapi::ULONG_PTR_ completion_key;
|
||||
::boost::winapi::LPOVERLAPPED_ overlapped;
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
while (workaround::get_queued_completion_status(
|
||||
p._io_port, &completion_code,
|
||||
&completion_key, &overlapped, static_cast<::boost::winapi::DWORD_>(wait_time)))
|
||||
{
|
||||
if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object &&
|
||||
completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_)
|
||||
{
|
||||
|
||||
//double check, could be a different handle from a child
|
||||
workaround::JOBOBJECT_BASIC_ACCOUNTING_INFORMATION_ info;
|
||||
if (!workaround::query_information_job_object(
|
||||
p._job_object,
|
||||
workaround::JobObjectBasicAccountingInformation_,
|
||||
static_cast<void *>(&info),
|
||||
sizeof(info), nullptr))
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
else if (info.ActiveProcesses == 0)
|
||||
return false; //correct, nothing left.
|
||||
}
|
||||
//reduce the remaining wait time -> in case interrupted by something else
|
||||
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 -= static_cast<std::chrono::system_clock::rep>(diff.count());
|
||||
start_time = now;
|
||||
if (wait_time <= 0)
|
||||
return true; //timeout with other source
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto ec_ = get_last_error();
|
||||
if (ec_.value() == ::boost::winapi::wait_timeout)
|
||||
return true; //timeout
|
||||
|
||||
ec = ec_;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void wait(const group_handle &p, std::error_code &ec)
|
||||
{
|
||||
if (::boost::winapi::WaitForSingleObject(p.handle(),
|
||||
::boost::winapi::infinite) == ::boost::winapi::wait_failed)
|
||||
ec = get_last_error();
|
||||
wait_impl(p, ec, ::boost::winapi::infinite);
|
||||
}
|
||||
|
||||
inline void wait(const group_handle &p)
|
||||
@@ -39,16 +90,8 @@ inline bool wait_until(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
timeout_time - Clock::now());
|
||||
|
||||
::boost::winapi::DWORD_ wait_code;
|
||||
wait_code = ::boost::winapi::WaitForSingleObject(p.handle(), ms.count());
|
||||
|
||||
if (wait_code == ::boost::winapi::wait_failed)
|
||||
ec = get_last_error();
|
||||
|
||||
else if (wait_code == ::boost::winapi::wait_timeout)
|
||||
return false; //
|
||||
|
||||
return true;
|
||||
auto timeout = wait_impl(p, ec, ms.count());
|
||||
return !ec && !timeout;
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
@@ -68,7 +111,9 @@ inline bool wait_for(
|
||||
const std::chrono::duration<Rep, Period>& rel_time,
|
||||
std::error_code &ec)
|
||||
{
|
||||
return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec);
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
|
||||
auto timeout = wait_impl(p, ec, ms.count());
|
||||
return !ec && !timeout;
|
||||
}
|
||||
|
||||
template< class Rep, class Period >
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BOOST_PROCESS_EXTENSIONS_HPP_
|
||||
|
||||
#include <boost/process/detail/handler.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#include <boost/process/detail/windows/executor.hpp>
|
||||
@@ -62,6 +63,9 @@ using ::boost::process::detail::api::async_handler;
|
||||
using ::boost::process::detail::get_io_context;
|
||||
using ::boost::process::detail::get_last_error;
|
||||
using ::boost::process::detail::throw_last_error;
|
||||
using ::boost::process::detail::uses_handles;
|
||||
using ::boost::process::detail::foreach_used_handle;
|
||||
using ::boost::process::detail::get_used_handles;
|
||||
|
||||
///This handler is invoked before the process in launched, to setup parameters. The required signature is `void(Exec &)`, where `Exec` is a template parameter.
|
||||
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_setup_> on_setup;
|
||||
@@ -164,10 +168,10 @@ struct require_io_context {};
|
||||
template<typename Executor>
|
||||
std::function<void(int, const std::error_code&)> on_exit_handler(Executor & exec)
|
||||
{
|
||||
auto handler = this->handler;
|
||||
return [handler](int exit_code, const std::error_code & ec)
|
||||
auto handler_ = this->handler;
|
||||
return [handler_](int exit_code, const std::error_code & ec)
|
||||
{
|
||||
handler(static_cast<int>(exit_code), ec);
|
||||
handler_(static_cast<int>(exit_code), ec);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
107
include/boost/process/handles.hpp
Normal file
107
include/boost/process/handles.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_HANDLES_HPP_
|
||||
|
||||
/**
|
||||
* \file boost/process/handles.hpp
|
||||
*
|
||||
* Defines functions to obtain handles of the current process and limit the amount for inherited ones.
|
||||
*/
|
||||
|
||||
#include <boost/process/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/detail/posix/handles.hpp>
|
||||
#elif defined(BOOST_WINDOWS_API)
|
||||
#include <boost/process/detail/windows/handles.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace this_process
|
||||
{
|
||||
|
||||
///The native type for handles
|
||||
using native_handle_type = ::boost::process::detail::api::native_handle_type;
|
||||
|
||||
/**
|
||||
* Get a snapshot of all handles of the process (i.e. file descriptors on posix and handles on windows) of the current process.
|
||||
*
|
||||
* \note This function might not work on certain posix systems.
|
||||
*
|
||||
* \note On Windows version older than windows 8 this function will iterate all the system handles, meaning it might be quite slow.
|
||||
*
|
||||
* \warning This functionality is utterly prone to race conditions, since other threads might open or close handles.
|
||||
*
|
||||
* \return The list of all open handles of the current process
|
||||
*/
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
return ::boost::process::detail::api::get_handles();
|
||||
}
|
||||
|
||||
|
||||
/** \overload std::vector<native_handle_type> get_handles() */
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code &ec)
|
||||
{
|
||||
return ::boost::process::detail::api::get_handles(ec);
|
||||
}
|
||||
|
||||
/** Determines if a given handle is a a stream-handle, i.e. any handle that can be used with read and write functions.
|
||||
* Stream handles include pipes, regular files and sockets.
|
||||
*
|
||||
* \return Indicates if it's a stream handle.
|
||||
*/
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
return ::boost::process::detail::api::is_stream_handle(handle);
|
||||
}
|
||||
|
||||
|
||||
/** \overload bool is_stream_handle(native_handle_type handle) */
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code &ec)
|
||||
{
|
||||
return ::boost::process::detail::api::is_stream_handle(handle, ec);
|
||||
}
|
||||
|
||||
}
|
||||
namespace process
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using limit_handles_ = ::boost::process::detail::api::limit_handles_;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The limit_handles property sets all properties to be inherited only expcitly. It closes all unused file-descriptors on posix after the fork and
|
||||
* removes the inherit flags on windows.
|
||||
*
|
||||
* \note This is executed after the fork on posix.
|
||||
*
|
||||
* \code{.cpp}
|
||||
* system("gcc", limit_handles);
|
||||
* \endcode
|
||||
*
|
||||
* Since limit also closes the standard handles unless they are explicitly redirected they can be ignored by `limit_handles` in the following way.
|
||||
*
|
||||
* \code{.cpp}
|
||||
* system("gcc", limit_handles.allowStd())
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
const static ::boost::process::detail::api::limit_handles_ limit_handles;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BOOST_PROCESS_HANDLES_HPP_
|
||||
@@ -120,6 +120,13 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Move Constructor
|
||||
basic_pipebuf(basic_pipebuf && ) = default;
|
||||
|
||||
///Destructor -> writes the frest of the data
|
||||
~basic_pipebuf()
|
||||
{
|
||||
if (is_open())
|
||||
overflow(Traits::eof());
|
||||
}
|
||||
|
||||
///Move construct from a pipe.
|
||||
basic_pipebuf(pipe_type && p) : _pipe(std::move(p)),
|
||||
_write(default_buffer_size),
|
||||
@@ -155,7 +162,7 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Writes characters to the associated output sequence from the put area
|
||||
int_type overflow(int_type ch = traits_type::eof()) override
|
||||
{
|
||||
if ((ch != traits_type::eof()) && _pipe.is_open())
|
||||
if (_pipe.is_open() && (ch != traits_type::eof()))
|
||||
{
|
||||
if (this->pptr() == this->epptr())
|
||||
{
|
||||
@@ -173,6 +180,9 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
else if (ch == traits_type::eof())
|
||||
this->sync();
|
||||
|
||||
return traits_type::eof();
|
||||
}
|
||||
///Synchronizes the buffers with the associated character sequence
|
||||
@@ -212,6 +222,36 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
const pipe_type &pipe() const & {return _pipe;}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_pipe);}
|
||||
|
||||
///Check if the pipe is open
|
||||
bool is_open() const {return _pipe.is_open(); }
|
||||
|
||||
///Open a new pipe
|
||||
basic_pipebuf<CharT, Traits>* open()
|
||||
{
|
||||
if (is_open())
|
||||
return nullptr;
|
||||
_pipe = pipe();
|
||||
return this;
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
basic_pipebuf<CharT, Traits>* open(const std::string & name)
|
||||
{
|
||||
if (is_open())
|
||||
return nullptr;
|
||||
_pipe = pipe(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
basic_pipebuf<CharT, Traits>* close()
|
||||
{
|
||||
if (!is_open())
|
||||
return nullptr;
|
||||
overflow(Traits::eof());
|
||||
return this;
|
||||
}
|
||||
private:
|
||||
pipe_type _pipe;
|
||||
std::vector<char_type> _write;
|
||||
@@ -223,8 +263,13 @@ private:
|
||||
return false;
|
||||
|
||||
auto base = this->pbase();
|
||||
auto wrt = _pipe.write(base,
|
||||
|
||||
if (base == this->pptr())
|
||||
return true;
|
||||
|
||||
std::ptrdiff_t wrt = _pipe.write(base,
|
||||
static_cast<typename pipe_type::int_type>(this->pptr() - base));
|
||||
|
||||
std::ptrdiff_t diff = this->pptr() - base;
|
||||
|
||||
if (wrt < diff)
|
||||
@@ -250,7 +295,7 @@ template<
|
||||
>
|
||||
class basic_ipstream : public std::basic_istream<CharT, Traits>
|
||||
{
|
||||
basic_pipebuf<CharT, Traits> _buf;
|
||||
mutable basic_pipebuf<CharT, Traits> _buf;
|
||||
public:
|
||||
|
||||
typedef basic_pipe<CharT, Traits> pipe_type;
|
||||
@@ -262,7 +307,7 @@ public:
|
||||
typedef typename Traits::off_type off_type ;
|
||||
|
||||
///Get access to the underlying stream_buf
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() {return &_buf;};
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() const {return &_buf;};
|
||||
|
||||
///Default constructor.
|
||||
basic_ipstream() : std::basic_istream<CharT, Traits>(nullptr)
|
||||
@@ -272,7 +317,10 @@ public:
|
||||
///Copy constructor.
|
||||
basic_ipstream(const basic_ipstream & ) = delete;
|
||||
///Move constructor.
|
||||
basic_ipstream(basic_ipstream && ) = default;
|
||||
basic_ipstream(basic_ipstream && lhs) : std::basic_istream<CharT, Traits>(nullptr), _buf(std::move(lhs._buf))
|
||||
{
|
||||
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
|
||||
}
|
||||
|
||||
///Move construct from a pipe.
|
||||
basic_ipstream(pipe_type && p) : std::basic_istream<CharT, Traits>(nullptr), _buf(std::move(p))
|
||||
@@ -289,7 +337,12 @@ public:
|
||||
///Copy assignment.
|
||||
basic_ipstream& operator=(const basic_ipstream & ) = delete;
|
||||
///Move assignment
|
||||
basic_ipstream& operator=(basic_ipstream && ) = default;
|
||||
basic_ipstream& operator=(basic_ipstream && lhs)
|
||||
{
|
||||
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
|
||||
};
|
||||
///Move assignment of a pipe.
|
||||
basic_ipstream& operator=(pipe_type && p)
|
||||
{
|
||||
@@ -312,6 +365,33 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
///Check if the pipe is open
|
||||
bool is_open() const {return _buf->is_open();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_ipstream<char> ipstream;
|
||||
@@ -326,7 +406,7 @@ template<
|
||||
>
|
||||
class basic_opstream : public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
basic_pipebuf<CharT, Traits> _buf;
|
||||
mutable basic_pipebuf<CharT, Traits> _buf;
|
||||
public:
|
||||
typedef basic_pipe<CharT, Traits> pipe_type;
|
||||
|
||||
@@ -338,7 +418,7 @@ public:
|
||||
|
||||
|
||||
///Get access to the underlying stream_buf
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() const {return _buf;};
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() const {return &_buf;};
|
||||
|
||||
///Default constructor.
|
||||
basic_opstream() : std::basic_ostream<CharT, Traits>(nullptr)
|
||||
@@ -348,8 +428,10 @@ public:
|
||||
///Copy constructor.
|
||||
basic_opstream(const basic_opstream & ) = delete;
|
||||
///Move constructor.
|
||||
basic_opstream(basic_opstream && ) = default;
|
||||
|
||||
basic_opstream(basic_opstream && lhs) : std::basic_ostream<CharT, Traits>(nullptr), _buf(std::move(lhs._buf))
|
||||
{
|
||||
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
|
||||
}
|
||||
///Move construct from a pipe.
|
||||
basic_opstream(pipe_type && p) : std::basic_ostream<CharT, Traits>(nullptr), _buf(std::move(p))
|
||||
{
|
||||
@@ -363,7 +445,13 @@ public:
|
||||
///Copy assignment.
|
||||
basic_opstream& operator=(const basic_opstream & ) = delete;
|
||||
///Move assignment
|
||||
basic_opstream& operator=(basic_opstream && ) = default;
|
||||
basic_opstream& operator=(basic_opstream && lhs)
|
||||
{
|
||||
std::basic_ostream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
|
||||
};
|
||||
|
||||
///Move assignment of a pipe.
|
||||
basic_opstream& operator=(pipe_type && p)
|
||||
{
|
||||
@@ -386,6 +474,31 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_opstream<char> opstream;
|
||||
@@ -401,7 +514,7 @@ template<
|
||||
>
|
||||
class basic_pstream : public std::basic_iostream<CharT, Traits>
|
||||
{
|
||||
basic_pipebuf<CharT, Traits> _buf;
|
||||
mutable basic_pipebuf<CharT, Traits> _buf;
|
||||
public:
|
||||
typedef basic_pipe<CharT, Traits> pipe_type;
|
||||
|
||||
@@ -413,7 +526,7 @@ public:
|
||||
|
||||
|
||||
///Get access to the underlying stream_buf
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() const {return _buf;};
|
||||
basic_pipebuf<CharT, Traits>* rdbuf() const {return &_buf;};
|
||||
|
||||
///Default constructor.
|
||||
basic_pstream() : std::basic_iostream<CharT, Traits>(nullptr)
|
||||
@@ -423,8 +536,10 @@ public:
|
||||
///Copy constructor.
|
||||
basic_pstream(const basic_pstream & ) = delete;
|
||||
///Move constructor.
|
||||
basic_pstream(basic_pstream && ) = default;
|
||||
|
||||
basic_pstream(basic_pstream && lhs) : std::basic_iostream<CharT, Traits>(nullptr), _buf(std::move(lhs._buf))
|
||||
{
|
||||
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
|
||||
}
|
||||
///Move construct from a pipe.
|
||||
basic_pstream(pipe_type && p) : std::basic_iostream<CharT, Traits>(nullptr), _buf(std::move(p))
|
||||
{
|
||||
@@ -438,7 +553,12 @@ public:
|
||||
///Copy assignment.
|
||||
basic_pstream& operator=(const basic_pstream & ) = delete;
|
||||
///Move assignment
|
||||
basic_pstream& operator=(basic_pstream && ) = default;
|
||||
basic_pstream& operator=(basic_pstream && lhs)
|
||||
{
|
||||
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
|
||||
};
|
||||
///Move assignment of a pipe.
|
||||
basic_pstream& operator=(pipe_type && p)
|
||||
{
|
||||
@@ -461,6 +581,31 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_pstream<char> pstream;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <boost/process/detail/config.hpp>
|
||||
#include <boost/process/detail/handler.hpp>
|
||||
#include <boost/process/locale.hpp>
|
||||
#include <boost/process/detail/traits/wchar_t.hpp>
|
||||
|
||||
#if defined (BOOST_POSIX_API)
|
||||
#include <boost/process/detail/posix/start_dir.hpp>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <boost/process/child.hpp>
|
||||
#include <boost/process/detail/async_handler.hpp>
|
||||
#include <boost/process/detail/execute_impl.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <type_traits>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
@@ -62,7 +63,7 @@ inline int system_impl(
|
||||
::boost::process::on_exit(
|
||||
[&](int, const std::error_code&)
|
||||
{
|
||||
ios.post([&]{exited.store(true);});
|
||||
boost::asio::post(ios.get_executor(), [&]{exited.store(true);});
|
||||
}));
|
||||
if (!c.valid() || !check.succeeded)
|
||||
return -1;
|
||||
@@ -85,6 +86,8 @@ inline int system_impl(
|
||||
return -1;
|
||||
|
||||
ios.run();
|
||||
if (c.running())
|
||||
c.wait();
|
||||
return c.exit_code();
|
||||
}
|
||||
|
||||
|
||||
114
test/Jamfile.jam
114
test/Jamfile.jam
@@ -16,9 +16,11 @@ if [ os.name ] = NT
|
||||
lib ws2_32 ;
|
||||
lib shell32 ;
|
||||
lib Advapi32 ;
|
||||
lib Ntdll ;
|
||||
}
|
||||
|
||||
project : requirements
|
||||
<define>BOOST_ASIO_NO_DEPRECATED
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
@@ -43,7 +45,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
|
||||
<warnings>off <target-os>windows:<source>shell32 <target-os>windows:<source>Ntdll
|
||||
;
|
||||
|
||||
exe exit_argc : exit_argc.cpp :
|
||||
@@ -52,63 +54,81 @@ exe exit_argc : exit_argc.cpp :
|
||||
|
||||
exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem : <warnings>off <target-os>windows:<source>shell32 ;
|
||||
|
||||
rule test-options ( name )
|
||||
{
|
||||
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 -- ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
test-suite bare :
|
||||
[ run environment.cpp system filesystem ]
|
||||
[ run async_pipe.cpp system filesystem ]
|
||||
[ run pipe.cpp system filesystem ]
|
||||
[ run environment.cpp system filesystem : [ test-options environment ] ]
|
||||
[ run async_pipe.cpp system filesystem : [ test-options async_pipe ] ]
|
||||
[ run pipe.cpp system filesystem : [ test-options pipe ] ]
|
||||
[ compile no_ansi_apps.cpp ]
|
||||
[ compile-fail spawn_fail.cpp ]
|
||||
[ compile-fail async_system_fail.cpp ]
|
||||
[ compile asio_no_deprecated.cpp ]
|
||||
;
|
||||
|
||||
test-suite with-valgrind :
|
||||
[ run async.cpp system thread filesystem : : sparring_partner ]
|
||||
[ run async_fut.cpp system thread filesystem : : sparring_partner ]
|
||||
[ run args_handling.cpp system thread filesystem : : exit_argc ]
|
||||
[ run args_cmd.cpp system filesystem : : sparring_partner ]
|
||||
[ run wargs_cmd.cpp system filesystem : : sparring_partner ]
|
||||
[ run bind_stderr.cpp filesystem : : sparring_partner ]
|
||||
[ run bind_stdin.cpp system filesystem : : sparring_partner ]
|
||||
[ run bind_stdin_stdout.cpp system filesystem : : sparring_partner ]
|
||||
[ run bind_stdout.cpp system filesystem : : sparring_partner ]
|
||||
[ run bind_stdout_stderr.cpp system filesystem : : sparring_partner ]
|
||||
[ run pipe_fwd.cpp system filesystem : : sparring_partner ]
|
||||
[ run cmd_test.cpp system filesystem : : sparring_partner ]
|
||||
[ run close_stderr.cpp system filesystem : : sparring_partner ]
|
||||
[ run close_stdin.cpp system filesystem : : sparring_partner ]
|
||||
[ run close_stdout.cpp system filesystem : : sparring_partner ]
|
||||
[ run error.cpp system filesystem : : sparring_partner ]
|
||||
[ run exit_code.cpp program_options system filesystem : : sparring_partner ]
|
||||
[ run extensions.cpp system filesystem : : sparring_partner ]
|
||||
[ run env.cpp program_options system filesystem : : sparring_partner ]
|
||||
[ run group.cpp system thread filesystem : : sub_launch ]
|
||||
[ run async.cpp system thread filesystem : [ test-options async ] : sparring_partner ]
|
||||
[ run async_fut.cpp system thread filesystem : [ test-options async_fut ] : sparring_partner ]
|
||||
[ run args_handling.cpp system thread filesystem : [ test-options args_handling ] : exit_argc ]
|
||||
[ run args_cmd.cpp system filesystem : [ test-options args_cmd ] : sparring_partner ]
|
||||
[ run wargs_cmd.cpp system filesystem : [ test-options wargs_cmd ] : sparring_partner ]
|
||||
[ run bind_stderr.cpp filesystem : [ test-options bind_stderr ] : sparring_partner ]
|
||||
[ run bind_stdin.cpp system filesystem : [ test-options bind_stdin ] : sparring_partner ]
|
||||
[ run bind_stdin_stdout.cpp system filesystem : [ test-options bind_stdin_stdout ] : sparring_partner ]
|
||||
[ run bind_stdout.cpp system filesystem : [ test-options bind_stdout ] : sparring_partner ]
|
||||
[ run bind_stdout_stderr.cpp system filesystem : [ test-options bind_stdout_stderr ] : sparring_partner ]
|
||||
[ run pipe_fwd.cpp system filesystem : [ test-options pipe_fwd ] : sparring_partner ]
|
||||
[ run cmd_test.cpp system filesystem : [ test-options cmd_test ] : sparring_partner ]
|
||||
[ run close_stderr.cpp system filesystem : [ test-options close_stderr ] : sparring_partner ]
|
||||
[ run close_stdin.cpp system filesystem : [ test-options close_stdin ] : sparring_partner ]
|
||||
[ run close_stdout.cpp system filesystem : [ test-options close_stdout ] : sparring_partner ]
|
||||
[ run error.cpp system filesystem : [ test-options error ] : sparring_partner ]
|
||||
[ 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 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 ]
|
||||
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ]
|
||||
[ run run_exe.cpp filesystem : : sparring_partner ]
|
||||
[ run run_exe_path.cpp filesystem : : sparring_partner ]
|
||||
[ run search_path.cpp filesystem system : : : <target-os>windows:<source>shell32 ]
|
||||
[ run shell.cpp filesystem system : : sparring_partner ]
|
||||
[ run shell_path.cpp filesystem system ]
|
||||
[ run system_test1.cpp filesystem system : : sparring_partner ]
|
||||
[ run system_test2.cpp filesystem system : : sparring_partner ]
|
||||
[ run spawn.cpp filesystem system : : sparring_partner ]
|
||||
[ run start_dir.cpp filesystem system : : sparring_partner ]
|
||||
[ run terminate.cpp system filesystem : : sparring_partner ]
|
||||
[ run throw_on_error.cpp system filesystem : : sparring_partner ]
|
||||
[ run wait.cpp system filesystem : : sparring_partner ]
|
||||
[ run wait_for.cpp system filesystem : : sparring_partner ]
|
||||
[ run on_exit.cpp system filesystem : : sparring_partner ]
|
||||
[ run on_exit2.cpp system filesystem : : sparring_partner ]
|
||||
[ run on_exit3.cpp system filesystem : : sparring_partner ]
|
||||
[ run posix_specific.cpp system filesystem : : sparring_partner : <build>no <target-os>linux:<build>yes ]
|
||||
[ run windows_specific.cpp filesystem system : : sparring_partner : <build>no <target-os>windows:<build>yes ]
|
||||
[ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ]
|
||||
[ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ]
|
||||
[ run shell.cpp filesystem system : [ test-options shell ] : sparring_partner ]
|
||||
[ run shell_path.cpp filesystem system : [ test-options shell_path ] ]
|
||||
[ run system_test1.cpp filesystem system : [ test-options system_test1 ] : sparring_partner ]
|
||||
[ run system_test2.cpp filesystem system : [ test-options system_test2 ] : sparring_partner ]
|
||||
[ run spawn.cpp filesystem system : [ test-options spawn ] : sparring_partner ]
|
||||
[ run start_dir.cpp filesystem system : [ test-options start_dir ] : sparring_partner ]
|
||||
[ run terminate.cpp system filesystem : [ test-options terminate ] : sparring_partner ]
|
||||
[ run throw_on_error.cpp system filesystem : [ test-options throw_on_error ] : sparring_partner ]
|
||||
[ run wait.cpp system filesystem : [ test-options wait ] : sparring_partner ]
|
||||
[ run wait_for.cpp system filesystem : [ test-options wait_for ] : sparring_partner ]
|
||||
[ run on_exit.cpp system filesystem : [ test-options on_exit ] : sparring_partner ]
|
||||
[ run on_exit2.cpp system filesystem : [ test-options on_exit2 ] : sparring_partner ]
|
||||
[ run on_exit3.cpp system filesystem : [ test-options on_exit3 ] : sparring_partner ]
|
||||
[ run posix_specific.cpp system filesystem : [ test-options posix_specific ] : sparring_partner : <build>no <target-os>linux:<build>yes ]
|
||||
[ run windows_specific.cpp filesystem system : [ test-options windows_specific ] : sparring_partner : <build>no <target-os>windows:<build>yes ]
|
||||
: <dependency>bare ;
|
||||
|
||||
test-suite without-valgrind :
|
||||
[ run async_system_future.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful_error.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful_except.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackless.cpp filesystem system coroutine : : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run vfork.cpp system filesystem : : sparring_partner : <build>no <target-os>linux:<build>yes ]
|
||||
[ run async_system_future.cpp filesystem system coroutine : [ test-options async_system_future ] : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful.cpp filesystem system coroutine : [ test-options async_system_stackful ] : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful_error.cpp filesystem system coroutine : [ test-options async_system_stackful_error ] : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackful_except.cpp filesystem system coroutine : [ test-options async_system_stackful_except ] : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run async_system_stackless.cpp filesystem system coroutine : [ test-options async_system_stackless ] : sparring_partner : <link>static <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run vfork.cpp system filesystem : [ test-options vfork ] : sparring_partner : <build>no <target-os>linux:<build>yes ]
|
||||
;
|
||||
|
||||
|
||||
|
||||
@@ -17,12 +17,13 @@ init:
|
||||
os: Visual Studio 2015
|
||||
configuration: Debug
|
||||
platform: x64
|
||||
build: off
|
||||
|
||||
###############################################################################################################
|
||||
# From this point and below code is same for all the Boost libs
|
||||
###############################################################################################################
|
||||
|
||||
version: 1.61.{build}-{branch}
|
||||
version: 1.69.{build}-{branch}
|
||||
|
||||
# branches to build
|
||||
branches:
|
||||
@@ -66,9 +67,11 @@ 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:
|
||||
on_failure:
|
||||
on_finish:
|
||||
- curl -s https://report.ci/upload.py | python - --name "Windows test run" --root_dir=%BOOST%/libs/%PROJECT_TO_TEST% --framework boost
|
||||
|
||||
|
||||
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
|
||||
164
test/async.cpp
164
test/async.cpp
@@ -8,7 +8,7 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
//#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <boost/process/error.hpp>
|
||||
@@ -20,15 +20,21 @@
|
||||
#include <future>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
#if __APPLE__
|
||||
auto abort_sig = signal(SIGALRM, +[](int){std::terminate();});
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
@@ -39,34 +45,34 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
|
||||
bool exit_called_for_c1 = false;
|
||||
int exit_code_c1 = 0;
|
||||
bp::child c1(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "123",
|
||||
ec,
|
||||
io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
BOOST_CHECK(!exit_called_for_c1);
|
||||
exit_code_c1 = exit; exit_called_for_c1=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
})
|
||||
);
|
||||
|
||||
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();});
|
||||
|
||||
bp::child c1(master_test_suite().argv[1],
|
||||
"test", "--exit-code", "123",
|
||||
ec, io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
BOOST_CHECK(!exit_called_for_c1);
|
||||
exit_code_c1 = exit; exit_called_for_c1=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
timeout.cancel();
|
||||
}));
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
bool exit_called_for_c2 = false;
|
||||
int exit_code_c2 = 0;
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "21",
|
||||
ec,
|
||||
io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
BOOST_CHECK(!exit_called_for_c2);
|
||||
exit_code_c2 = exit; exit_called_for_c2=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
})
|
||||
);
|
||||
bp::child c2(master_test_suite().argv[1],
|
||||
"test", "--exit-code", "21",
|
||||
ec, io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
BOOST_CHECK(!exit_called_for_c2);
|
||||
exit_code_c2 = exit; exit_called_for_c2=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
})
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
io_context.run();
|
||||
@@ -80,7 +86,7 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(c2.exit_code(), 21);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(3))
|
||||
BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
@@ -90,6 +96,10 @@ BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(3))
|
||||
bool exit_called = false;
|
||||
int exit_code = 0;
|
||||
std::error_code ec;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(3)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
bp::child c1(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "1",
|
||||
@@ -106,6 +116,7 @@ BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(3))
|
||||
{
|
||||
exit_code = exit; exit_called=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
timeout.cancel();
|
||||
})
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
@@ -121,7 +132,7 @@ BOOST_AUTO_TEST_CASE(async_wait_sync_wait, *boost::unit_test::timeout(3))
|
||||
BOOST_CHECK_EQUAL(c2.exit_code(), 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3))
|
||||
BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(10))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
@@ -129,6 +140,11 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3
|
||||
boost::asio::io_context io_context1;
|
||||
boost::asio::io_context io_context2;
|
||||
|
||||
boost::asio::deadline_timer timeout1{io_context1, boost::posix_time::seconds(2)};
|
||||
timeout1.async_wait([&](boost::system::error_code ec){if (!ec) io_context1.stop();});
|
||||
|
||||
boost::asio::deadline_timer timeout2{io_context2, boost::posix_time::seconds(7)};
|
||||
timeout2.async_wait([&](boost::system::error_code ec){if (!ec) io_context2.stop();});
|
||||
std::error_code ec;
|
||||
|
||||
bool exit_called_for_c1 = false;
|
||||
@@ -143,6 +159,7 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3
|
||||
BOOST_CHECK(!exit_called_for_c1);
|
||||
exit_code_c1 = exit; exit_called_for_c1=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
timeout1.cancel();
|
||||
})
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
@@ -151,7 +168,7 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3
|
||||
int exit_code_c2 = 0;
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "2", "--wait", "1",
|
||||
"test", "--exit-code", "2", "--wait", "4",
|
||||
ec,
|
||||
io_context2,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
@@ -159,13 +176,17 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3
|
||||
BOOST_CHECK(!exit_called_for_c2);
|
||||
exit_code_c2 = exit; exit_called_for_c2=true;
|
||||
BOOST_CHECK(!ec_in);
|
||||
timeout2.cancel();
|
||||
})
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
// Regression test for #143: make sure each io_context handles its own children
|
||||
io_context2.run();
|
||||
io_context1.run();
|
||||
std::thread thr1{[&]{io_context1.run();}};
|
||||
std::thread thr2{[&]{io_context2.run();}};
|
||||
|
||||
thr1.join();
|
||||
thr2.join();
|
||||
c1.wait(ec);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
@@ -177,7 +198,7 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(3
|
||||
BOOST_CHECK_EQUAL(c2.exit_code(), 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
@@ -186,6 +207,9 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(2))
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(5)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
bool exit_called = false;
|
||||
int exit_code = 0;
|
||||
bp::child c(
|
||||
@@ -196,9 +220,11 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(2))
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
BOOST_CHECK(!exit_called);
|
||||
exit_code = exit; exit_called=true;
|
||||
exit_code = exit;
|
||||
exit_called=true;
|
||||
BOOST_TEST_MESSAGE(ec_in.message());
|
||||
BOOST_CHECK(!ec_in);
|
||||
timeout.cancel();
|
||||
})
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
@@ -206,18 +232,21 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(2))
|
||||
io_context.run();
|
||||
|
||||
BOOST_CHECK(exit_called);
|
||||
BOOST_CHECK(exit_code != 0);
|
||||
BOOST_CHECK_NE(exit_code, 0);
|
||||
BOOST_CHECK_EQUAL(c.exit_code(), exit_code);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_future, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_future, *boost::unit_test::timeout(3))
|
||||
{
|
||||
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();});
|
||||
|
||||
std::error_code ec;
|
||||
std::future<int> fut;
|
||||
bp::child c(
|
||||
@@ -229,13 +258,15 @@ BOOST_AUTO_TEST_CASE(async_future, *boost::unit_test::timeout(2))
|
||||
);
|
||||
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
io_context.run();
|
||||
|
||||
BOOST_REQUIRE(fut.valid());
|
||||
BOOST_CHECK_EQUAL(fut.get(), 42);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
@@ -246,6 +277,9 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(2))
|
||||
|
||||
boost::asio::streambuf buf;
|
||||
|
||||
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();});
|
||||
|
||||
bp::child c(master_test_suite().argv[1],
|
||||
"test", "--echo-stdout", "abc",
|
||||
bp::std_out > buf,
|
||||
@@ -253,8 +287,8 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(2))
|
||||
ec);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
|
||||
io_context.run();
|
||||
|
||||
std::istream istr(&buf);
|
||||
|
||||
std::string line;
|
||||
@@ -266,7 +300,7 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(2))
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_in_stream, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_in_stream, *boost::unit_test::timeout(5))
|
||||
{
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
@@ -283,6 +317,9 @@ BOOST_AUTO_TEST_CASE(async_in_stream, *boost::unit_test::timeout(2))
|
||||
std::ostream ostr(&in_buf);
|
||||
ostr << "-string" << endl ;
|
||||
|
||||
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();});
|
||||
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--prefix-once", "test",
|
||||
@@ -293,8 +330,8 @@ BOOST_AUTO_TEST_CASE(async_in_stream, *boost::unit_test::timeout(2))
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
|
||||
io_context.run();
|
||||
|
||||
std::istream istr(&buf);
|
||||
|
||||
std::string line;
|
||||
@@ -310,13 +347,16 @@ BOOST_AUTO_TEST_CASE(async_in_stream, *boost::unit_test::timeout(2))
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_error, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_error, *boost::unit_test::timeout(3))
|
||||
{
|
||||
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(
|
||||
@@ -330,7 +370,47 @@ BOOST_AUTO_TEST_CASE(async_error, *boost::unit_test::timeout(2))
|
||||
);
|
||||
|
||||
BOOST_REQUIRE(ec);
|
||||
|
||||
io_context.run();
|
||||
|
||||
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();
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -101,3 +103,4 @@ BOOST_AUTO_TEST_CASE(emtpy_out, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(fut.get(), "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -23,6 +23,9 @@ using namespace std;
|
||||
namespace bp = boost::process;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(plain_async, *boost::unit_test::timeout(5))
|
||||
{
|
||||
asio::io_context ios;
|
||||
@@ -83,3 +86,57 @@ BOOST_AUTO_TEST_CASE(multithreaded_async_pipe)
|
||||
for (auto &t : threads)
|
||||
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();
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <array>
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
BOOST_AUTO_TEST_CASE(future, *boost::unit_test::timeout(15))
|
||||
{
|
||||
@@ -59,3 +60,5 @@ BOOST_AUTO_TEST_CASE(future_error, *boost::unit_test::timeout(15))
|
||||
int exit_code = 0;
|
||||
BOOST_CHECK_THROW(exit_code = fut.get(), boost::system::system_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -15,13 +15,16 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#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>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
@@ -44,9 +47,12 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
BOOST_CHECK(did_something_else);
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
ios.post([&]{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);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
@@ -45,7 +47,7 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
ios.post([&]{did_something_else = true;});
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
|
||||
ios.run();
|
||||
BOOST_CHECK(did_something_else);
|
||||
@@ -71,7 +73,7 @@ BOOST_AUTO_TEST_CASE(stackful_except, *boost::unit_test::timeout(15))
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
ios.post([&]{did_something_else = true;});
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
ios.run();
|
||||
|
||||
BOOST_CHECK(did_something_else);
|
||||
@@ -100,8 +102,10 @@ BOOST_AUTO_TEST_CASE(stackful_error, *boost::unit_test::timeout(15))
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
ios.post([&]{did_something_else = true;});
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
ios.run();
|
||||
|
||||
BOOST_CHECK(did_something_else);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_CASE(stackful_except, *boost::unit_test::timeout(15))
|
||||
@@ -44,10 +46,10 @@ BOOST_AUTO_TEST_CASE(stackful_except, *boost::unit_test::timeout(15))
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
ios.post([&]{did_something_else = true;});
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
ios.run();
|
||||
|
||||
BOOST_CHECK(did_something_else);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
BOOST_AUTO_TEST_SUITE( async );
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
|
||||
@@ -57,10 +59,13 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
|
||||
}
|
||||
} stackless{ios, did_something_else};
|
||||
|
||||
ios.post([&]{stackless();});
|
||||
ios.post([&]{did_something_else = true;});
|
||||
boost::asio::post(ios.get_executor(), [&]{stackless();});
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
|
||||
ios.run();
|
||||
|
||||
BOOST_CHECK(did_something_else);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -39,6 +39,7 @@ typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_SUITE( bind_stderr );
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(2))
|
||||
{
|
||||
@@ -150,3 +151,5 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2))
|
||||
boost::filesystem::remove(pth);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( bind_stdin );
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
# include <windows.h>
|
||||
@@ -255,3 +256,5 @@ BOOST_AUTO_TEST_CASE(file_io_C, *boost::unit_test::timeout(2))
|
||||
c.wait();
|
||||
boost::filesystem::remove(pth);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <iostream>
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_SUITE( bind_stdin_stdout );
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(10))
|
||||
{
|
||||
@@ -53,3 +54,5 @@ BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(10))
|
||||
|
||||
BOOST_CHECK(c.wait_for(std::chrono::seconds(3)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -37,6 +37,9 @@ typedef boost::asio::windows::stream_handle pipe_end;
|
||||
typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( bind_stdout );
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
@@ -164,3 +167,5 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2))
|
||||
boost::filesystem::remove(pth);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -35,6 +35,9 @@ typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( bind_stdout_stderr );
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(2))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
@@ -131,3 +134,5 @@ BOOST_AUTO_TEST_CASE(nul, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(EXIT_SUCCESS, exit_code);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -25,7 +25,7 @@
|
||||
namespace bp = boost::process;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(closed)
|
||||
BOOST_AUTO_TEST_CASE(close_stderr)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(closed)
|
||||
BOOST_AUTO_TEST_CASE(close_stdin)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(closed)
|
||||
BOOST_AUTO_TEST_CASE(close_stdout)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <boost/process/error.hpp>
|
||||
@@ -72,11 +72,12 @@ BOOST_AUTO_TEST_CASE(sync_wait_abort)
|
||||
struct wait_handler
|
||||
{
|
||||
HANDLE handle_;
|
||||
|
||||
wait_handler(HANDLE handle) : handle_(handle) {}
|
||||
bool &called_;
|
||||
wait_handler(HANDLE handle, bool &called) : handle_(handle), called_(called) {}
|
||||
|
||||
void operator()(const boost::system::error_code &ec)
|
||||
{
|
||||
called_ = true;
|
||||
BOOST_REQUIRE(!ec);
|
||||
DWORD exit_code;
|
||||
BOOST_REQUIRE(GetExitCodeProcess(handle_, &exit_code));
|
||||
@@ -86,8 +87,13 @@ struct wait_handler
|
||||
#elif defined(BOOST_POSIX_API)
|
||||
struct wait_handler
|
||||
{
|
||||
bool &called_;
|
||||
|
||||
wait_handler (bool & called) : called_(called) {}
|
||||
|
||||
void operator()(const boost::system::error_code &ec, int signal)
|
||||
{
|
||||
called_ = true;
|
||||
BOOST_REQUIRE(!ec);
|
||||
BOOST_REQUIRE_EQUAL(SIGCHLD, signal);
|
||||
int status;
|
||||
@@ -104,9 +110,14 @@ BOOST_AUTO_TEST_CASE(async_wait)
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(3)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
|
||||
bool wh_called = false;
|
||||
#if defined(BOOST_POSIX_API)
|
||||
signal_set set(io_context, SIGCHLD);
|
||||
set.async_wait(wait_handler());
|
||||
set.async_wait(wait_handler(wh_called));
|
||||
#endif
|
||||
|
||||
std::error_code ec;
|
||||
@@ -118,10 +129,31 @@ BOOST_AUTO_TEST_CASE(async_wait)
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
windows::object_handle handle(io_context, c.native_handle());
|
||||
handle.async_wait(wait_handler(handle.native_handle()));
|
||||
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());
|
||||
}
|
||||
@@ -28,14 +28,6 @@
|
||||
#include <istream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
# include <windows.h>
|
||||
typedef boost::asio::windows::stream_handle pipe_end;
|
||||
#elif defined(BOOST_POSIX_API)
|
||||
# include <sys/wait.h>
|
||||
# include <unistd.h>
|
||||
typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
#endif
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
@@ -66,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;
|
||||
@@ -97,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());
|
||||
@@ -109,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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -184,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;
|
||||
}
|
||||
|
||||
135
test/group_wait.cpp
Normal file
135
test/group_wait.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
||||
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
|
||||
// Copyright (c) 2009 Boris Schaeling
|
||||
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
|
||||
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#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>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <boost/process/error.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/args.hpp>
|
||||
#include <boost/process/child.hpp>
|
||||
#include <boost/process/group.hpp>
|
||||
#include <system_error>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <istream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 50 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
bp::group g;
|
||||
|
||||
|
||||
bp::child c1(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "2",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "2",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
BOOST_CHECK(c1.running());
|
||||
BOOST_CHECK(c2.running());
|
||||
|
||||
BOOST_REQUIRE(!ec);
|
||||
BOOST_REQUIRE(c1.in_group(ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_REQUIRE(c2.in_group(ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
g.wait();
|
||||
|
||||
BOOST_CHECK(!c1.running());
|
||||
BOOST_CHECK(!c2.running());
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 150 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
std::error_code ec;
|
||||
bp::group g;
|
||||
|
||||
|
||||
bp::child c1(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "1",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "4",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
BOOST_CHECK(c1.running());
|
||||
BOOST_CHECK(c2.running());
|
||||
|
||||
BOOST_REQUIRE(!ec);
|
||||
BOOST_REQUIRE(c1.in_group());
|
||||
BOOST_REQUIRE(c2.in_group());
|
||||
|
||||
BOOST_CHECK(!g.wait_for(std::chrono::seconds(2), ec));
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, std::to_string(ec.value()) + " == " + ec.message());
|
||||
BOOST_CHECK(!c1.running());
|
||||
BOOST_CHECK(c2.running());
|
||||
|
||||
BOOST_CHECK(g.wait_for(std::chrono::seconds(5), ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, std::to_string(ec.value()) + " == " + ec.message());
|
||||
BOOST_CHECK(!c1.running());
|
||||
BOOST_CHECK(!c2.running());
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
179
test/limit_fd.cpp
Normal file
179
test/limit_fd.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <boost/process/extend.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <system_error>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#include <boost/winapi/get_current_thread.hpp>
|
||||
#include <boost/winapi/get_current_process.hpp>
|
||||
#endif
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
namespace bt = boost::this_process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));};
|
||||
const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);};
|
||||
#else
|
||||
const auto get_handle = [](FILE * f) {return fileno(f);};
|
||||
const auto socket_to_handle = [](int i){ return i;};
|
||||
#endif
|
||||
|
||||
std::error_code ec;
|
||||
auto fd_list = bt::get_handles(ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1);
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
|
||||
BOOST_CHECK_GE(fd_list.size(), 3);
|
||||
BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size());
|
||||
|
||||
bp::pipe p;
|
||||
|
||||
{
|
||||
|
||||
auto fd_list_new = bt::get_handles(ec);
|
||||
BOOST_CHECK_MESSAGE(!ec, ec);
|
||||
BOOST_CHECK_LE(fd_list.size() + 2, fd_list_new.size());
|
||||
fd_list = std::move(fd_list_new);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1);
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
|
||||
p.close();
|
||||
fd_list = bt::get_handles(ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0);
|
||||
|
||||
#if defined( BOOST_WINDOWS_API )
|
||||
std::thread thr([]{});
|
||||
BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
thr.join();
|
||||
#else
|
||||
# if defined(TFD_CLOEXEC) //check timer
|
||||
int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
||||
BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
# if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
|
||||
int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
int dir_fd = ::dirfd(::opendir("."));
|
||||
BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
|
||||
|
||||
boost::asio::io_context ioc;
|
||||
boost::asio::ip::tcp::socket tcp_socket(ioc);
|
||||
boost::asio::ip::udp::socket udp_socket(ioc);
|
||||
bp::async_pipe ap(ioc);
|
||||
|
||||
tcp_socket.open(boost::asio::ip::tcp::v4());
|
||||
udp_socket.open(boost::asio::ip::udp::v4());
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
struct on_setup_t
|
||||
{
|
||||
std::vector<bt::native_handle_type> &res;
|
||||
|
||||
on_setup_t(std::vector<bt::native_handle_type> & res) : res(res) {}
|
||||
template<typename Executor>
|
||||
void operator()(Executor & e)
|
||||
{
|
||||
bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle)
|
||||
{
|
||||
res.push_back(handle);
|
||||
std::cout << "Pushing " << handle << std::endl;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::vector<bt::native_handle_type> res;
|
||||
|
||||
bp::pipe p_in;
|
||||
bp::pipe p_out;
|
||||
|
||||
auto source = p_in.native_source();
|
||||
auto sink = p_out.native_sink();
|
||||
std::error_code ec;
|
||||
|
||||
BOOST_WARN_NE(source, sink); //Sanity check
|
||||
|
||||
const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42",
|
||||
bp::std_in < p_out,
|
||||
bp::std_out > p_in,
|
||||
bp::extend::on_setup(on_setup_t(res)), ec);
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
BOOST_CHECK_EQUAL(ret, 42);
|
||||
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0);
|
||||
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
|
||||
{
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));};
|
||||
#else
|
||||
const auto get_handle = [](FILE * f){return std::to_string(fileno(f));};
|
||||
#endif
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr), EXIT_SUCCESS);
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS);
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE);
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS);
|
||||
|
||||
}
|
||||
@@ -3,5 +3,8 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/system/api_config.hpp>
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#define BOOST_NO_ANSI_APIS 1
|
||||
#endif
|
||||
#include <boost/process.hpp>
|
||||
@@ -11,10 +11,14 @@
|
||||
#include <thread>
|
||||
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
|
||||
using namespace std;
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( pipe_tests );
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(plain, *boost::unit_test::timeout(2))
|
||||
{
|
||||
bp::pipe pipe;
|
||||
@@ -35,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";
|
||||
@@ -230,3 +235,37 @@ BOOST_AUTO_TEST_CASE(coverage, *boost::unit_test::timeout(5))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_close, *boost::unit_test::timeout(5))
|
||||
{
|
||||
bp::pipe p;
|
||||
int i = 1234, j = 0;
|
||||
bp::opstream op{p};
|
||||
bp::ipstream ip{p};
|
||||
p.close();
|
||||
|
||||
op << i << " ";
|
||||
op.close();
|
||||
|
||||
ip >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_close_scope, *boost::unit_test::timeout(5))
|
||||
{
|
||||
bp::pipe p;
|
||||
int i = 1234, j = 0;
|
||||
bp::ipstream ip;
|
||||
|
||||
{
|
||||
bp::opstream op{ip.pipe()};
|
||||
op << i << " ";
|
||||
}
|
||||
ip >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( pipe_tests );
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
@@ -50,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 "},
|
||||
@@ -74,3 +77,4 @@ BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(5))
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <boost/lambda/lambda.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
@@ -57,6 +58,7 @@ int main(int argc, char *argv[])
|
||||
("pwd", bool_switch())
|
||||
("query", value<std::string>())
|
||||
("stdin-to-stdout", bool_switch())
|
||||
("has-handle", value<std::uintptr_t>())
|
||||
#if defined(BOOST_POSIX_API)
|
||||
("posix-echo-one", value<std::vector<std::string> >()->multitoken())
|
||||
("posix-echo-two", value<std::vector<std::string> >()->multitoken());
|
||||
@@ -223,5 +225,15 @@ int main(int argc, char *argv[])
|
||||
std::cout << si.dwFlags << std::endl;
|
||||
}
|
||||
#endif
|
||||
else if (vm.count("has-handle"))
|
||||
{
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto handle = reinterpret_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
|
||||
#else
|
||||
const auto handle = static_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
|
||||
#endif
|
||||
auto all_handles = boost::this_process::get_handles();
|
||||
return (std::find(all_handles.begin(), all_handles.end(), handle) != all_handles.end()) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(5))
|
||||
BOOST_AUTO_TEST_CASE(sync_spawn, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
@@ -83,7 +83,7 @@ struct read_handler
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_io, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_spawn, *boost::unit_test::timeout(2))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ typedef boost::asio::posix::stream_descriptor pipe_end;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(exit_code, *boost::unit_test::timeout(5))
|
||||
BOOST_AUTO_TEST_CASE(system_exit_code, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
@@ -67,15 +67,15 @@ BOOST_AUTO_TEST_CASE(implicit_async_io, *boost::unit_test::timeout(2))
|
||||
|
||||
std::future<std::string> fut;
|
||||
std::error_code ec;
|
||||
bp::system(
|
||||
int res = bp::system(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--echo-stdout", "abc",
|
||||
bp::std_out > fut,
|
||||
ec
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
BOOST_REQUIRE(fut.valid());
|
||||
BOOST_CHECK_EQUAL(res, 0);
|
||||
BOOST_CHECK(boost::starts_with(
|
||||
fut.get(), "abc"));
|
||||
}
|
||||
|
||||
@@ -70,17 +70,19 @@ BOOST_AUTO_TEST_CASE(explicit_async_io_running, *boost::unit_test::timeout(10))
|
||||
std::future<std::string> fut;
|
||||
std::error_code ec;
|
||||
|
||||
ios.post([&]
|
||||
{
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--echo-stdout", "abc",
|
||||
bp::std_out > fut,
|
||||
ios,
|
||||
ec
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
});
|
||||
boost::asio::post(
|
||||
ios.get_executor(),
|
||||
[&] {
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--echo-stdout", "abc",
|
||||
bp::std_out > fut,
|
||||
ios,
|
||||
ec
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
ios.run();
|
||||
|
||||
@@ -22,8 +22,17 @@
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(terminate_set_on_error)
|
||||
BOOST_AUTO_TEST_CASE(terminate_set_on_error, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 50 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
std::error_code ec;
|
||||
bp::child c(
|
||||
@@ -44,10 +53,22 @@ BOOST_AUTO_TEST_CASE(terminate_set_on_error)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_CHECK(!c.running(ec));
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(terminate_throw_on_error)
|
||||
BOOST_AUTO_TEST_CASE(terminate_throw_on_error, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 50 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
@@ -68,4 +89,7 @@ BOOST_AUTO_TEST_CASE(terminate_throw_on_error)
|
||||
c.terminate();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
BOOST_CHECK(!c.running());
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#include <boost/process/error.hpp>
|
||||
#include <boost/process/child.hpp>
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( wait_test );
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_wait, *boost::unit_test::timeout(2))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
@@ -40,7 +43,7 @@ BOOST_AUTO_TEST_CASE(sync_wait, *boost::unit_test::timeout(2))
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(4))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
@@ -50,6 +53,10 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
std::error_code ec;
|
||||
bool called = false;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(3)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
bp::args+={"test", "--wait", "1"},
|
||||
@@ -64,3 +71,5 @@ BOOST_AUTO_TEST_CASE(async_wait, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK(called);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
//#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#include <boost/process/error.hpp>
|
||||
#include <boost/process/child.hpp>
|
||||
@@ -22,12 +22,15 @@
|
||||
#endif
|
||||
|
||||
namespace bp = boost::process;
|
||||
BOOST_AUTO_TEST_SUITE( wait_test);
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_for)
|
||||
{
|
||||
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"},
|
||||
@@ -35,8 +38,13 @@ BOOST_AUTO_TEST_CASE(wait_for)
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
|
||||
BOOST_CHECK(!c.wait_for(std::chrono::milliseconds(200)));
|
||||
BOOST_CHECK( c.wait_for(std::chrono::milliseconds(1000)));
|
||||
|
||||
auto timeout_t = std::chrono::system_clock::now();
|
||||
|
||||
BOOST_CHECK_LE(std::chrono::duration_cast<std::chrono::seconds>(timeout_t - launch_time).count(), 5); //should be less
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_for_ec)
|
||||
@@ -101,4 +109,28 @@ BOOST_AUTO_TEST_CASE(wait_until_ec)
|
||||
BOOST_CHECK( c.wait_until(t2, 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();
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
namespace bp = boost::process;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(args, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(args, *boost::unit_test::timeout(2))
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cmd, *boost::unit_test::timeout(2))
|
||||
BOOST_AUTO_TEST_CASE(wcmd, *boost::unit_test::timeout(2))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user