123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
- //
- // 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)
- //
- //
- // process.hpp
- // ~~~~~~~~~~~~~~
- //
- #ifndef BOOST_PROCESS_V2_PROCESS_HPP
- #define BOOST_PROCESS_V2_PROCESS_HPP
- #include <boost/process/v2/detail/config.hpp>
- #include <boost/process/v2/default_launcher.hpp>
- #include <boost/process/v2/exit_code.hpp>
- #include <boost/process/v2/pid.hpp>
- #include <boost/process/v2/ext/exe.hpp>
- #include <boost/process/v2/process_handle.hpp>
- #if defined(BOOST_PROCESS_V2_STANDALONE)
- #include <asio/any_io_executor.hpp>
- #include <asio/post.hpp>
- #include <utility>
- #else
- #include <boost/asio/any_io_executor.hpp>
- #include <boost/asio/post.hpp>
- #include <boost/core/exchange.hpp>
- #endif
- BOOST_PROCESS_V2_BEGIN_NAMESPACE
- /// A class managing a subprocess
- /* A `basic_process` object manages a subprocess; it tracks the status and exit-code,
- * and will terminate the process on destruction if `detach` was not called.
- */
- template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
- struct basic_process
- {
- /// The executor of the process
- using executor_type = Executor;
- /// Get the executor of the process
- executor_type get_executor() {return process_handle_.get_executor();}
- /// The non-closing handle type
- using handle_type = basic_process_handle<executor_type>;
- /// Get the underlying non-closing handle
- handle_type & handle() { return process_handle_; }
- /// Get the underlying non-closing handle
- const handle_type & handle() const { return process_handle_; }
- /// Provides access to underlying operating system facilities
- using native_handle_type = typename handle_type::native_handle_type;
- /// Rebinds the process_handle to another executor.
- template <typename Executor1>
- struct rebind_executor
- {
- /// The socket type when rebound to the specified executor.
- typedef basic_process<Executor1> other;
- };
- /** An empty process is similar to a default constructed thread. It holds an empty
- handle and is a place holder for a process that is to be launched later. */
- basic_process() = default;
- basic_process(const basic_process&) = delete;
- basic_process& operator=(const basic_process&) = delete;
- /// Move construct the process. It will be detached from `lhs`.
- basic_process(basic_process&& lhs) = default;
- /// Move assign a process. It will be detached from `lhs`.
- basic_process& operator=(basic_process&& lhs) = default;
- /// Move construct and rebind the executor.
- template<typename Executor1>
- basic_process(basic_process<Executor1>&& lhs)
- : process_handle_(std::move(lhs.process_handle_)),
- exit_status_{lhs.exit_status_}
- {
- }
- /// Construct a child from a property list and launch it using the default launcher..
- template<typename ... Inits>
- explicit basic_process(
- executor_type executor,
- const filesystem::path& exe,
- std::initializer_list<string_view> args,
- Inits&&... inits)
- : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
- {
- }
-
- /// Construct a child from a property list and launch it using the default launcher..
- template<typename Args, typename ... Inits>
- explicit basic_process(
- executor_type executor,
- const filesystem::path& exe,
- Args&& args, Inits&&... inits)
- : basic_process(default_process_launcher()(std::move(executor), exe,
- std::forward<Args>(args), std::forward<Inits>(inits)...))
- {
- }
- /// Construct a child from a property list and launch it using the default launcher..
- template<typename ExecutionContext, typename ... Inits>
- explicit basic_process(
- ExecutionContext & context,
- typename std::enable_if<
- std::is_convertible<ExecutionContext&,
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
- const filesystem::path&>::type exe,
- std::initializer_list<string_view> args,
- Inits&&... inits)
- : basic_process(default_process_launcher()(executor_type(context.get_executor()),
- exe, args, std::forward<Inits>(inits)...))
- {
- }
- /// Construct a child from a property list and launch it using the default launcher.
- template<typename ExecutionContext, typename Args, typename ... Inits>
- explicit basic_process(
- ExecutionContext & context,
- typename std::enable_if<
- std::is_convertible<ExecutionContext&,
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
- const filesystem::path&>::type exe,
- Args&& args, Inits&&... inits)
- : basic_process(default_process_launcher()(executor_type(context.get_executor()),
- exe, std::forward<Args>(args), std::forward<Inits>(inits)...))
- {
- }
- /// Attach to an existing process
- explicit basic_process(executor_type exec, pid_type pid) : process_handle_(std::move(exec), pid) {}
- /// Attach to an existing process and the internal handle
- explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle)
- : process_handle_(std::move(exec), pid, native_handle) {}
- /// Create an invalid handle
- explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {}
- /// Attach to an existing process
- template <typename ExecutionContext>
- explicit basic_process(ExecutionContext & context, pid_type pid,
- typename std::enable_if<
- std::is_convertible<ExecutionContext&,
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
- : process_handle_(context, pid) {}
- /// Attach to an existing process and the internal handle
- template <typename ExecutionContext>
- explicit basic_process(ExecutionContext & context, pid_type pid, native_handle_type native_handle,
- typename std::enable_if<
- std::is_convertible<ExecutionContext&,
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
- : process_handle_(context.get_executor(), pid, native_handle) {}
- /// Create an invalid handle
- template <typename ExecutionContext>
- explicit basic_process(ExecutionContext & context,
- typename std::enable_if<
- is_convertible<ExecutionContext&,
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
- : process_handle_(context.get_executor()) {}
- /// Destruct the handle and terminate the process if it wasn't detached.
- ~basic_process()
- {
- process_handle_.terminate_if_running();
- }
- /// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown.
- /** Maybe be ignored by the subprocess. */
- void interrupt()
- {
- error_code ec;
- interrupt(ec);
- if (ec)
- throw system_error(ec, "interrupt failed");
- }
- /// Throwing @overload void interrupt()
- void interrupt(error_code & ec)
- {
- process_handle_.interrupt(ec);
- }
- /// Throwing @overload void request_exit(error_code & ec)
- void request_exit()
- {
- error_code ec;
- request_exit(ec);
- if (ec)
- throw system_error(ec, "request_exit failed");
- }
- /// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
- void request_exit(error_code & ec)
- {
- process_handle_.request_exit(ec);
- }
- /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
- void suspend(error_code &ec)
- {
- process_handle_.suspend(ec);
- }
- /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
- void suspend()
- {
- error_code ec;
- suspend(ec);
- if (ec)
- detail::throw_error(ec, "suspend");
- }
- /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
- void resume(error_code &ec)
- {
- process_handle_.resume(ec);
- }
- /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
- void resume()
- {
- error_code ec;
- suspend(ec);
- if (ec)
- detail::throw_error(ec, "resume");
- }
- /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
- void terminate()
- {
- error_code ec;
- terminate(ec);
- if (ec)
- detail::throw_error(ec, "terminate failed");
- }
- /// Unconditionally terminates the process and stores the exit code in exit_status.
- void terminate(error_code & ec)
- {
- process_handle_.terminate(exit_status_, ec);
- }
- /// Throwing @overload wait(error_code & ec)
- int wait()
- {
- error_code ec;
- if (running(ec))
- process_handle_.wait(exit_status_, ec);
- if (ec)
- detail::throw_error(ec, "wait failed");
- return exit_code();
- }
- /// Waits for the process to exit, store the exit code internally and return it.
- int wait(error_code & ec)
- {
- if (running(ec))
- process_handle_.wait(exit_status_, ec);
- return exit_code();
- }
- /// Detach the process.
- handle_type detach()
- {
- #if defined(BOOST_PROCESS_V2_STANDALONE)
- return std::exchange(process_handle_, get_executor());
- #else
- return boost::exchange(process_handle_, get_executor());
- #endif
- }
- /// Get the native
- native_handle_type native_handle() {return process_handle_.native_handle(); }
- /// Return the evaluated exit_code.
- int exit_code() const
- {
- return evaluate_exit_code(exit_status_);
- }
- /// Get the id of the process;
- pid_type id() const {return process_handle_.id();}
- /// The native handle of the process.
- /** This might be undefined on posix systems that only support signals */
- native_exit_code_type native_exit_code() const
- {
- return exit_status_;
- }
- /// Checks if the current process is running.
- /** If it has already completed the exit code will be stored internally
- * and can be obtained by calling `exit_code.
- */
- bool running()
- {
- error_code ec;
- native_exit_code_type exit_code{};
- auto r = process_handle_.running(exit_code, ec);
- if (!ec && !r)
- exit_status_ = exit_code;
- else
- detail::throw_error(ec, "running failed");
- return r;
- }
- /// Throwing @overload bool running(error_code & ec)
- bool running(error_code & ec) noexcept
- {
- native_exit_code_type exit_code{};
- auto r = process_handle_.running(exit_code, ec);
- if (!ec && !r)
- exit_status_ = exit_code;
- return r;
- }
-
- /// Check if the process is referring to an existing process.
- /** Note that this might be a process that already exited.*/
- bool is_open() const { return process_handle_.is_open(); }
-
- /// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
- template <BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
- WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
- BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int))
- async_wait(WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
- {
- return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void (error_code, int)>(
- async_wait_op_{process_handle_, exit_status_}, handler, process_handle_);
- }
- private:
- template<typename Executor1>
- friend struct basic_process;
- basic_process_handle<Executor> process_handle_;
- native_exit_code_type exit_status_{detail::still_active};
-
- struct async_wait_op_
- {
- basic_process_handle<Executor> & handle;
- native_exit_code_type & res;
- template<typename Self>
- void operator()(Self && self)
- {
- if (!process_is_running(res))
- {
- struct completer
- {
- int code;
- typename std::decay<Self>::type self;
- void operator()()
- {
- self.complete(error_code{}, evaluate_exit_code(code));
- }
- };
- BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(),
- completer{static_cast<int>(res), std::move(self)});
- }
- else
- handle.async_wait(std::move(self));
- }
- template<typename Self>
- void operator()(Self && self, error_code ec, native_exit_code_type code)
- {
- if (!ec && process_is_running(code))
- handle.async_wait(std::move(self));
- else
- {
- if (!ec)
- res = code;
- std::move(self).complete(ec, evaluate_exit_code(code));
- }
- }
- };
- };
- /// Process with the default executor.
- typedef basic_process<> process;
- BOOST_PROCESS_V2_END_NAMESPACE
- #endif //BOOST_PROCESS_V2_PROCESS_HPP
|