1 // Copyright (c) 2006, 2007 Julio M. Merino Vidal 2 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling 3 // Copyright (c) 2009 Boris Schaeling 4 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling 5 // Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling 6 // Copyright (c) 2016 Klemens D. Morgenstern 7 // 8 // Distributed under the Boost Software License, Version 1.0. (See accompanying 9 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 11 /** 12 * \file boost/process/child.hpp 13 * 14 * Defines a child process class. 15 */ 16 17 #ifndef BOOST_PROCESS_CHILD_DECL_HPP 18 #define BOOST_PROCESS_CHILD_DECL_HPP 19 20 #include <boost/process/detail/config.hpp> 21 #include <chrono> 22 #include <memory> 23 24 #include <boost/none.hpp> 25 #include <atomic> 26 27 #if defined(BOOST_POSIX_API) 28 #include <boost/process/detail/posix/child_handle.hpp> 29 #include <boost/process/detail/posix/terminate.hpp> 30 #include <boost/process/detail/posix/wait_for_exit.hpp> 31 #include <boost/process/detail/posix/is_running.hpp> 32 #elif defined(BOOST_WINDOWS_API) 33 #include <boost/process/detail/windows/child_handle.hpp> 34 #include <boost/process/detail/windows/terminate.hpp> 35 #include <boost/process/detail/windows/wait_for_exit.hpp> 36 #include <boost/process/detail/windows/is_running.hpp> 37 38 #endif 39 namespace boost { 40 41 namespace process { 42 43 using ::boost::process::detail::api::pid_t; 44 45 class child 46 { 47 ::boost::process::detail::api::child_handle _child_handle; 48 std::shared_ptr<std::atomic<int>> _exit_status = std::make_shared<std::atomic<int>>(::boost::process::detail::api::still_active); 49 bool _attached = true; 50 bool _terminated = false; 51 _exited()52 bool _exited() 53 { 54 return _terminated || !::boost::process::detail::api::is_running(_exit_status->load()); 55 }; 56 public: 57 typedef ::boost::process::detail::api::child_handle child_handle; 58 typedef child_handle::process_handle_t native_handle_t; child(child_handle && ch,std::shared_ptr<std::atomic<int>> & ptr)59 explicit child(child_handle &&ch, std::shared_ptr<std::atomic<int>> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} child(child_handle && ch,const std::shared_ptr<std::atomic<int>> & ptr)60 explicit child(child_handle &&ch, const std::shared_ptr<std::atomic<int>> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} child(child_handle && ch)61 explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {} 62 child(pid_t & pid)63 explicit child(pid_t & pid) : _child_handle(pid), _attached(false) {}; 64 child(const child&) = delete; child(child && lhs)65 child(child && lhs) noexcept 66 : _child_handle(std::move(lhs._child_handle)), 67 _exit_status(std::move(lhs._exit_status)), 68 _attached (lhs._attached), 69 _terminated (lhs._terminated) 70 { 71 lhs._attached = false; 72 } 73 74 template<typename ...Args> 75 explicit child(Args&&...args); child()76 child() { } // Must be kept non defaulted for MSVC 14.1 & 14.2 #113 77 child& operator=(const child&) = delete; operator =(child && lhs)78 child& operator=(child && lhs) 79 { 80 _child_handle= std::move(lhs._child_handle); 81 _exit_status = std::move(lhs._exit_status); 82 _attached = lhs._attached; 83 _terminated = lhs._terminated; 84 lhs._attached = false; 85 return *this; 86 }; 87 detach()88 void detach() {_attached = false; } join()89 void join() {wait();} joinable()90 bool joinable() { return _attached;} 91 ~child()92 ~child() 93 { 94 std::error_code ec; 95 if (_attached && !_exited() && running(ec)) 96 terminate(ec); 97 } native_handle() const98 native_handle_t native_handle() const { return _child_handle.process_handle(); } 99 100 exit_code() const101 int exit_code() const {return ::boost::process::detail::api::eval_exit_status(_exit_status->load());} id() const102 pid_t id() const {return _child_handle.id(); } 103 native_exit_code() const104 int native_exit_code() const {return _exit_status->load();} 105 running()106 bool running() 107 { 108 std::error_code ec; 109 bool b = running(ec); 110 boost::process::detail::throw_error(ec, "running error"); 111 return b; 112 } 113 terminate()114 void terminate() 115 { 116 std::error_code ec; 117 terminate(ec); 118 boost::process::detail::throw_error(ec, "terminate error"); 119 } 120 wait()121 void wait() 122 { 123 std::error_code ec; 124 wait(ec); 125 boost::process::detail::throw_error(ec, "wait error"); 126 } 127 128 template< class Rep, class Period > wait_for(const std::chrono::duration<Rep,Period> & rel_time)129 bool wait_for (const std::chrono::duration<Rep, Period>& rel_time) 130 { 131 std::error_code ec; 132 bool b = wait_for(rel_time, ec); 133 boost::process::detail::throw_error(ec, "wait_for error"); 134 return b; 135 } 136 137 template< class Clock, class Duration > wait_until(const std::chrono::time_point<Clock,Duration> & timeout_time)138 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time ) 139 { 140 std::error_code ec; 141 bool b = wait_until(timeout_time, ec); 142 boost::process::detail::throw_error(ec, "wait_until error"); 143 return b; 144 } 145 running(std::error_code & ec)146 bool running(std::error_code & ec) noexcept 147 { 148 ec.clear(); 149 if (valid() && !_exited() && !ec) 150 { 151 int exit_code = 0; 152 auto res = boost::process::detail::api::is_running(_child_handle, exit_code, ec); 153 if (!ec && !res && !_exited()) 154 _exit_status->store(exit_code); 155 156 return res; 157 } 158 return false; 159 } 160 terminate(std::error_code & ec)161 void terminate(std::error_code & ec) noexcept 162 { 163 if (valid() && running(ec) && !ec) 164 boost::process::detail::api::terminate(_child_handle, ec); 165 166 if (!ec) 167 _terminated = true; 168 } 169 wait(std::error_code & ec)170 void wait(std::error_code & ec) noexcept 171 { 172 if (!_exited() && valid()) 173 { 174 int exit_code = 0; 175 boost::process::detail::api::wait(_child_handle, exit_code, ec); 176 if (!ec) 177 _exit_status->store(exit_code); 178 } 179 } 180 181 template< class Rep, class Period > wait_for(const std::chrono::duration<Rep,Period> & rel_time,std::error_code & ec)182 bool wait_for (const std::chrono::duration<Rep, Period>& rel_time, std::error_code & ec) noexcept 183 { 184 return wait_until(std::chrono::steady_clock::now() + rel_time, ec); 185 } 186 187 template< class Clock, class Duration > wait_until(const std::chrono::time_point<Clock,Duration> & timeout_time,std::error_code & ec)188 bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time, std::error_code & ec) noexcept 189 { 190 if (!_exited()) 191 { 192 int exit_code = 0; 193 auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time, ec); 194 if (!b || ec) 195 return false; 196 _exit_status->store(exit_code); 197 } 198 return true; 199 } 200 201 valid() const202 bool valid() const 203 { 204 return _child_handle.valid(); 205 } operator bool() const206 operator bool() const {return valid();} 207 in_group() const208 bool in_group() const 209 { 210 return _child_handle.in_group(); 211 } in_group(std::error_code & ec) const212 bool in_group(std::error_code &ec) const noexcept 213 { 214 return _child_handle.in_group(ec); 215 } 216 }; 217 218 219 220 }} 221 #endif 222 223