2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 16:52:14 +00:00

Compare commits

...

309 Commits

Author SHA1 Message Date
Klemens Morgenstern
0341e08297 Merge pull request #212 from klemens-morgenstern/develop
Master update
2019-12-29 04:37:11 +08:00
Klemens David Morgenstern
5853345715 trying to fix appveyor to upload to report.ci 2019-12-29 04:16:44 +08:00
Klemens David Morgenstern
41f8b1cf00 removed sigtimedwait for group_wait 2019-12-29 03:39:32 +08:00
Klemens David Morgenstern
56ae00c7a4 typo fix 2019-12-29 00:48:51 +08:00
Klemens David Morgenstern
f58882c956 small travis fixes, hopefully fixing the build 2019-12-29 00:42:21 +08:00
Klemens David Morgenstern
3f14ebc755 changed report script 2019-12-29 00:04:26 +08:00
Klemens David Morgenstern
6a4d2ff721 added typedef executor_type; 2019-11-22 14:03:22 +08:00
Klemens David Morgenstern
6bf37ea8e8 Merge branch 'develop' of github.com:boostorg/process into develop
# Conflicts:
#	test/async_pipe.cpp
2019-11-22 13:11:17 +08:00
Klemens Morgenstern
ad38cdfada Merge pull request #106 from treesoft-de/bugfix
fix double CloseHandle after move of file_descriptor on windows
2019-11-22 13:08:17 +08:00
Klemens Morgenstern
167ee79fa9 Merge pull request #102 from res2k/develop
Miscellaneous fixes related to POSIX environments
2019-11-22 13:07:52 +08:00
Klemens David Morgenstern
6b83d0b9dd closes boostorg/process#97 2019-11-22 13:07:08 +08:00
Klemens Morgenstern
16d16d40be Merge pull request #90 from BikingGlobetrotter/develop
fixed hidden local variable
2019-11-22 13:06:12 +08:00
Klemens David Morgenstern
f5f0866745 closes boostorg/process#103 2019-11-22 13:04:59 +08:00
Klemens Morgenstern
408cff1997 Merge pull request #70 from thomassuckow/patch-1
Guard against EINTR in basic_pipe
2019-11-22 13:01:52 +08:00
Klemens David Morgenstern
09faec4732 closes boostorg/process#104 2019-11-22 12:59:05 +08:00
Klemens Morgenstern
5ab43529b7 Merge pull request #209 from killerbot242/changes3
only carry out when buffer size is > 0
2019-11-22 12:52:41 +08:00
Klemens Morgenstern
97f5b3c049 Merge pull request #211 from Instand/patch
Fix basic_ipstream is_open compile error.
2019-11-22 12:51:58 +08:00
Klemens David Morgenstern
6dd3e0bdb4 typo fix, closes klemens-morgenstern/boost-process#210 2019-11-22 12:50:40 +08:00
Arew
eba5cb7be2 Fix basic_ipstream is_open compile error. 2019-11-21 17:57:24 +03:00
Klemens David Morgenstern
f4c51bcd5a closes klemens-morgenstern/boost-process#107 2019-10-26 11:04:42 +07:00
Raymond Häb
410c0d592e fix double CloseHandle after move of file_descriptor on windows 2019-10-18 10:08:21 +02:00
Frank Richter
40df7899b2 test: Add test for direct conversion of wnative_environment to native_environment 2019-09-11 11:31:19 +02:00
Frank Richter
51083a8fa8 posix environment: Fix assignment of wnative_environment to wenvironment
Using 'environ' as the native_handle for a wide environment doesn't work,
as environ returns char**. Instead, use data we already converted.
2019-09-11 11:23:36 +02:00
Frank Richter
fe3cb0efc7 posix environment: Avoid using front() on empty string
That is undefined behaviour. Pushing a single NUL makes the string
non-empty but still gives us "".
2019-09-11 11:20:24 +02:00
Frank Richter
e0dd3b9658 test: Build fix for async_pipe 2019-09-11 11:01:45 +02:00
Lieven de Cock
d7d84f3952 only carry out when buffer size is > 0 2019-08-19 12:11:34 +02:00
Klemens Morgenstern
6ccce9104a Merge pull request #91 from orgads/patch-1
Fix GCC9 warning
2019-06-26 20:30:18 +10:00
Orgad Shaneh
984c0c5b71 Fix GCC9 warning
warning: moving a local object in a return statement prevents copy elision
2019-06-26 13:11:49 +03:00
michael
43523fcf8b fixed hidden local variable 2019-06-21 13:39:54 +02:00
Klemens Morgenstern
cf7ad36438 Merge pull request #87 from OBorce/feature/fix-stream-move-assignments
fix move assignment operators for basic_[io]pstream
2019-06-06 11:30:33 +07:00
boris
ca994c1972 fix move assignment operators for basic_[io]pstream 2019-06-05 00:19:53 +02:00
Klemens David Morgenstern
0a554c92b5 Merge branch 'develop' of github.com:boostorg/process into develop 2019-05-18 11:11:20 +07:00
Klemens Morgenstern
fa81cecffc Merge pull request #196 from klemens-morgenstern/limit_fd
Limit fd
2019-05-18 11:07:55 +07:00
Klemens David Morgenstern
977b76f6aa Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2019-05-15 12:31:16 +07:00
Klemens Morgenstern
fc1acb82d9 Merge pull request #199 from klemens-morgenstern/stream_close
added opstream::close, closes klemens-morgenstern/boost-process#198
2019-05-14 22:04:21 +07:00
Klemens David Morgenstern
db7af9f87d doc addition 2019-05-14 13:22:05 +07:00
Klemens Morgenstern
a350cc346b Merge pull request #203 from klemens-morgenstern/win_pipe_unlimited_instances
Closes boostorg/process#84 and boostorg/process#83
2019-05-14 12:41:29 +07:00
Klemens David Morgenstern
9fa86d3d65 removed call to 'close_sink' 2019-05-14 12:15:57 +07:00
Klemens David Morgenstern
e426f2bfac remove ill-formed test & minor fixes 2019-05-14 11:57:20 +07:00
Klemens David Morgenstern
6b173117aa Merge branch 'develop' into stream_close 2019-05-14 10:45:53 +07:00
Klemens David Morgenstern
ecbc93408f Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-14 10:45:16 +07:00
Klemens David Morgenstern
6ba9a48d15 Merge branch 'develop' into new_wait_for_test 2019-05-14 10:44:40 +07:00
Klemens David Morgenstern
519c0a636a Merge branch 'develop' into limit_fd 2019-05-14 10:44:08 +07:00
Klemens David Morgenstern
82195c61af Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop
# Conflicts:
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-14 00:41:41 +07:00
Klemens David Morgenstern
c604e3a20e switched to a wait/sleep version for OSX - should work fine for groups 2019-05-14 00:36:24 +07:00
Klemens Morgenstern
b27d0170ba Typo fix 2019-05-13 23:50:31 +07:00
Klemens David Morgenstern
98fd4eecf0 small reordering of code 2019-05-13 20:43:03 +07:00
Klemens David Morgenstern
3799315ce7 reverted to pipes for OSX 2019-05-13 20:11:17 +07:00
Klemens David Morgenstern
b9431ba492 fixing signal workaround 2019-05-13 10:30:28 +07:00
Klemens David Morgenstern
c0dca35615 added wrongfully removed build script options 2019-05-13 00:28:10 +07:00
Klemens David Morgenstern
790d79db9c Merge branch 'develop' into limit_fd 2019-05-13 00:23:52 +07:00
Klemens David Morgenstern
5de0a795d1 fixed wait_for_exit 2019-05-13 00:22:46 +07:00
Klemens David Morgenstern
cbaa913e3d Merge branch 'develop' into limit_fd
# Conflicts:
#	include/boost/process/detail/posix/pipe_out.hpp
#	test/Jamfile.jam
2019-05-12 17:43:40 +07:00
Klemens David Morgenstern
5786162fb5 Merge branch 'develop' into new_wait_for_test 2019-05-12 17:42:08 +07:00
Klemens David Morgenstern
78c44dd560 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-12 17:40:54 +07:00
Klemens David Morgenstern
23ff67d83d added space for pipe close test 2019-05-12 17:37:35 +07:00
Klemens David Morgenstern
476c6ccd95 Merge branch 'develop' into stream_close 2019-05-12 17:13:36 +07:00
Klemens David Morgenstern
28126b3432 osx fix 2019-05-12 17:02:25 +07:00
Klemens David Morgenstern
ed8388d091 actual fix 2019-05-12 16:55:45 +07:00
Klemens David Morgenstern
43c402a5da test logic fix 2019-05-12 16:53:54 +07:00
Klemens David Morgenstern
9ff2f6f3ef still trying for circleci 2019-05-10 16:56:11 +07:00
Klemens David Morgenstern
b2a0fadaca Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	.travis.yml
2019-05-10 16:38:23 +07:00
Klemens David Morgenstern
3c4057204e hopefully fixed wait_for for osx 2019-05-10 16:34:51 +07:00
Klemens David Morgenstern
717ac47510 further investigation of circlci error 2019-05-10 12:21:37 +07:00
Klemens David Morgenstern
4f6f4eb391 updated for circle 2019-05-10 12:00:43 +07:00
Klemens David Morgenstern
99e04036c7 removed report-ci from circle-ci 2019-05-10 00:37:00 +07:00
Klemens David Morgenstern
519e339365 added more checks 2019-05-09 23:05:09 +07:00
Klemens David Morgenstern
d2c930470f removed && false 2019-05-09 17:08:58 +07:00
Klemens David Morgenstern
d5709ae747 trying to fix wait_for 2019-05-09 16:35:58 +07:00
Klemens David Morgenstern
d2ab81b1b9 Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-09 12:20:20 +07:00
Klemens David Morgenstern
885557fe01 still trying to make wait_for work 2019-05-09 12:19:26 +07:00
Klemens David Morgenstern
b5b758f89a Merge branch 'develop' into stream_close 2019-05-09 11:29:31 +07:00
Klemens David Morgenstern
2a954eb809 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-09 11:24:14 +07:00
Klemens David Morgenstern
0edff5449a Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
2019-05-08 16:53:45 +07:00
Klemens David Morgenstern
417ea77f2f changed SIGTERM to SIGKILL 2019-05-08 16:50:49 +07:00
Klemens David Morgenstern
9e3fdc9669 SIGSEV fix 2019-05-08 16:44:38 +07:00
Klemens David Morgenstern
66c2867371 removed travis token 2019-05-08 16:30:39 +07:00
Klemens David Morgenstern
caa7b2fcc8 two minor fixes 2019-05-08 16:28:54 +07:00
Klemens David Morgenstern
6263e74bcd Merge branch 'develop' into limit_fd 2019-05-07 12:16:53 +07:00
Klemens David Morgenstern
faae08ee64 replaced 1 with ::boost::winapi::HANDLE_FLAG_INHERIT_ 2019-05-07 12:16:32 +07:00
Klemens David Morgenstern
2314e19f12 restructured osx wait
Check if it solves klemens-morgenstern/boost-process#200
2019-05-07 12:01:04 +07:00
Klemens David Morgenstern
cfd0fc055c fixed overflow func 2019-05-07 11:48:16 +07:00
Klemens David Morgenstern
849b5d0f30 Closes boostorg/process#84 and boostorg/process#83 2019-05-07 10:59:50 +07:00
Klemens Morgenstern
d13df2a194 Merge pull request #82 from Parean/develop
should close klemens-morgenstern/boost-process#201
2019-05-03 21:07:53 +07:00
Denis Smirnov
86fc3b0b4d should close klemens-morgenstern/boost-process#201 2019-05-03 19:07:17 +07:00
Klemens David Morgenstern
4733ca719f typo fix 2019-04-25 22:40:13 +07:00
Klemens David Morgenstern
296f12eb64 RAII issues & resetting flags on windwos handles 2019-04-25 22:36:30 +07:00
Klemens David Morgenstern
96d3470e37 added this-> before clear() 2019-04-25 22:06:23 +07:00
Klemens David Morgenstern
2265c98d81 added missing ; 2019-04-25 20:24:42 +07:00
Klemens David Morgenstern
060e5c2526 fixed name lookup 2019-04-25 15:15:29 +07:00
Klemens David Morgenstern
b4894807f1 added opstream::close, closes klemens-morgenstern/boost-process#198 2019-04-24 18:24:44 +07:00
Klemens David Morgenstern
2aa5e1461c Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	test/Jamfile.jam
2019-04-20 16:41:31 +08:00
Klemens Morgenstern
61fa15fa48 Merge pull request #197 from toonetown/fix-wait_for-fork-macOS
Use SIGTERM instead of `-15`.
2019-04-20 15:39:54 +07:00
Nathan Toone
92508e06a1 Use SIGTERM instead of -15. 2019-04-19 23:44:54 -06:00
Klemens David Morgenstern
e85f0d0816 Closes klemens-morgenstern/boost-process#139 2019-04-10 12:18:02 +08:00
Klemens David Morgenstern
5e90c8de9b fixed up tests 2019-04-10 10:09:32 +08:00
Klemens David Morgenstern
a486a25a07 appveyor minor fix, pipe_out naming fix & limit_fd small sanity check because of weird osx bug 2019-04-10 01:33:21 +08:00
Klemens David Morgenstern
f8c0dd4da5 prototype for limit_fd 2019-04-09 23:39:43 +08:00
Klemens David Morgenstern
ee6870bfbc added used_handles & limit_fd for windows 2019-04-09 19:16:55 +08:00
Klemens David Morgenstern
0422b6bfb8 added posix get_handle function 2019-04-09 15:40:26 +08:00
Klemens David Morgenstern
7fc41b2815 added windows handles functionality 2019-04-09 14:31:50 +08:00
Klemens David Morgenstern
a49f1f6e2d Started on limit-fd functionality 2019-04-08 11:11:55 +08:00
Klemens David Morgenstern
af54484bc2 sed typo fix 2019-04-07 21:40:28 +08:00
Klemens David Morgenstern
ebcb30e4bd reduced amount of threads for build 2019-04-07 13:31:46 +08:00
Klemens David Morgenstern
5371c9813b typo fix 2019-04-07 13:07:11 +08:00
Klemens David Morgenstern
9a833c610d trying to fix the exit code 2019-04-07 12:53:02 +08:00
Klemens David Morgenstern
c99ebfee7a flipping typos 2019-04-07 12:34:37 +08:00
Klemens David Morgenstern
34861366e0 added sed for annotations 2019-04-07 12:19:17 +08:00
Klemens David Morgenstern
e6722c452c faulty log name fix (attempt) 2019-04-07 11:47:18 +08:00
Klemens David Morgenstern
b1e38842fb renamed the no-valgrind log 2019-04-07 03:15:38 +08:00
Klemens David Morgenstern
bcc9826e67 still doing the ci thing here 2019-04-07 03:09:17 +08:00
Klemens David Morgenstern
b6c6753b87 added valgrind to circle Ci 2019-04-07 02:57:14 +08:00
Klemens David Morgenstern
0d008a88fc dammit circle 2019-04-07 02:49:14 +08:00
Klemens David Morgenstern
f92ec53968 circle ci example job fix 2019-04-07 02:36:14 +08:00
Klemens David Morgenstern
00a87d0a67 report-ci update for circleci 2019-04-07 02:29:52 +08:00
Klemens David Morgenstern
462334639c typo fix 2019-04-07 02:08:31 +08:00
Klemens David Morgenstern
7129182044 ci fixes - the similtaneous wait doen't work on osx 2019-04-07 02:03:08 +08:00
Klemens David Morgenstern
03fbed44ad typo fix 2019-04-07 01:23:10 +08:00
Klemens David Morgenstern
0277c4fcec CI fixes 2019-04-07 01:19:19 +08:00
Klemens David Morgenstern
4bc1ae6ff8 added async fix & fixed dangling develop workaroudn for async_pipe copy 2019-04-07 00:21:27 +08:00
Klemens David Morgenstern
266c4503aa Merge branch 'develop' into asio_no_deprecated 2019-04-06 23:58:47 +08:00
Klemens David Morgenstern
80479b6b70 removed errornous global variable 2019-04-06 23:58:27 +08:00
Klemens David Morgenstern
823a346a08 added another exit_code test 2019-04-06 23:53:11 +08:00
Klemens David Morgenstern
8f3a7b9c63 Merge branch 'develop' into asio_no_deprecated 2019-04-06 22:35:45 +08:00
Klemens David Morgenstern
2ebcc07892 Merge branch 'circleci' into develop 2019-04-06 22:33:22 +08:00
Klemens Morgenstern
a673cd9643 Update config.yml 2019-04-06 22:25:52 +08:00
Klemens Morgenstern
12971db132 Update config.yml 2019-04-06 22:19:11 +08:00
Klemens Morgenstern
94be279992 Update config.yml 2019-04-06 22:15:38 +08:00
Klemens David Morgenstern
12cded5995 switched test to strand 2019-04-06 22:15:12 +08:00
Klemens David Morgenstern
0c16a0e5b3 set params back to support report.ci 2019-04-06 22:11:04 +08:00
Klemens Morgenstern
e4a6fde7b9 Update config.yml 2019-04-06 18:28:40 +08:00
Klemens David Morgenstern
92d2bebaaf added test and fixed #189 and #157 as well as #93 2019-04-06 18:25:08 +08:00
Klemens David Morgenstern
7e7a8cbc1d fixed #194 2019-04-06 17:29:52 +08:00
Klemens Morgenstern
4f767f4bfe Update config.yml 2019-04-06 17:25:03 +08:00
Klemens Morgenstern
63f714ae2f Update config.yml 2019-04-06 16:54:07 +08:00
Klemens Morgenstern
1b3b9b707c Update config.yml 2019-04-06 16:41:44 +08:00
Klemens Morgenstern
f64bc8a6d4 Update config.yml 2019-04-06 16:34:26 +08:00
Klemens Morgenstern
102834130d Update config.yml 2019-04-06 16:28:48 +08:00
Klemens Morgenstern
fdc6a11cbc Update config.yml 2019-04-06 16:09:16 +08:00
Klemens Morgenstern
19b20f55ce Update config.yml 2019-04-06 16:04:28 +08:00
Klemens David Morgenstern
60d072ce46 Merge branch 'develop' into circleci 2019-04-06 15:29:22 +08:00
Klemens David Morgenstern
34f05b9276 Merge branch 'develop' into asio_no_deprecated 2019-04-06 15:22:10 +08:00
Klemens David Morgenstern
1b476b0430 Closes #65 2019-04-06 15:21:52 +08:00
Klemens David Morgenstern
b294710e60 fixed self-pipe issue 2019-04-06 14:42:08 +08:00
Klemens David Morgenstern
f8cd325d1b trying circleci 2019-04-05 15:13:14 +08:00
Klemens David Morgenstern
2a6c23e173 typo fix 2019-04-04 18:11:33 +08:00
Klemens David Morgenstern
d7768b7221 Merge branch 'develop' into asio_no_deprecated 2019-04-04 17:57:28 +08:00
Klemens David Morgenstern
b8821eac57 should close #78 2019-03-30 23:14:25 +08:00
Klemens David Morgenstern
d20b64cf37 removed unnecessary int to remove warning 2019-03-29 01:39:00 +08:00
Klemens David Morgenstern
f0ddd6ca29 typo & structure fix 2019-03-29 00:57:11 +08:00
Klemens David Morgenstern
574d9e09d6 should close #193 2019-03-29 00:32:12 +08:00
Klemens Morgenstern
7085a50f36 Merge pull request #77 from SimonEbner/develop
Fix leaking pipe
2019-03-23 00:01:27 +08:00
Klemens Morgenstern
0485459da2 Merge pull request #76 from orgads/patch-1
Fix signed/unsigned comparison
2019-03-22 23:56:13 +08:00
Simon Ebner
318439af2e Fix Issue 62
Fixes a leaking pipe. See https://github.com/boostorg/process/issues/62
2019-03-22 15:27:35 +01:00
Orgad Shaneh
4d52ff362f Fix signed/unsigned comparison 2019-03-21 23:39:27 +02:00
Klemens David Morgenstern
8d48d9cbfa dumb workaround -> fix report.ci detection! 2019-03-21 16:21:40 +08:00
Klemens David Morgenstern
f25c31c847 added test by @grives as recommended in #69 2019-03-21 12:51:32 +08:00
Klemens David Morgenstern
3eacae5e38 Merge branch 'develop' 2019-03-04 12:16:07 +08:00
Klemens David Morgenstern
14b294c10c Merge branch 'develop' of github.com:boostorg/process into develop 2019-03-04 12:14:56 +08:00
Klemens Morgenstern
881da4f9e2 Merge pull request #191 from klemens-morgenstern/develop
Update master
2019-02-28 12:44:50 +08:00
Klemens Morgenstern
2758e8d438 Merge pull request #190 from klemens-morgenstern/report-ci
Report ci
2019-02-27 12:44:12 +08:00
Klemens David Morgenstern
b5a6b4b945 updated readme 2019-02-25 12:59:56 +08:00
Klemens David Morgenstern
ac94f81b77 renamed wait test suite 2019-02-25 09:42:45 +08:00
Klemens David Morgenstern
06747d7dd3 added test suites 2019-02-25 00:04:09 +08:00
Klemens David Morgenstern
fb9b9215e0 another windows fix 2019-02-24 22:37:13 +08:00
Klemens David Morgenstern
eed0505f6e trying to fix the windows build 2019-02-24 21:26:34 +08:00
Klemens David Morgenstern
8f6aa8bcff added root-dir parameter 2019-02-24 17:14:28 +08:00
Klemens David Morgenstern
6f9cabbd04 trying to fix the build scripts 2019-02-24 13:10:03 +08:00
Klemens David Morgenstern
355d20faf3 update async pipe executor assignment 2019-02-24 00:51:44 +08:00
Klemens David Morgenstern
15c4a64a81 updated asio fwd 2019-02-23 23:38:20 +08:00
Klemens David Morgenstern
49ec65a735 updated yamls 2019-02-23 22:29:04 +08:00
Klemens David Morgenstern
7cdea96caf added report-ci 2019-02-23 20:51:24 +08:00
Klemens Morgenstern
8bf7da3b6b Merge pull request #71 from killerbot242/changes
fix warning due to extra semi colon
2019-02-03 21:19:45 +08:00
Lieven de Cock
c1ad9d1227 fix warning due to extra semi colon
In file included from /home/ldco/boost/include/boost/process/detail/on_exit.hpp:12:0,
                 from /home/ldco/boost/include/boost/process/async.hpp:33,
                 from /home/ldco/boost/include/boost/process.hpp:23,
                 from /home/ldco/Projects/Teaching/Teaching/Boost/Process/Process1/src/main.cpp:7:
/home/ldco/boost/include/boost/process/detail/posix/on_exit.hpp:30:6: warning: extra ‘;’ [-Wpedantic]
     };
      ^
In file included from /home/ldco/boost/include/boost/process/async.hpp:42:0,
                 from /home/ldco/boost/include/boost/process.hpp:23,
                 from /home/ldco/Projects/Teaching/Teaching/Boost/Process/Process1/src/main.cpp:7:
/home/ldco/boost/include/boost/process/detail/posix/io_context_ref.hpp:67:6: warning: extra ‘;’ [-Wpedantic]
     };
      ^
2019-01-29 12:57:43 +01:00
Thomas Suckow
1b6ccb6d39 Guard against EINTR in basic_pipe
Based on the code from executor.hpp, a signal can cause invoking a child process to fail because reading/writing the pipe was interrupted.
2019-01-22 09:22:52 -08:00
Klemens David Morgenstern
e2e2b5ddb2 Merge branch 'develop' 2019-01-08 15:02:41 +08:00
Klemens David Morgenstern
b4584dd0b0 Merge branch 'develop' of github.com:boostorg/process into develop 2019-01-08 15:02:17 +08:00
Klemens Morgenstern
55e5d178bd Merge pull request #67 from amensel/develop
Use the non-deprecated ASIO APIs.
2019-01-07 17:30:48 +07:00
Klemens David Morgenstern
f778702257 closes #188 2019-01-07 17:12:58 +07:00
Klemens David Morgenstern
b4ff07cfcb fixed BOOST_NO_ANSI_APIS issue in basic_pipe, closed #26 2019-01-07 17:03:00 +07:00
Adam Mensel
d43858078f Use the non-deprecated function name. 2019-01-04 15:32:38 -07:00
Adam Mensel
f2cf918be5 Missed one post call. 2019-01-04 15:24:28 -07:00
Adam Mensel
54dd027a64 Use the non-deprecated ASIO APIs. 2019-01-04 11:57:47 -07:00
Klemens David Morgenstern
1ea894ab30 Closes #187 2018-12-15 11:37:51 +07:00
Klemens David Morgenstern
92c45f4b61 testing for actual timing 2018-12-14 23:33:26 +07:00
Klemens Morgenstern
03cba8f5bd Merge pull request #185 from klemens-morgenstern/develop
Develop update
2018-12-14 12:17:46 +07:00
Klemens David Morgenstern
97f08ba0b3 Merge remote-tracking branch 'remotes/origin/master' into develop
# Conflicts:
#	doc/tutorial.qbk
2018-12-14 12:17:17 +07:00
Klemens David Morgenstern
c843815c6c added multithreading for osx only 2018-12-14 11:31:08 +07:00
Klemens David Morgenstern
b5785b3370 added -j8 to travis 2018-12-13 14:17:54 +07:00
Klemens David Morgenstern
a6e5a5a619 typoe fix 2018-12-13 11:45:48 +07:00
Klemens David Morgenstern
2d627f633b added group_wait 2018-12-12 00:19:14 +07:00
Klemens David Morgenstern
ef486764ac added check if forked proc is running 2018-12-11 23:13:20 +07:00
Klemens David Morgenstern
fce3962376 WNOHANG test 2018-12-11 21:58:17 +07:00
Klemens David Morgenstern
5a40a0ee9c typo fix 2018-12-11 18:24:25 +07:00
Klemens David Morgenstern
4bf795922d trying nanosleep 2018-12-11 18:05:24 +07:00
Klemens David Morgenstern
1646b240c7 trying to fix wait_for 2018-12-11 17:19:37 +07:00
Klemens David Morgenstern
d4f7bfaa6c double timeout for terminate... 2018-12-11 16:17:29 +07:00
Klemens David Morgenstern
bac06d2bc7 added timeout to terminate 2018-12-11 15:38:41 +07:00
Klemens David Morgenstern
579499d9e8 group_wait 2018-12-11 15:06:08 +07:00
Klemens David Morgenstern
b8c46bfc47 trying explicit alarm for osx 2018-12-11 14:36:04 +07:00
Klemens David Morgenstern
d234ce4f63 bumped up the log_level 2018-12-11 14:16:48 +07:00
Klemens David Morgenstern
1be807e748 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-12-11 13:52:09 +07:00
Klemens David Morgenstern
f4db2613eb disabling valgrind for osx 2018-12-11 13:50:10 +07:00
Klemens Morgenstern
bae6098e91 Merge pull request #186 from jasonmccampbell/native-exit-code
Allow access to the uninterpreted exit code
2018-12-11 10:15:50 +07:00
Jason McCampbell
c40153fab1 Allow access to the uninterpreted exit code to allow calls to
distinugish between exit codes and exit due to signals (POSIX)
2018-12-11 02:41:27 +00:00
Klemens David Morgenstern
e5eec4a5dc updated develop thingy 2018-12-11 01:02:28 +07:00
Klemens David Morgenstern
887bb8c482 removed notify_fork again 2018-12-11 00:41:47 +07:00
Klemens David Morgenstern
0642bf5e70 added had SIGALARM For apple 2018-12-11 00:15:17 +07:00
Klemens David Morgenstern
513208a117 travis fix 2018-12-10 23:31:57 +07:00
Klemens David Morgenstern
49247f0a83 revamped async 2018-12-10 22:42:05 +07:00
Klemens David Morgenstern
ccb7dd482a implemented badges for OSX / Linux 2018-12-10 17:23:12 +07:00
Klemens David Morgenstern
08f40ff46b trying BOOST_TEST_CATCH_SYSTEM_ERRORS=no for osx 2018-12-10 15:21:03 +07:00
Klemens David Morgenstern
956f7b5fdf updated group_wait to remove wrong condition 2018-12-10 15:06:26 +07:00
Klemens David Morgenstern
40d0f8c401 trying to fix group wait 2018-11-20 20:40:44 +07:00
Klemens David Morgenstern
cbc7580fcd updated ansi_apps by api-guard 2018-11-20 20:15:46 +07:00
Klemens David Morgenstern
f96dfd6e8f typo fix 2018-11-15 15:55:47 +07:00
Klemens David Morgenstern
ea24ebaf92 added notify fork back into io_context_ref 2018-11-15 15:07:34 +07:00
Klemens David Morgenstern
504e760760 added timeout to wait-test 2018-11-15 14:30:11 +07:00
Klemens David Morgenstern
c5e7cfb4f5 added called-bool & time out for exit_code test 2018-11-15 13:53:01 +07:00
Klemens David Morgenstern
9904e3a5e8 removed BOOST_TEST_IGNORE_SIGCHLD from test 2018-11-15 12:54:48 +07:00
Klemens David Morgenstern
191673b049 updated waitpid functionality 2018-11-15 11:51:33 +07:00
Klemens David Morgenstern
3a6d11f9b8 damn typos 2018-11-15 11:22:43 +07:00
Klemens David Morgenstern
e022ad3742 typo fix, again 2018-11-15 11:06:09 +07:00
Klemens David Morgenstern
16d9350992 typo fix 2018-11-15 10:51:29 +07:00
Klemens David Morgenstern
5e1d7b6901 updated sigchld_service to check first check if the child is actually running 2018-11-15 10:35:52 +07:00
Klemens David Morgenstern
82d4cef182 trying to queue process creation 2018-11-13 19:06:03 +07:00
Klemens David Morgenstern
5a112ea4bb Merge branch 'develop' into travis_osx
# Conflicts:
#	.travis.yml
#	include/boost/process/detail/posix/wait_for_exit.hpp
#	include/boost/process/detail/posix/wait_group.hpp
2018-11-13 17:47:41 +07:00
Klemens David Morgenstern
b058e1cadf trying to remove BOOST_TEST_IGNORE_SIGCHLD 2018-11-13 16:57:06 +07:00
Klemens David Morgenstern
a8b28ef262 fixed timeout values 2018-11-13 14:57:29 +07:00
Klemens David Morgenstern
080f3fb074 checking if it's just an asio timing problem 2018-11-13 14:37:57 +07:00
Klemens David Morgenstern
997f10c7e9 fixed wrong timeout values 2018-11-13 14:20:39 +07:00
Klemens David Morgenstern
4ced4d0933 typo fix in wait functions 2018-11-13 13:52:17 +07:00
Klemens David Morgenstern
eafe8e327a removed auto from lambdas 2018-11-13 13:49:28 +07:00
Klemens David Morgenstern
a4c89a3dec added timeouts to async test 2018-11-13 13:30:14 +07:00
Klemens David Morgenstern
902390d57a still trying for the osx on travis 2018-11-13 11:56:38 +07:00
Klemens David Morgenstern
5c55590922 trying llvm now 2018-11-12 20:23:46 +07:00
Klemens David Morgenstern
0485932309 typo fix 2018-11-12 19:53:55 +07:00
Klemens David Morgenstern
e1a3aded4e added . output for filter for section warning 2018-11-12 19:32:21 +07:00
Klemens David Morgenstern
5b2d5c76c8 nsleep -> nanosleep 2018-11-12 18:59:18 +07:00
Klemens David Morgenstern
2c3c9e84a5 added include of unistd.h 2018-11-12 18:40:48 +07:00
Klemens David Morgenstern
ad90ca6366 CI fixes and attempt on new wait_for mechanic
Squashed commit of the following:

commit 88952f0ab2
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 18:27:04 2018 +0700

    rolling back the --coverage option

commit 29cd54ea8c
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 18:12:33 2018 +0700

    another attempt

commit c2ee6da367
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 17:54:53 2018 +0700

    trying to get the sanitization to work here

commit 317801ca5e
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 17:10:36 2018 +0700

    added -lprofile_rt

commit 3923da14f7
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 16:56:32 2018 +0700

    attempting new timed wait for OSX

commit 5aa691cc3a
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 16:32:11 2018 +0700

    adding valgrind to build

commit 04712d57f4
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 16:09:34 2018 +0700

    changin pipe mode

commit ed4c861e78
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Mon Nov 12 15:37:08 2018 +0700

    trying to filter section warnings
2018-11-12 18:39:31 +07:00
Klemens David Morgenstern
88952f0ab2 rolling back the --coverage option 2018-11-12 18:27:04 +07:00
Klemens David Morgenstern
29cd54ea8c another attempt 2018-11-12 18:12:33 +07:00
Klemens David Morgenstern
c2ee6da367 trying to get the sanitization to work here 2018-11-12 17:54:53 +07:00
Klemens David Morgenstern
317801ca5e added -lprofile_rt 2018-11-12 17:10:36 +07:00
Klemens David Morgenstern
3923da14f7 attempting new timed wait for OSX 2018-11-12 16:56:32 +07:00
Klemens David Morgenstern
5aa691cc3a adding valgrind to build 2018-11-12 16:32:11 +07:00
Klemens David Morgenstern
04712d57f4 changin pipe mode 2018-11-12 16:09:34 +07:00
Klemens David Morgenstern
ed4c861e78 trying to filter section warnings 2018-11-12 15:37:08 +07:00
Klemens David Morgenstern
587eaa8054 yaml file fix 2018-11-12 14:55:38 +07:00
Klemens David Morgenstern
78b5ea1f6b checking the sigtimedwait macro --> test should fail on OSX & work on Linux 2018-11-12 14:48:58 +07:00
Klemens David Morgenstern
6412aa3ece trying badging properly 2018-11-12 14:34:50 +07:00
Klemens David Morgenstern
68a3ba0c41 Merge branch 'develop' into travis_osx 2018-11-12 14:29:36 +07:00
Klemens David Morgenstern
ecaba9efc1 typo fix 2018-11-12 14:04:50 +07:00
Klemens David Morgenstern
fb682944d9 trying to install gcc-5 for osx 2018-11-12 13:49:55 +07:00
Klemens David Morgenstern
9c60e4987c added matrix links to readme 2018-11-09 11:06:17 +07:00
Klemens David Morgenstern
59b361fb46 trying to use osx 2018-11-09 11:01:35 +07:00
Klemens David Morgenstern
f3b2c0a67e typo fixes 2018-10-26 11:35:33 +07:00
Klemens David Morgenstern
69c04d5e29 updated doc to say io_context instead of io_service 2018-10-26 10:55:28 +07:00
Klemens David Morgenstern
78f4115a32 Implement move operations for streams, closes #173 2018-10-26 10:53:47 +07:00
Klemens David Morgenstern
9b1b83f5e7 Merge branch 'develop' of github.com:boostorg/process into develop 2018-10-26 10:38:46 +07:00
Klemens Morgenstern
bd859e98d3 Merge pull request #54 from tnixeu/develop
removes boost::asio:: from example in doc
2018-10-26 14:35:06 +11:00
tnixeu
d7accdcf0c fixes some string replacements issues in doc. Some replacements caused wrong namespaces. 2018-10-20 13:04:17 +02:00
tnixeu
d159fea7b8 removes boost::asio:: from example in doc 2018-10-20 11:50:45 +02:00
Klemens David Morgenstern
b5b91d578d Merge branch 'develop' of github.com:boostorg/process into develop 2018-10-01 10:01:40 +07:00
Klemens Morgenstern
498055bc8d Merge pull request #52 from pivotal-jbarrett/isrunning-solaris
Changes still_active constant to support Solaris.
2018-09-28 00:04:08 +07:00
Jacob Barrett
502169a5ad Changes still_active constant to support Solaris.
The use of the WIFSTOPPED bits did not go far enough on Solaris. It
is expected to also have a non-zero value in the next bytes. This
new value is tested be valid on Linux, MacOS and Solaris.

Fixes #51
2018-09-27 07:36:03 -07:00
Klemens David Morgenstern
42fbbbd8a6 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-09-27 10:02:01 +07:00
Klemens David Morgenstern
dd0b26de4c closes #180 2018-09-27 10:00:29 +07:00
Klemens Morgenstern
10665bfaff Merge pull request #181 from krwalker/fix-vc-c4458-warnings
Fix warnings: decl. of 'x' hides class member
2018-09-27 09:55:41 +07:00
K. R. Walker
2576ed166f Fix warnings: decl. of 'x' hides class member
Address C4458 warnings when compiled with Visual C++
2018-09-26 16:54:46 -06:00
Klemens David Morgenstern
9ad7413189 Merge branch 'develop' 2018-09-26 15:18:30 +07:00
Klemens David Morgenstern
9cd405a66f Squashed commit of the following:
commit 4107154e8056ec3460ccd70dea6b919e8c71fc59
Author: Klemens David Morgenstern <klemens.morgenstern@gmx.net>
Date:   Wed Sep 26 14:42:00 2018 +0700

    trying to fix the coverage reporting
2018-09-26 15:17:56 +07:00
Klemens David Morgenstern
fc6773d7d3 trying to improve coverage 2018-09-26 14:03:13 +07:00
Klemens David Morgenstern
c3b707b709 Merge branch 'master' of github.com:boostorg/process 2018-09-26 13:53:10 +07:00
Klemens Morgenstern
57e9dfb705 Merge pull request #179 from klemens-morgenstern/develop
Update for master
2018-09-26 13:50:47 +07:00
Klemens David Morgenstern
4fd8887601 fixed group wait-for on windows 2018-09-26 11:50:18 +07:00
Lemmy
3cf4bf6480 Hope fully fixed group waiting 2018-09-25 17:32:32 +02:00
Lemmy
256523d36e Merge branch 'develop' of https://github.com/klemens-morgenstern/boost-process into develop 2018-09-25 11:45:23 +02:00
Lemmy
6ba8e88def wait-group fix 2018-09-25 11:45:04 +02:00
Klemens David Morgenstern
f139f863a0 typo fix in test 2018-09-25 16:10:11 +07:00
Lemmy
0938103427 Reworked wait_for_exit, concerns #99 and #112 2018-09-25 10:45:54 +02:00
Lemmy
e72127f9f8 Implemented proper wait_for for group_handles 2018-09-25 10:27:40 +02:00
Lemmy
eea73753b5 Fixed group wait in linux 2018-09-25 07:40:07 +02:00
Klemens David Morgenstern
4f3b425073 fixed group-wait, finally 2018-09-25 02:37:51 +07:00
Klemens David Morgenstern
dcb8a0266a preserving creation_flags, closes #176 2018-09-24 23:42:53 +07:00
Klemens David Morgenstern
99285a9de6 fixed windows-h variant 2018-09-24 23:27:01 +07:00
Klemens David Morgenstern
6cc31b93d8 readded BOOST_WINAPI_WINAPI_CC 2018-09-24 23:10:33 +07:00
Klemens David Morgenstern
d1ce19d848 fixes #178 2018-09-21 10:48:03 +07:00
Klemens Morgenstern
f00895a9fc Update tutorial.qbk
closes #49
2018-09-11 14:38:49 +08:00
Klemens David Morgenstern
8d2bd87707 ALternative (typeless) implementation of #177 2018-08-29 09:37:07 +08:00
Klemens David Morgenstern
44162ecf22 removed errornous noexcept 2018-06-19 18:34:39 +08:00
Klemens David Morgenstern
d709c1cd07 fixed tutorial example type 2018-06-18 09:46:15 +08:00
Klemens Morgenstern
90d2c0ceca Merge pull request #168 from klemens-morgenstern/develop
1.68 release merge
2018-06-13 22:00:09 +03:00
Klemens David Morgenstern
9549ffe7e1 capture list fix 2018-05-27 11:39:56 +02:00
Klemens David Morgenstern
dd0edb4aee Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2018-05-26 22:07:15 +02:00
Klemens David Morgenstern
3029f4623a closes #41 2018-05-26 22:06:54 +02:00
Klemens David Morgenstern
74606db379 Merge branch 'bugfixing' into develop 2018-05-26 22:00:12 +02:00
Klemens David Morgenstern
81803868a3 closes #45 2018-05-26 21:59:58 +02:00
Klemens David Morgenstern
eff42f91ef closes #42 2018-05-26 21:57:25 +02:00
Klemens David Morgenstern
a25b6ca35b closes #32 2018-05-26 21:55:07 +02:00
Klemens Morgenstern
1c8323650d Merge pull request #166 from egorpugin/patch-2
Add missing returns on windows side.
2018-05-26 20:46:14 +02:00
Klemens David Morgenstern
52f030a83c closes #167 2018-05-26 20:32:08 +02:00
Egor Pugin
9cc651bdeb Add missing returns on windows side. 2018-05-09 20:09:32 +03:00
Klemens Morgenstern
128cb0283d Merge pull request #165 from egorpugin/patch-2
Add missing returns
2018-05-09 14:41:31 +02:00
Egor Pugin
bb259f8f16 Add missing returns 2018-05-09 15:16:15 +03:00
Klemens Morgenstern
bb1bb431e5 Merge pull request #163 from tomaszjonak/develop
Fix async_pipe::async_read_some always returning 0
2018-05-04 17:22:48 +02:00
Tomasz Jonak
41b7e30c18 Add missing return to async_pipe::async_{read,write}_some 2018-05-04 14:03:54 +00:00
Klemens David Morgenstern
6abce365c5 started on asio_no_deprecated.cpp 2018-03-11 20:57:18 +01:00
105 changed files with 3221 additions and 686 deletions

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

View File

@@ -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,121 @@ 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 || true
- 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:
- bash <(curl -s https://codecov.io/bash)
- cd $BOOST/libs/$PROJECT_TO_TEST/test
- curl -s https://report.ci/upload.py | python - --name="$BADGE test run" --root_dir $BOOST
# Sending data to Coveralls
- cd $TRAVIS_BUILD_DIR
- gem install coveralls-lcov
- coveralls-lcov coverals/coverage.info

View File

@@ -4,10 +4,11 @@ Boost.process is a library for comfortable management of processes, released wit
### Test results
Branches | Build | Tests coverage |
----------------|-------------- | -------------- |
Develop: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=develop)](https://travis-ci.org/klemens-morgenstern/boost-process) [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=develop)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=develop) |
Master: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=master)](https://travis-ci.org/klemens-morgenstern/boost-process) [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba/branch/master?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/master) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=master)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=master) |
Branches | Linux | OSX | Windows | Code coverage | Matrix |
----------------|-------|-----|---------| ------------- |--------|
Develop: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=develop&env=BADGE=linux)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=linux)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=linux) | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=develop&env=BADGE=osx)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=osx)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=osx) | [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba/branch/develop?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/develop) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=windows)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=windows) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=develop)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=develop) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](http://www.boost.org/development/tests/develop/developer/process.html)
Master: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=master&env=BADGE=linux)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=linux)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=linux) | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=master&env=BADGE=osx)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=osx)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=osx) | [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba/branch/master?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/master) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=windows)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=windows) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=master)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=master) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](http://www.boost.org/development/tests/master/developer/process.html)
[Open Issues](https://github.com/klemens-morgenstern/boost-process/issues)

View File

@@ -16,11 +16,11 @@ 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]
The usual type of pipes, are the anonymous ones. Since the have no name,
The most common pipes are anonymous. Since the have no name,
a handle to them can only be obtained from duplicating either handle.
In this library the following functions are used for the creation of unnamed pipes:

View File

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

View File

@@ -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
@@ -296,7 +295,7 @@ provided we also pass a reference to an io_service.
```
io_service ios;
std::vector<char> buf;
std::vector<char> buf(4096);
bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > asio_buffer(buf), ios);

14
filter_section_warning.py Normal file
View 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()

View File

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

View File

@@ -47,6 +47,8 @@ public:
*/
typedef platform_specific handle_type;
typedef typename handle_type::executor_type executor_type;
/** Construct a new async_pipe, does automatically open the pipe.
* Initializes source and sink with the same io_context.
* @note Windows creates a named pipe here, where the name is automatically generated.

View File

@@ -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))
@@ -82,7 +84,7 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
{
#if defined(BOOST_POSIX_API)
if (errored)
return [](int exit_code, const std::error_code & ec){};
return [](int , const std::error_code &){};
#endif
auto & h = init.completion_handler;
return [h](int exit_code, const std::error_code & ec) mutable

View File

@@ -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() */

View File

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

View File

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

View File

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

View File

@@ -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,36 +36,37 @@ 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)
{
}
template <typename Executor>
inline void on_success(Executor &exec)
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&ec, std::size_t size){});
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)
{

View File

@@ -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)
{
@@ -61,7 +75,7 @@ struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
{
auto pipe = this->pipe;
boost::asio::async_read(*pipe, buf,
[pipe](const boost::system::error_code&, std::size_t size){});
[pipe](const boost::system::error_code&, std::size_t){});
this->pipe = nullptr;
std::move(*pipe).sink().close();
@@ -112,32 +126,35 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
fut = promise->get_future();
}
template <typename Executor>
inline void on_success(Executor &exec)
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 size)
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));
if (buffer_->size() > 0)
{
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;
}

View File

@@ -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>
@@ -22,6 +23,7 @@ class async_pipe
public:
typedef int native_handle_type;
typedef ::boost::asio::posix::stream_descriptor handle_type;
typedef typename handle_type::executor_type executor_type;
inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
@@ -109,9 +111,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>
@@ -148,7 +150,7 @@ public:
const MutableBufferSequence & buffers,
ReadHandler &&handler)
{
_source.async_read_some(buffers, std::forward<ReadHandler>(handler));
return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence,
@@ -159,7 +161,7 @@ public:
const ConstBufferSequence & buffers,
WriteHandler&& handler)
{
_sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
@@ -218,8 +220,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.
@@ -250,8 +252,8 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
int sink;
//cannot get the handle from a const object.
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._sink).native_handle();
if (source_in == -1)
source = -1;
else

View File

@@ -82,22 +82,30 @@ public:
int_type write(const char_type * data, int_type count)
{
auto write_len = ::write(_sink, data, count * sizeof(char_type));
if (write_len == -1)
::boost::process::detail::throw_last_error();
int_type write_len;
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::detail::throw_last_error();
}
return write_len;
}
int_type read(char_type * data, int_type count)
{
auto read_len = ::read(_source, data, count * sizeof(char_type));
if (read_len == -1)
::boost::process::detail::throw_last_error();
int_type read_len;
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::detail::throw_last_error();
}
return read_len;
}
bool is_open()
bool is_open() const
{
return (_source != -1) ||
(_sink != -1);

View File

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

View File

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

View File

@@ -94,7 +94,7 @@ public:
native_environment_impl & operator=(native_environment_impl && ) = default;
native_handle_type _env_impl = _impl.data();
native_handle_type native_handle() const {return environ;}
native_handle_type native_handle() const {return _env_impl;}
};
template<>
@@ -294,7 +294,11 @@ std::vector<Char*> basic_environment_impl<Char>::_load_var(std::vector<std::basi
ret.reserve(data.size() +1);
for (auto & val : data)
{
if (val.empty())
val.push_back(0);
ret.push_back(&val.front());
}
ret.push_back(nullptr);
return ret;

View File

@@ -22,47 +22,12 @@
#include <errno.h>
#include <unistd.h>
#if !defined(__GLIBC__)
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#endif
namespace boost { namespace process { namespace detail { namespace posix {
inline int execvpe(const char* filename, char * const arg_list[], char* env[])
{
#if defined(__GLIBC__)
return ::execvpe(filename, arg_list, env);
#else
//use my own implementation
std::string fn = filename;
if ((fn.find('/') == std::string::npos) && ::access(fn.c_str(), X_OK))
{
auto e = ::environ;
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
e++;
if (e != nullptr)
{
std::vector<std::string> path;
boost::split(path, *e, boost::is_any_of(":"));
for (const std::string & pp : path)
{
auto p = pp + "/" + filename;
if (!::access(p.c_str(), X_OK))
{
fn = p;
break;
}
}
}
}
return ::execve(fn.c_str(), arg_list, env);
#endif
}
template<typename Executor>
struct on_setup_t
{
@@ -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";

View File

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

View File

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

View File

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

View File

@@ -56,7 +56,7 @@ struct group_handle
{
return ::getpgid(proc) == grp;
}
bool has(handle_t proc, std::error_code & ec) noexcept
bool has(handle_t proc, std::error_code &) noexcept
{
return ::getpgid(proc) == grp;
}

View 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ struct on_exit_ : boost::process::detail::posix::async_handler
{
return handler;
};
}
};

View File

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

View File

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

View File

@@ -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,8 +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([ec]{});
this->_handle_signal(ec);
boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
});
}
}

View File

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

View File

@@ -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();
@@ -87,7 +206,7 @@ template< class Clock, class Duration >
inline bool wait_until(
const child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& time_out) noexcept
const std::chrono::time_point<Clock, Duration>& time_out)
{
std::error_code ec;
bool b = wait_until(p, exit_code, time_out, ec);
@@ -109,7 +228,7 @@ template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time) noexcept
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);

View File

@@ -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,34 @@ 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;
do
bool timed_out = false;
int ret;
::timespec sleep_interval;
sleep_interval.tv_sec = 0;
sleep_interval.tv_nsec = 100000000;
while (!(timed_out = (Clock::now() > time_out)))
{
ret = ::waitpid(-p.grp, &status, WNOHANG);
if (ret == 0)
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
if (ret == -1)
{
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
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);
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
if (ret == -1)
ec = boost::process::detail::get_last_error();
else
ec.clear();
return true;
return !timed_out;
}
template< class Clock, class Duration >

View 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_ */

View File

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

View File

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

View File

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

View File

@@ -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,31 @@ class async_pipe
{
::boost::asio::windows::stream_handle _source;
::boost::asio::windows::stream_handle _sink ;
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) {}
inline async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
const std::string & name = make_pipe_name());
const std::string & name, bool private_);
public:
typedef ::boost::winapi::HANDLE_ native_handle_type;
typedef ::boost::asio::windows::stream_handle handle_type;
typedef typename handle_type::executor_type executor_type;
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(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 +108,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 +121,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 +137,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>
@@ -167,7 +176,7 @@ public:
const MutableBufferSequence & buffers,
ReadHandler &&handler)
{
_source.async_read_some(buffers, std::forward<ReadHandler>(handler));
return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence,
@@ -178,7 +187,7 @@ public:
const ConstBufferSequence & buffers,
WriteHandler && handler)
{
_sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
const handle_type & sink () const & {return _sink;}
@@ -189,14 +198,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 +225,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 +241,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 +275,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 +296,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 +321,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 +370,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 +389,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;
}

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
#include <boost/winapi/file_management.hpp>
#include <string>
#include <boost/filesystem/path.hpp>
#include <boost/core/exchange.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
@@ -90,10 +91,18 @@ struct file_descriptor
}
file_descriptor(const file_descriptor & ) = delete;
file_descriptor(file_descriptor && ) = default;
file_descriptor(file_descriptor &&other)
: _handle( boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_) )
{
}
file_descriptor& operator=(const file_descriptor & ) = delete;
file_descriptor& operator=(file_descriptor && ) = default;
file_descriptor& operator=(file_descriptor &&other)
{
if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_)
::boost::winapi::CloseHandle(_handle);
_handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_);
}
~file_descriptor()
{

View File

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

View File

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

View File

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

View File

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

View 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_ */

View 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 (;
nt_status == STATUS_INFO_LENGTH_MISMATCH_;
nt_status = workaround::nt_system_query_information(
workaround::SystemHandleInformation_,
info_pointer, static_cast<::boost::winapi::ULONG_>(buffer.size()),
nullptr))
{
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

View File

@@ -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_ */

View File

@@ -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_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -55,19 +55,19 @@ inline boost::filesystem::path search_path(
for (auto & ext : extensions)
boost::to_lower(ext);
for (const boost::filesystem::path & pp : path)
for (const boost::filesystem::path & pp_ : path)
{
auto p = pp / filename;
auto p = pp_ / filename;
for (boost::filesystem::path ext : extensions)
{
boost::filesystem::path pp = p;
pp += ext;
boost::filesystem::path pp_ext = p;
pp_ext += ext;
boost::system::error_code ec;
bool file = boost::filesystem::is_regular_file(pp, ec);
bool file = boost::filesystem::is_regular_file(pp_ext, ec);
if (!ec && file &&
::boost::winapi::sh_get_file_info(pp.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
{
return pp;
return pp_ext;
}
}
}

View File

@@ -34,7 +34,7 @@ struct create_no_window_ : public ::boost::process::detail::handler_base
template <class Executor>
void on_setup(Executor &exec) const
{
exec.creation_flags |= ::boost::detail::winapi::CREATE_NO_WINDOW_;
exec.creation_flags |= ::boost::winapi::CREATE_NO_WINDOW_;
}
};

View File

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

View File

@@ -44,7 +44,7 @@ struct const_entry
bool operator()(char c) const {return c == api::env_seperator<char> ();}
} s;
boost::split(data, _data, s);
return std::move(data);
return data;
}
string_type to_string() const
{
@@ -94,8 +94,8 @@ struct entry : const_entry<Char, Environment>
explicit entry(string_type&& name, pointer data, environment_t & env) :
father(std::move(name), data, env) {}
explicit entry(string_type &&name, environment_t & env) :
father(std::move(name), env) {}
explicit entry(string_type &&name, environment_t & env_) :
father(std::move(name), env_) {}
entry(const entry&) = default;
entry& operator=(const entry&) = default;

View File

@@ -26,10 +26,28 @@ namespace boost {
</programlisting>
\endxmlonly
*/
namespace boost { namespace process { namespace detail {
namespace boost {
namespace filesystem { class path; }
namespace process {
namespace detail {
struct exe_
{
template<typename = void>
inline exe_setter_<typename boost::filesystem::path::value_type> operator()(const boost::filesystem::path & pth) const
{
return exe_setter_<typename boost::filesystem::path::value_type>(pth.native());
}
template<typename = void>
inline exe_setter_<typename boost::filesystem::path::value_type> operator=(const boost::filesystem::path & pth) const
{
return exe_setter_<typename boost::filesystem::path::value_type>(pth.native());
}
template<typename Char>
inline exe_setter_<Char> operator()(const Char *s) const
{

View File

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

View 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_

View File

@@ -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,13 @@ 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._buf);
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_ipstream& operator=(pipe_type && p)
{
@@ -312,6 +366,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 +407,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 +419,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 +429,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 +446,14 @@ 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._buf);
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_opstream& operator=(pipe_type && p)
{
@@ -386,6 +476,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 +516,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 +528,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 +538,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 +555,13 @@ 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._buf);
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_pstream& operator=(pipe_type && p)
{
@@ -461,6 +584,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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
//
// Created by kleme on 26.02.2018.
//
#define BOOST_ASIO_NO_DEPRECATED 1
#include <boost/process.hpp>
int main() {}
#if defined(BOOST_POSIX_API)
#include <boost/process/posix.hpp>
#else
#include <boost/process/windows.hpp>
#endif

View File

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

View File

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

View File

@@ -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,62 @@ 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)};
#if defined(BOOST_WINDOWS_API)
BOOST_CHECK_EQUAL(ap.native_source(), ::boost::winapi::INVALID_HANDLE_VALUE_);
BOOST_CHECK_EQUAL(ap.native_sink (), ::boost::winapi::INVALID_HANDLE_VALUE_);
#elif defined(BOOST_POSIX_API)
BOOST_CHECK_EQUAL(ap.native_source(), -1);
BOOST_CHECK_EQUAL(ap.native_sink (), -1);
#endif
BOOST_TEST_CHECKPOINT("Second move");
ap = std::move(ap2);
{
BOOST_TEST_CHECKPOINT("Third move, from closed");
bp::async_pipe ap_inv{ios};
ap_inv.close();
ap = std::move(ap_inv);
}
{
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
bp::async_pipe ap_inv{ios};
ap_inv.close();
const auto ap3 = std::move(ap_inv);
}
{
//copy an a closed pipe
BOOST_TEST_CHECKPOINT("Copy assign");
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
bp::async_pipe ap_inv{ios};
ap_inv.close();
ap = ap_inv; //copy an invalid pipe
}
{
//copy an a closed pipe
BOOST_TEST_CHECKPOINT("Copy assign");
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
bp::async_pipe ap_inv{ios};
ap_inv.close();
BOOST_TEST_CHECKPOINT("Copy construct");
bp::async_pipe ap4{ap_inv};
}
}
BOOST_AUTO_TEST_SUITE_END();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -118,6 +118,29 @@ BOOST_AUTO_TEST_CASE(compare, *boost::unit_test::timeout(5))
BOOST_TEST_PASSPOINT();
}
BOOST_AUTO_TEST_CASE(wcompare, *boost::unit_test::timeout(5))
{
auto nat = boost::this_process::wenvironment();
bp::wenvironment env = nat;
{
BOOST_CHECK_EQUAL(nat.size(), env.size());
auto ni = nat.begin();
auto ei = env.begin();
while ((ni != nat.end()) &&(ei != env.end()))
{
BOOST_CHECK_EQUAL(ni->get_name(), ei->get_name());
BOOST_CHECK_EQUAL(ni->to_string(), ei->to_string());
ni++; ei++;
}
}
BOOST_TEST_PASSPOINT();
env.clear();
BOOST_TEST_PASSPOINT();
}
BOOST_AUTO_TEST_CASE(insert_remove, *boost::unit_test::timeout(5))
{
bp::environment env(boost::this_process::environment());

View File

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

View File

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

View File

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

View File

@@ -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";
@@ -97,6 +102,110 @@ BOOST_AUTO_TEST_CASE(stream, *boost::unit_test::timeout(2))
BOOST_CHECK_EQUAL(i, j);
}
BOOST_AUTO_TEST_CASE(stream_move, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::pstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::pstream os2 = std::move(os);
bp::ipstream is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(ostream_move, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::opstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::opstream os2 = std::move(os);
bp::ipstream is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(stream_move_assignment, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::pstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::pstream os2;
os2 = std::move(os);
bp::ipstream is2;
is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(ostream_move_assignment, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::opstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::opstream os2;
os2 = std::move(os);
bp::ipstream is2;
is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(stream_line, *boost::unit_test::timeout(2))
{
@@ -230,3 +339,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();

View File

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

View File

@@ -14,6 +14,8 @@
#include <boost/process.hpp>
#include <boost/process/posix.hpp>
#include <boost/filesystem.hpp>
#include <system_error>
@@ -21,6 +23,7 @@
#include <sys/wait.h>
#include <errno.h>
namespace fs = boost::filesystem;
namespace bp = boost::process;
BOOST_AUTO_TEST_CASE(bind_fd, *boost::unit_test::timeout(2))
@@ -99,3 +102,67 @@ BOOST_AUTO_TEST_CASE(execve_throw_on_error, *boost::unit_test::timeout(2))
BOOST_CHECK_EQUAL(e.code().value(), ENOENT);
}
}
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
const auto pid = boost::this_process::get_id();
const auto fd_path = fs::path("/proc") / std::to_string(pid) / "fd";
auto get_fds = [&]{
std::vector<int> fds;
for (auto && fd : fs::directory_iterator(fd_path))
fds.push_back(std::stoi(fd.path().filename().string()));
return fds;
};
std::vector<int> fd_list = get_fds();
if (fd_list.empty()) //then there's no /proc in the current linux distribution.
return;
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDOUT_FILENO) != fd_list.end());
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDIN_FILENO) != fd_list.end());
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDERR_FILENO) != fd_list.end());
bp::pipe p; //should add two descriptors.
auto fd_list_new = get_fds();
BOOST_CHECK_EQUAL(fd_list_new.size(), fd_list.size() + 2);
fd_list.push_back(p.native_source());
fd_list.push_back(p.native_sink());
BOOST_CHECK_EQUAL(
bp::system(
master_test_suite().argv[1],
"test", "--exit-code", "123", ec), 123);
fd_list_new = get_fds();
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
const int native_source = p.native_source();
BOOST_CHECK_EQUAL(
bp::system(
master_test_suite().argv[1],
bp::std_in < p,
"test", "--exit-code", "123", ec), 123);
BOOST_CHECK(!ec);
////now, p.source should be closed, so we remove it from fd_list
const auto itr = std::find(fd_list.begin(), fd_list.end(), native_source);
if (itr != fd_list.end())
fd_list.erase(itr);
fd_list_new = get_fds();
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
}

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More