Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:50:13

0001 // Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0004 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0005 //
0006 //
0007 // process.hpp
0008 // ~~~~~~~~~~~~~~
0009 //
0010 
0011 #ifndef BOOST_PROCESS_V2_PROCESS_HPP
0012 #define BOOST_PROCESS_V2_PROCESS_HPP
0013 
0014 #include <boost/process/v2/detail/config.hpp>
0015 #include <boost/process/v2/default_launcher.hpp>
0016 #include <boost/process/v2/exit_code.hpp>
0017 #include <boost/process/v2/pid.hpp>
0018 #include <boost/process/v2/ext/exe.hpp>
0019 #include <boost/process/v2/process_handle.hpp>
0020 
0021 #if defined(BOOST_PROCESS_V2_STANDALONE)
0022 #include <asio/any_io_executor.hpp>
0023 #include <asio/post.hpp>
0024 #include <utility>
0025 #else
0026 #include <boost/asio/any_io_executor.hpp>
0027 #include <boost/asio/post.hpp>
0028 #include <boost/core/exchange.hpp>
0029 #endif
0030 
0031 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0032 
0033 /// A class managing a subprocess
0034 /* A `basic_process` object manages a subprocess; it tracks the status and exit-code,
0035  * and will terminate the process on destruction if `detach` was not called.
0036 */
0037 template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
0038 struct basic_process
0039 {
0040   /// The executor of the process
0041   using executor_type = Executor;
0042   /// Get the executor of the process
0043   executor_type get_executor() {return process_handle_.get_executor();}
0044 
0045   /// The non-closing handle type
0046   using handle_type = basic_process_handle<executor_type>;
0047 
0048   /// Get the underlying non-closing handle
0049   handle_type & handle() { return process_handle_; }
0050 
0051   /// Get the underlying non-closing handle
0052   const handle_type & handle() const { return process_handle_; }
0053 
0054   /// Provides access to underlying operating system facilities
0055   using native_handle_type = typename handle_type::native_handle_type;
0056 
0057   /// Rebinds the process_handle to another executor.
0058   template <typename Executor1>
0059   struct rebind_executor
0060   {
0061     /// The socket type when rebound to the specified executor.
0062     typedef basic_process<Executor1> other;
0063   };
0064 
0065   /** An empty process is similar to a default constructed thread. It holds an empty
0066   handle and is a place holder for a process that is to be launched later. */
0067   basic_process() = default;
0068 
0069   basic_process(const basic_process&) = delete;
0070   basic_process& operator=(const basic_process&) = delete;
0071 
0072   /// Move construct the process. It will be detached from `lhs`.
0073   basic_process(basic_process&& lhs) = default;
0074 
0075   /// Move assign a process. It will be detached from `lhs`.
0076   basic_process& operator=(basic_process&& lhs) = default;
0077 
0078   /// Move construct and rebind the executor.
0079   template<typename Executor1>
0080   basic_process(basic_process<Executor1>&& lhs)
0081           : process_handle_(std::move(lhs.process_handle_)),
0082             exit_status_{lhs.exit_status_}
0083   {
0084   }
0085 
0086   /// Construct a child from a property list and launch it using the default launcher..
0087   template<typename ... Inits>
0088   explicit basic_process(
0089       executor_type executor,
0090       const filesystem::path& exe,
0091       std::initializer_list<string_view> args,
0092       Inits&&... inits)
0093       : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
0094   {
0095   }
0096   
0097   /// Construct a child from a property list and launch it using the default launcher..
0098   template<typename Args, typename ... Inits>
0099   explicit basic_process(
0100       executor_type executor,
0101       const filesystem::path& exe,
0102       Args&& args, Inits&&... inits)
0103       : basic_process(default_process_launcher()(std::move(executor), exe,
0104                                                std::forward<Args>(args), std::forward<Inits>(inits)...))
0105   {
0106   }
0107 
0108   /// Construct a child from a property list and launch it using the default launcher..
0109   template<typename ExecutionContext, typename ... Inits>
0110   explicit basic_process(
0111       ExecutionContext & context,
0112       typename std::enable_if<
0113           std::is_convertible<ExecutionContext&, 
0114                               BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0115           const filesystem::path&>::type exe,
0116       std::initializer_list<string_view> args,
0117       Inits&&... inits)
0118       : basic_process(default_process_launcher()(executor_type(context.get_executor()),
0119                                                  exe, args, std::forward<Inits>(inits)...))
0120   {
0121   }
0122   /// Construct a child from a property list and launch it using the default launcher.
0123   template<typename ExecutionContext, typename Args, typename ... Inits>
0124   explicit basic_process(
0125       ExecutionContext & context,
0126       typename std::enable_if<
0127           std::is_convertible<ExecutionContext&, 
0128                               BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
0129           const filesystem::path&>::type exe,
0130       Args&& args, Inits&&... inits)
0131       : basic_process(default_process_launcher()(executor_type(context.get_executor()),
0132        exe, std::forward<Args>(args), std::forward<Inits>(inits)...))
0133   {
0134   }
0135 
0136   /// Attach to an existing process
0137   explicit basic_process(executor_type exec, pid_type pid) : process_handle_(std::move(exec), pid) {}
0138 
0139   /// Attach to an existing process and the internal handle
0140   explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle)
0141         : process_handle_(std::move(exec), pid, native_handle) {}
0142 
0143   /// Create an invalid handle
0144   explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {}
0145 
0146   /// Attach to an existing process
0147   template <typename ExecutionContext>
0148   explicit basic_process(ExecutionContext & context, pid_type pid,
0149                          typename std::enable_if<
0150                              std::is_convertible<ExecutionContext&,
0151                                 BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
0152        : process_handle_(context, pid) {}
0153 
0154   /// Attach to an existing process and the internal handle
0155   template <typename ExecutionContext>
0156   explicit basic_process(ExecutionContext & context, pid_type pid, native_handle_type native_handle,
0157                          typename std::enable_if<
0158                             std::is_convertible<ExecutionContext&, 
0159                                 BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
0160       : process_handle_(context.get_executor(), pid, native_handle) {}
0161 
0162   /// Create an invalid handle
0163   template <typename ExecutionContext>
0164   explicit basic_process(ExecutionContext & context,
0165                          typename std::enable_if<
0166                              is_convertible<ExecutionContext&, 
0167                                 BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
0168      : process_handle_(context.get_executor()) {}
0169 
0170 
0171 
0172   /// Destruct the handle and terminate the process if it wasn't detached.
0173   ~basic_process()
0174   {
0175     process_handle_.terminate_if_running();
0176   }
0177 
0178   /// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown.
0179   /** Maybe be ignored by the subprocess. */
0180   void interrupt()
0181   {
0182     error_code ec;
0183     interrupt(ec);
0184     if (ec)
0185       throw system_error(ec, "interrupt failed");
0186 
0187   }
0188   /// Throwing @overload void interrupt()
0189   void interrupt(error_code & ec)
0190   {
0191     process_handle_.interrupt(ec);
0192   }
0193 
0194   /// Throwing @overload void request_exit(error_code & ec)
0195   void request_exit()
0196   {
0197     error_code ec;
0198     request_exit(ec);
0199     if (ec)
0200       throw system_error(ec, "request_exit failed");
0201   }
0202   /// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
0203   void request_exit(error_code & ec)
0204   {
0205     process_handle_.request_exit(ec);
0206   }
0207 
0208   /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
0209   void suspend(error_code &ec)
0210   {
0211     process_handle_.suspend(ec);
0212   }
0213 
0214   /// Send the process a signal requesting it to stop. This may rely on undocumented functions.
0215   void suspend()
0216   {
0217     error_code ec;
0218     suspend(ec);
0219     if (ec)
0220         detail::throw_error(ec, "suspend");
0221   }
0222 
0223 
0224   /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
0225   void resume(error_code &ec)
0226   {
0227     process_handle_.resume(ec);  
0228   }
0229 
0230   /// Send the process a signal requesting it to resume. This may rely on undocumented functions.
0231   void resume()
0232   {
0233       error_code ec;
0234       suspend(ec);
0235       if (ec)
0236           detail::throw_error(ec, "resume");
0237   }
0238 
0239   /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
0240   void terminate()
0241   {
0242     error_code ec;
0243     terminate(ec);
0244     if (ec)
0245       detail::throw_error(ec, "terminate failed");
0246   }
0247   /// Unconditionally terminates the process and stores the exit code in exit_status.
0248   void terminate(error_code & ec)
0249   {
0250     process_handle_.terminate(exit_status_, ec);
0251   }
0252 
0253   /// Throwing @overload wait(error_code & ec)
0254   int wait()
0255   {
0256     error_code ec;
0257     if (running(ec))
0258       process_handle_.wait(exit_status_, ec);
0259     if (ec)
0260       detail::throw_error(ec, "wait failed");
0261     return exit_code();
0262   }
0263   /// Waits for the process to exit, store the exit code internally and return it.
0264   int wait(error_code & ec)
0265   {
0266     if (running(ec))
0267         process_handle_.wait(exit_status_, ec);
0268     return exit_code();
0269   }
0270 
0271   /// Detach the process.
0272   handle_type detach()
0273   {
0274 #if defined(BOOST_PROCESS_V2_STANDALONE)
0275     return std::exchange(process_handle_, get_executor());
0276 #else
0277     return boost::exchange(process_handle_, get_executor());
0278 #endif
0279   }
0280   /// Get the native
0281   native_handle_type native_handle() {return process_handle_.native_handle(); }
0282   /// Return the evaluated exit_code.
0283   int exit_code() const
0284   {
0285     return evaluate_exit_code(exit_status_);
0286   }
0287 
0288   /// Get the id of the process;
0289   pid_type id() const {return process_handle_.id();}
0290 
0291   /// The native handle of the process. 
0292   /** This might be undefined on posix systems that only support signals */
0293   native_exit_code_type native_exit_code() const
0294   {
0295     return exit_status_;
0296   }
0297   /// Checks if the current process is running. 
0298   /** If it has already completed the exit code will be stored internally 
0299    * and can be obtained by calling `exit_code.
0300    */
0301   bool running()
0302   {
0303     error_code ec;
0304     native_exit_code_type exit_code{};
0305     auto r =  process_handle_.running(exit_code, ec);
0306     if (!ec && !r)
0307       exit_status_ = exit_code;
0308     else
0309       detail::throw_error(ec, "running failed");
0310 
0311     return r;
0312   }
0313 
0314   /// Throwing @overload bool running(error_code & ec)
0315   bool running(error_code & ec) noexcept
0316   {
0317     native_exit_code_type exit_code{};
0318     auto r =  process_handle_.running(exit_code, ec);
0319     if (!ec && !r)
0320       exit_status_ = exit_code;
0321     return r;
0322   }
0323   
0324   /// Check if the process is referring to an existing process.
0325   /** Note that this might be a process that already exited.*/
0326   bool is_open() const { return process_handle_.is_open(); }
0327   
0328   /// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
0329   template <BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
0330             WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
0331   BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int))
0332   async_wait(WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
0333   {
0334     return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void (error_code, int)>(
0335       async_wait_op_{process_handle_, exit_status_}, handler, process_handle_);
0336   }
0337 
0338 private:
0339   template<typename Executor1>
0340   friend struct basic_process;
0341 
0342   basic_process_handle<Executor> process_handle_;
0343   native_exit_code_type exit_status_{detail::still_active};
0344 
0345   
0346   struct async_wait_op_
0347   {
0348     basic_process_handle<Executor> & handle;
0349     native_exit_code_type & res;
0350 
0351     template<typename Self>
0352     void operator()(Self && self)
0353     {
0354       if (!process_is_running(res))
0355       {
0356         struct completer
0357         {
0358             int code;
0359             typename std::decay<Self>::type self;
0360             void operator()()
0361             {
0362                 self.complete(error_code{}, evaluate_exit_code(code));
0363             }
0364         };
0365 
0366         BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(),
0367                                               completer{static_cast<int>(res), std::move(self)});
0368       }
0369       else
0370         handle.async_wait(std::move(self));
0371     }
0372 
0373     template<typename Self>
0374     void operator()(Self && self, error_code ec, native_exit_code_type code)
0375     {
0376       if (!ec && process_is_running(code))
0377         handle.async_wait(std::move(self));
0378       else
0379       {
0380         if (!ec)
0381           res = code;
0382         std::move(self).complete(ec, evaluate_exit_code(code));
0383       }
0384     }
0385   };
0386 };
0387 
0388 /// Process with the default executor.
0389 typedef basic_process<> process;
0390 
0391 BOOST_PROCESS_V2_END_NAMESPACE
0392 
0393 
0394 #endif //BOOST_PROCESS_V2_PROCESS_HPP