process.hpp
12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
// 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/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 ... Inits>
explicit basic_process(
executor_type executor,
const filesystem::path& exe,
std::initializer_list<wstring_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);
}
/// 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))
wait(ec);
if (ec)
detail::throw_error(ec, "wait failed");
return exit_code();
}
/// Waits for the process to exit, store the exit code internall 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(); }
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 portable 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