process.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. //
  6. //
  7. // process.hpp
  8. // ~~~~~~~~~~~~~~
  9. //
  10. #ifndef BOOST_PROCESS_V2_PROCESS_HPP
  11. #define BOOST_PROCESS_V2_PROCESS_HPP
  12. #include <boost/process/v2/detail/config.hpp>
  13. #include <boost/process/v2/default_launcher.hpp>
  14. #include <boost/process/v2/exit_code.hpp>
  15. #include <boost/process/v2/pid.hpp>
  16. #include <boost/process/v2/ext/exe.hpp>
  17. #include <boost/process/v2/process_handle.hpp>
  18. #if defined(BOOST_PROCESS_V2_STANDALONE)
  19. #include <asio/any_io_executor.hpp>
  20. #include <boost/asio/dispatch.hpp>
  21. #include <asio/post.hpp>
  22. #include <utility>
  23. #else
  24. #include <boost/asio/any_io_executor.hpp>
  25. #include <boost/asio/dispatch.hpp>
  26. #include <boost/asio/post.hpp>
  27. #include <boost/core/exchange.hpp>
  28. #endif
  29. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  30. /// A class managing a subprocess
  31. /* A `basic_process` object manages a subprocess; it tracks the status and exit-code,
  32. * and will terminate the process on destruction if `detach` was not called.
  33. */
  34. template<typename Executor = net::any_io_executor>
  35. struct basic_process
  36. {
  37. /// The executor of the process
  38. using executor_type = Executor;
  39. /// Get the executor of the process
  40. executor_type get_executor() {return process_handle_.get_executor();}
  41. /// The non-closing handle type
  42. using handle_type = basic_process_handle<executor_type>;
  43. /// Get the underlying non-closing handle
  44. handle_type & handle() { return process_handle_; }
  45. /// Get the underlying non-closing handle
  46. const handle_type & handle() const { return process_handle_; }
  47. /// Provides access to underlying operating system facilities
  48. using native_handle_type = typename handle_type::native_handle_type;
  49. /// Rebinds the process_handle to another executor.
  50. template <typename Executor1>
  51. struct rebind_executor
  52. {
  53. /// The socket type when rebound to the specified executor.
  54. typedef basic_process<Executor1> other;
  55. };
  56. /** An empty process is similar to a default constructed thread. It holds an empty
  57. handle and is a place holder for a process that is to be launched later. */
  58. basic_process() = default;
  59. basic_process(const basic_process&) = delete;
  60. basic_process& operator=(const basic_process&) = delete;
  61. /// Move construct the process. It will be detached from `lhs`.
  62. basic_process(basic_process&& lhs) = default;
  63. /// Move assign a process. It will be detached from `lhs`.
  64. basic_process& operator=(basic_process&& lhs) = default;
  65. /// Move construct and rebind the executor.
  66. template<typename Executor1>
  67. basic_process(basic_process<Executor1>&& lhs)
  68. : process_handle_(std::move(lhs.process_handle_)),
  69. exit_status_{lhs.exit_status_}
  70. {
  71. }
  72. /// Construct a child from a property list and launch it using the default launcher..
  73. template<typename ... Inits>
  74. explicit basic_process(
  75. executor_type executor,
  76. const filesystem::path& exe,
  77. std::initializer_list<string_view> args,
  78. Inits&&... inits)
  79. : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
  80. {
  81. }
  82. /// Construct a child from a property list and launch it using the default launcher..
  83. template<typename Args, typename ... Inits>
  84. explicit basic_process(
  85. executor_type executor,
  86. const filesystem::path& exe,
  87. Args&& args, Inits&&... inits)
  88. : basic_process(default_process_launcher()(std::move(executor), exe,
  89. std::forward<Args>(args), std::forward<Inits>(inits)...))
  90. {
  91. }
  92. /// Construct a child from a property list and launch it using the default launcher..
  93. template<typename ExecutionContext, typename ... Inits>
  94. explicit basic_process(
  95. ExecutionContext & context,
  96. typename std::enable_if<
  97. std::is_convertible<ExecutionContext&,
  98. net::execution_context&>::value,
  99. const filesystem::path&>::type exe,
  100. std::initializer_list<string_view> args,
  101. Inits&&... inits)
  102. : basic_process(default_process_launcher()(executor_type(context.get_executor()),
  103. exe, args, std::forward<Inits>(inits)...))
  104. {
  105. }
  106. /// Construct a child from a property list and launch it using the default launcher.
  107. template<typename ExecutionContext, typename Args, typename ... Inits>
  108. explicit basic_process(
  109. ExecutionContext & context,
  110. typename std::enable_if<
  111. std::is_convertible<ExecutionContext&,
  112. net::execution_context&>::value,
  113. const filesystem::path&>::type exe,
  114. Args&& args, Inits&&... inits)
  115. : basic_process(default_process_launcher()(executor_type(context.get_executor()),
  116. exe, std::forward<Args>(args), std::forward<Inits>(inits)...))
  117. {
  118. }
  119. /// Attach to an existing process
  120. explicit basic_process(executor_type exec, pid_type pid) : process_handle_(std::move(exec), pid) {}
  121. /// Attach to an existing process and the internal handle
  122. explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle)
  123. : process_handle_(std::move(exec), pid, native_handle) {}
  124. /// Create an invalid handle
  125. explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {}
  126. /// Attach to an existing process
  127. template <typename ExecutionContext>
  128. explicit basic_process(ExecutionContext & context, pid_type pid,
  129. typename std::enable_if<
  130. std::is_convertible<ExecutionContext&,
  131. net::execution_context&>::value, void *>::type = nullptr)
  132. : process_handle_(context, pid) {}
  133. /// Attach to an existing process and the internal handle
  134. template <typename ExecutionContext>
  135. explicit basic_process(ExecutionContext & context, pid_type pid, native_handle_type native_handle,
  136. typename std::enable_if<
  137. std::is_convertible<ExecutionContext&,
  138. net::execution_context&>::value, void *>::type = nullptr)
  139. : process_handle_(context.get_executor(), pid, native_handle) {}
  140. /// Create an invalid handle
  141. template <typename ExecutionContext>
  142. explicit basic_process(ExecutionContext & context,
  143. typename std::enable_if<
  144. is_convertible<ExecutionContext&,
  145. net::execution_context&>::value, void *>::type = nullptr)
  146. : process_handle_(context.get_executor()) {}
  147. /// Destruct the handle and terminate the process if it wasn't detached.
  148. ~basic_process()
  149. {
  150. process_handle_.terminate_if_running();
  151. }
  152. /// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown.
  153. /** Maybe be ignored by the subprocess. */
  154. void interrupt()
  155. {
  156. error_code ec;
  157. interrupt(ec);
  158. if (ec)
  159. throw system_error(ec, "interrupt failed");
  160. }
  161. /// Throwing @overload void interrupt()
  162. void interrupt(error_code & ec)
  163. {
  164. process_handle_.interrupt(ec);
  165. }
  166. /// Throwing @overload void request_exit(error_code & ec)
  167. void request_exit()
  168. {
  169. error_code ec;
  170. request_exit(ec);
  171. if (ec)
  172. throw system_error(ec, "request_exit failed");
  173. }
  174. /// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
  175. void request_exit(error_code & ec)
  176. {
  177. process_handle_.request_exit(ec);
  178. }
  179. /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
  180. void suspend(error_code &ec)
  181. {
  182. process_handle_.suspend(ec);
  183. }
  184. /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
  185. void suspend()
  186. {
  187. error_code ec;
  188. suspend(ec);
  189. if (ec)
  190. detail::throw_error(ec, "suspend");
  191. }
  192. /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
  193. void resume(error_code &ec)
  194. {
  195. process_handle_.resume(ec);
  196. }
  197. /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
  198. void resume()
  199. {
  200. error_code ec;
  201. resume(ec);
  202. if (ec)
  203. detail::throw_error(ec, "resume");
  204. }
  205. /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
  206. void terminate()
  207. {
  208. error_code ec;
  209. terminate(ec);
  210. if (ec)
  211. detail::throw_error(ec, "terminate failed");
  212. }
  213. /// Unconditionally terminates the process and stores the exit code in exit_status.
  214. void terminate(error_code & ec)
  215. {
  216. process_handle_.terminate(exit_status_, ec);
  217. }
  218. /// Throwing @overload wait(error_code & ec)
  219. int wait()
  220. {
  221. error_code ec;
  222. if (running(ec))
  223. process_handle_.wait(exit_status_, ec);
  224. if (ec)
  225. detail::throw_error(ec, "wait failed");
  226. return exit_code();
  227. }
  228. /// Waits for the process to exit, store the exit code internally and return it.
  229. int wait(error_code & ec)
  230. {
  231. if (running(ec))
  232. process_handle_.wait(exit_status_, ec);
  233. return exit_code();
  234. }
  235. /// Detach the process.
  236. handle_type detach()
  237. {
  238. #if defined(BOOST_PROCESS_V2_STANDALONE)
  239. return std::exchange(process_handle_, get_executor());
  240. #else
  241. return boost::exchange(process_handle_, get_executor());
  242. #endif
  243. }
  244. /// Get the native
  245. native_handle_type native_handle() {return process_handle_.native_handle(); }
  246. /// Return the evaluated exit_code.
  247. int exit_code() const
  248. {
  249. return evaluate_exit_code(exit_status_);
  250. }
  251. /// Get the id of the process;
  252. pid_type id() const {return process_handle_.id();}
  253. /// The native handle of the process.
  254. /** This might be undefined on posix systems that only support signals */
  255. native_exit_code_type native_exit_code() const
  256. {
  257. return exit_status_;
  258. }
  259. /// Checks if the current process is running.
  260. /** If it has already completed the exit code will be stored internally
  261. * and can be obtained by calling `exit_code.
  262. */
  263. bool running()
  264. {
  265. if (!process_is_running(exit_status_))
  266. return false;
  267. error_code ec;
  268. native_exit_code_type exit_code{};
  269. auto r = process_handle_.running(exit_code, ec);
  270. if (!ec && !r)
  271. exit_status_ = exit_code;
  272. else
  273. detail::throw_error(ec, "running failed");
  274. return r;
  275. }
  276. /// Throwing @overload bool running(error_code & ec)
  277. bool running(error_code & ec) noexcept
  278. {
  279. if (!process_is_running(exit_status_))
  280. return false;
  281. native_exit_code_type exit_code{};
  282. auto r = process_handle_.running(exit_code, ec);
  283. if (!ec && !r)
  284. exit_status_ = exit_code;
  285. return r;
  286. }
  287. /// Check if the process is referring to an existing process.
  288. /** Note that this might be a process that already exited.*/
  289. bool is_open() const { return process_handle_.is_open(); }
  290. private:
  291. template<typename Executor1>
  292. friend struct basic_process;
  293. basic_process_handle<Executor> process_handle_;
  294. native_exit_code_type exit_status_{detail::still_active};
  295. struct async_wait_op_
  296. {
  297. basic_process_handle<Executor> & handle;
  298. native_exit_code_type & res;
  299. template<typename Self>
  300. void operator()(Self && self)
  301. {
  302. if (!process_is_running(res))
  303. {
  304. struct completer
  305. {
  306. int code;
  307. typename std::decay<Self>::type self;
  308. void operator()()
  309. {
  310. self.complete(error_code{}, evaluate_exit_code(code));
  311. }
  312. };
  313. net::dispatch(
  314. net::get_associated_immediate_executor(handle, handle.get_executor()),
  315. completer{static_cast<int>(res), std::move(self)});
  316. }
  317. else
  318. handle.async_wait(res, std::move(self));
  319. }
  320. template<typename Self>
  321. void operator()(Self && self, error_code ec)
  322. {
  323. if (!ec && process_is_running(res))
  324. handle.async_wait(res, std::move(self));
  325. else
  326. {
  327. std::move(self).complete(ec, evaluate_exit_code(res));
  328. }
  329. }
  330. };
  331. public:
  332. /// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
  333. template <BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
  334. WaitHandler = net::default_completion_token_t<executor_type>>
  335. auto async_wait(WaitHandler && handler = net::default_completion_token_t<executor_type>())
  336. -> decltype(net::async_compose<WaitHandler, void (error_code, int)>(
  337. async_wait_op_{process_handle_, exit_status_}, handler, process_handle_))
  338. {
  339. return net::async_compose<WaitHandler, void (error_code, int)>(
  340. async_wait_op_{process_handle_, exit_status_}, handler, process_handle_);
  341. }
  342. };
  343. /// Process with the default executor.
  344. typedef basic_process<> process;
  345. BOOST_PROCESS_V2_END_NAMESPACE
  346. #endif //BOOST_PROCESS_V2_PROCESS_HPP