Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
ProcessRunner.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_ProcessRunner_h_
5#define _Stroika_Foundation_Execution_ProcessRunner_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <filesystem>
10#include <optional>
11
14#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Containers/Mapping.h"
16#include "Stroika/Foundation/Containers/Sequence.h"
18#include "Stroika/Foundation/Execution/CommandLine.h"
19#include "Stroika/Foundation/Execution/Process.h"
20#include "Stroika/Foundation/Execution/Signals.h"
24
25// DEPRECATED - only while we have deprecated apis for use of progressmontior in this class
26#include "ProgressMonitor.h"
27
28/**
29 * TODO:
30 * @todo After we lose DEPRECATED APIS (STREAM STUFF) - Run and RunInBackground can become const methods
31 *
32 * @todo Redo POSIX impl using vfork () or http://linux.die.net/man/3/posix_spawn
33 *
34 * @todo Fix POSIX version to use vfork() instead of fork () - but carefully! Must setup data just so.
35 *
36 * @todo Fix POSIX version to properly handle reading and writing streams at the same time to avoid deadlock
37 * in finite kernel buffer sizes.
38 *
39 * @todo Fix POSIX version to use pipe2 and close appropriate open file descriptors (and other 'clean invoke' stuff.
40 *
41 * @todo Redo DWORD waitResult = ::WaitForMultipleObjects()... logic to wait on thread and each read/write socket
42 * with select() AND somehow maybe eventually wait on streams (so we don't have to pre-read it all)
43 *
44 * @todo Make sure it handles well without blocking
45 * (tricks I had todo in HF - forcing extra reads so writes wouldn't block).
46 * Currently structured to work off a single runnable, which implies works off a single thread. That implies
47 * it must use select() - probably a good idea anyhow - on each socket used for operations (windows and POSIX).
48 *
49 * So data pusher/buffer loop does select on external streams to see if data available.
50 *
51 * This implies I must also be able to do the moral equivalent of selects on my BinaryInput/Output streams? Maybe,
52 * unless I do all the buffering... But at least for the stdin stream - I need to be able to check if/when there
53 * is new data available!!! TRICKY
54 *
55 * @todo Decide on/document semantics if there is a change in setting STDIN/SETDOUT etc while a runner exists?
56 * If error - always detectable?
57 *
58 * And related - what if we create a runner, and then destroy the object? How to assure runner fully
59 * destroyed? Blocking/waiting or error or detached state?
60 *
61 * @todo Add optional hook to be run (at least for POSIX) inside the FORKED process, before the exec.
62 * Can be used to reset signals, and/or close file descriptors. Maybe have optional
63 * flag to auto-do this stuff and or have a preset value hook proc do do most standard things.
64 *
65 * Design Goals:
66 * o Be able to run simple processes and capture output with little overhead, and very easy to do
67 * (like perl backticks).
68 *
69 * o Be able to support pipes between processes (either within the shell, or between Stroika threads)
70 *
71 * o Support large data and blocking issues properly - automating avoidance of pipe full bugs
72 * which block processes
73 *
74 * o Efficient/Low performance overhead
75 *
76 * o For POSIX - simple to cleanly cleanup open sockets/resources (not needed on windows)
77 *
78 * o Separate threading implementation from API, so easy to externally specify the thread
79 * stuff runs on (e.g. so you can use thread pools to run the processes).
80 *
81 * o Work with stroika streams so its easy to have user-defined producers and consumers, and
82 * easy to hook together TextStreams (wrappers) - for format conversion/piping.
83 *
84 * \em Design Overview
85 * o
86 *
87 */
88
90
91 using Characters::String;
92 using Containers::Mapping;
93 using Containers::Sequence;
94
95 /**
96 * \brief Run the given command, and optionally support stdin/stdout/stderr as streams (either sync with Run, RunInBackground)
97 *
98 * \note ProcessRunner searches the PATH for the given executable: it need not be a full or even relative to
99 * cwd path.
100 *
101 * \note Historical Note:
102 * IDEA HERE IS FROM KDJ - Do something like python/perl stuff for managing subprocesses easily.
103 *
104 * Look input stream, output stream(or streams - stdout/stderr) - and some kind of external process control
105 * so can say WIAT or Terminate.
106 *
107 * Simple portable wrapper.
108 *
109 * Could use simple singly threaded approach used in TypeNValue ReportDefinition::RunExternalProcess_ (const SDKString& cmdLine, const SDKString& currentDir, const BLOBs::BLOB& stdinBLOB, const ContentType& resultFormat, float timeout)
110 * except that code has the defect that when the input pipe is full, and there is nothing in the output pipes
111 * it busy waits. We COULD fix this by doing a select.
112 *
113 * OR - as KDJ suggests - create 3 threads - one that just reads on stdout, one that just reads on stderr, and one that
114 * spits into stdin.
115 *
116 * The caller of 'subprocess' then would just wait on each of the 3 subprocesses (or would implement the aforementioned
117 * looping over reads/writes/selects etc).
118 *
119 * \par Example Usage
120 * \code
121 * String name = get<0> (ProcessRunner{"uname"}.Run (String {})).Trim ();
122 * \endcode
123 *
124 * \par Example Usage
125 * \code
126 * ProcessRunner pr{"echo hi mom"};
127 * auto [stdOutStr, stdErrStr] = pr.Run ("");
128 * EXPECT_EQ (stdOutStr.Trim (), "hi mom");
129 * EXPECT_EQ (stdErrStr, "");
130 * \endcode
131 *
132 * \par Example Usage
133 * \code
134 * ProcessRunner pr{"cat"};
135 * Memory::BLOB kData_{ Memory::BLOB::FromRaw ("this is a test") };
136 * Streams::MemoryStream::Ptr<byte> processStdIn = Streams::MemoryStream::New<byte> (kData_ );
137 * Streams::MemoryStream::Ptr<byte> processStdOut = Streams::MemoryStream::New<byte> ();
138 * pr.Run (processStdIn, processStdOut).ThrowIfFailed ();
139 * EXPECT_EQ (processStdOut.ReadAll (), kData_);
140 * \endcode
141 *
142 */
144 public:
145 static constexpr CommandLine::WrapInShell kDefaultShell =
146#if qStroika_Foundation_Common_Platform_Windows
147 CommandLine::WrapInShell::eWindowsCMD
148#else
149 CommandLine::WrapInShell::eBash
150#endif
151 ;
152
153 public:
154 /**
155 */
156 struct Options {
157 /**
158 * \brief pwd/cwd of the created process
159 * defaults to 'missing'. If missing, then WellKnownDirectories::GetTemporary () is
160 * used (since this is a generally safe place to run an executable); use filesystem::current_path () if that is the intention.
161 */
162 optional<filesystem::path> fWorkingDirectory;
163
164 /**
165 * If provided, child executed with this replacing its 'environment'; Variant 'Sequence<path>' means just the PATH part replaced.
166 * If Mapping<String,...> converted to SDKString codepage, and if SDKString provided, used as-is.
167 */
168 optional<variant<Sequence<filesystem::path>, Mapping<String, String>, Mapping<Characters::SDKString, Characters::SDKString>>> fEnvironment;
169
170 /**
171 * If true, then any nullptr input / output pipes are replaced with /dev/null (or equivalent)
172 * And any 'terminal' associated with the calling process is eliminated from the child
173 * process.
174 *
175 * Case POSIX:
176 * This also detaches from the terminal driver, to avoid spurious SIGHUP
177 * and SIGTTIN and SIGTTOU (setsid);
178 *
179 * Case Windoze:
180 * From: https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
181 * DETACHED_PROCESS - the new process does not inherit its parent's console
182 * This flag is ignored if the application is not a console application, or if it is used with either CREATE_NEW_CONSOLE or DETACHED_PROCESS
183 *
184 * \note replaces the Stroika v2.1 DetachedProcessRunner API
185 *
186 * \note as of Stroika v3.0d23, Run () API is not compatible with fDetached=true - use RunInBackground () API instead.
187 * This restriction may be lifted in a future release (if I can figure out what it means cleanly).
188 */
189 bool fDetached{false};
190
191#if qStroika_Foundation_Common_Platform_POSIX
192 /**
193 * \brief set umask of child process
194 *
195 * mostly harmless, not clearly needed, but suggested in http://codingfreak.blogspot.com/2012/03/daemon-izing-process-in-linux.html
196 */
197 optional<mode_t> fChildUMask{027};
198#endif
199
200#if qStroika_Foundation_Common_Platform_Windows
201 /**
202 * From: https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
203 * CONSOLE handle from this app not passed to child process. Obviates fDetachConsole.
204 */
205 bool fCreateNoWindow : 1 {true};
206
207#endif
208 };
209
210 public:
211 /**
212 * \brief Construct ProcessRunner with a CommandLine to run (doesn't actually RUN til you call Run or RunInBackground).
213 *
214 * \note overload with executable allows specifying an alternate executable to run, even though args[0] will be what is reported
215 * to that application (a somewhat common trick in unix-land).
216 *
217 * \note overload with String commandLine:
218 * Simple commands are run directly, and strings with apparent shell-isms, like pipes and quotes etc, are run through kDefaultShell.
219 * This overload is handy, but easy to explicitly control shell used with CommandLine argument instead.
220 */
221 ProcessRunner () = delete;
222 ProcessRunner (const ProcessRunner&) = delete;
223
224#if qCompilerAndStdLib_DefaultMemberInitializerNeededEnclosingForDefaultFunArg_Buggy
225 ProcessRunner (const filesystem::path& executable, const CommandLine& args);
226 ProcessRunner (const CommandLine& args);
227 ProcessRunner (const String& commandLine);
228 ProcessRunner (const filesystem::path& executable, const CommandLine& args, const Options& o);
229 ProcessRunner (const CommandLine& args, const Options& o);
230 ProcessRunner (const String& commandLine, const Options& o);
231#else
232 ProcessRunner (const filesystem::path& executable, const CommandLine& args, const Options& o = {});
233 ProcessRunner (const CommandLine& args, const Options& o = {});
234 ProcessRunner (const String& commandLine, const Options& o = {});
235#endif
236
237 public:
238 nonvirtual ProcessRunner& operator= (const ProcessRunner&) = delete;
239
240 public:
241#if qStroika_Foundation_Common_Platform_POSIX
242 using ExitStatusType = uint8_t;
243#elif qStroika_Foundation_Common_Platform_Windows
244 using ExitStatusType = DWORD;
245#else
246 using ExitStatusType = int;
247#endif
248
249 public:
250 class Exception;
251
252 public:
253 /**
254 */
255 nonvirtual CommandLine GetCommandLine () const;
256 nonvirtual void SetCommandLine (const CommandLine& args);
257
258 public:
259 /**
260 */
261 nonvirtual Options GetOptions () const;
262 nonvirtual void SetOptions (const Options& o);
263
264 public:
265 /**
266 * Zero means success. Run() returns optional<ProcessResultType> by reference, and that
267 * value is only provided if the child process exited. If exited, we return the exit
268 * status and signal number (if any) - see waitpid - http://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html
269 */
270 struct [[nodiscard]] ProcessResultType {
271 optional<ExitStatusType> fExitStatus;
272 optional<SignalID> fTerminatedByUncaughtSignalNumber;
273
274 nonvirtual void ThrowIfFailed ();
275
276 /**
277 * Purely for debugging / diagnostic purposes. Don't count on this format.
278 */
279 nonvirtual String ToString () const;
280 };
281
282 public:
283 /**
284 * \brief Run () options for mapping Strings - what code page converters to use.
285 *
286 * \note defaults now are UTF-8, but don't count on this if you care about the encoding used (subject to change).
287 */
289 /**
290 * Input refers to the input of the sub-process being run. So this conversion is applied before sending the data to
291 * that process.
292 */
293 optional<Characters::CodeCvt<>> fInputCodeCvt;
294
295 /**
296 * Output refers to the output of the sub-process being run. So this conversion is applied to the data retrieved
297 * from that process.
298 */
299 optional<Characters::CodeCvt<>> fOutputCodeCvt;
300 };
301
302 public:
303 /**
304 * \brief Run the given external command/process (set by constructor) - with the given arguments, and block until that completes and return the results
305 *
306 * Run the given external command/process (set by constructor) - with the given arguments, and block until
307 * that completes and return the results.
308 *
309 * Run STREAMS overload:
310 * This overload takes input/output/error binary streams
311 *
312 * STDIN/STDOUT/STDERR:
313 * * If nullptr/not specified, will redirected to /dev/null
314 *
315 * Run STRING overload:
316 * This is the simplest API. Just pass in a string, and get back a string (first one is stdout, second is stderr).
317 *
318 * The cmdStdInValue is passed as stdin (stream) to the subprocess.
319 *
320 * Run ()/0:
321 * Treat as Run("") - so stderr captured automatically and inserted into exception (and logged).
322 *
323 * BOTH overloads will throw if there is any sort of error, including error exit from the process called.
324 * Use RunInBackground () to examine the results of the sub-process.
325 *
326 * \note Exceptions:
327 * A number of issues before the process is run will generate an exception.
328 * If the argument processResult is null, failure (non SUCCESS exit or signal termination) will trigger an exception, and otherwise the
329 * parameter *processResult will be filled in.
330 *
331 * \note if this is called with a timeout, and it times out, the child is killed immediately upon timeout.
332 * To avoid this behavior, use RunInBackground
333 *
334 * \par Example Usage (using strings in/out)
335 * \code
336 * String name = get<0> (ProcessRunner{"uname"}.Run (String {})).Trim ();
337 * \endcode
338 *
339 * \par Example Usage (using binary streams)
340 * \code
341 * ProcessRunner pr{"cat"};
342 * Memory::BLOB kData_{ Memory::BLOB::FromRaw ("this is a test") };
343 * Streams::MemoryStream::Ptr<byte> processStdIn = Streams::MemoryStream::New<byte> (kData_);
344 * Streams::MemoryStream::Ptr<byte> processStdOut = Streams::MemoryStream::New<byte> ();
345 * pr.Run (processStdIn, processStdOut);
346 * EXPECT_EQ (processStdOut.ReadAll (), kData_);
347 * \endcode
348 *
349 * @see RunInBackground
350 */
351 nonvirtual void Run (const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
352 const Streams::OutputStream::Ptr<byte>& error = nullptr, Time::DurationSeconds timeout = Time::kInfinity);
353 nonvirtual tuple<Characters::String, Characters::String> Run (const Characters::String& cmdStdInValue = ""sv,
354 const StringOptions& stringOpts = {},
355 Time::DurationSeconds timeout = Time::kInfinity);
356
357 public:
358 class BackgroundProcess;
359
360 public:
361 /**
362 * \brief Run the given external command/process (set by constructor) - with the given arguments in the background,
363 * and return a handle to the results.
364 *
365 * This function is generally quick, and non-blocking - just creates a thread todo the work.
366 *
367 * \note it is perfectly legal to launch a subprocess, and not track it in any way, just ignoring (not saving)
368 * the BackgroundProcess object.
369 *
370 * \note if options.fDetached is true, as of Stroika v3.0d23, then we REQUIRE (in==nullptr, out==nullptr, and err==nullptr)
371 * and this means no data is written to the detached process, and no data is read from it.
372 *
373 * @see Run
374 */
376 const Streams::OutputStream::Ptr<byte>& out = nullptr,
377 const Streams::OutputStream::Ptr<byte>& error = nullptr);
378
379 private:
380 /**
381 * @brief like CreateDetailedRunnable_, but doesnt track pid or result - just creates runnable to execute
382 */
383 nonvirtual function<void ()> CreateSimpleRunnable_ ();
384
385 private:
386 /**
387 * Capture the process results and running PID. NOTE - this uses Syncrhonized, since its generally looked at and set from
388 * two different threads.
389 */
390 struct DetailedRunnableRep_ {
392 Synchronized<optional<pid_t>> fRunningPID;
393 };
394
395 private:
396 /**
397 * @brief DOESNT run anything - but creates a function object that when run will do the work of running the process, and returns a shared DetailedRunnableRep_ to track the progress of the runnable when run
398 *
399 * Note that 'in' will be sent to the stdin of the subprocess, 'out' will be read from the
400 * stdout of the subprocess and error will be read from the stderr of the subprocess.
401 *
402 * Each of these CAN be null, and will if so, that will be interpreted as an empty stream
403 * (for in/stdin), and for out/error, just means the results will be redirected to /dev/null.
404 *
405 * Note the runnable holds onto shared_ptr<DetailedRunnableRep_> so it doesn't NEED to be by the caller - just hold
406 * onto it if you want to see the results.
407 */
408 nonvirtual tuple<function<void ()>, shared_ptr<DetailedRunnableRep_>> CreateDetailedRunnable_ ();
409
410 private:
411#if qStroika_Foundation_Common_Platform_POSIX
412 static void Process_Runner_POSIX_ (const shared_ptr<DetailedRunnableRep_>& runneeDetails,
413 [[maybe_unused]] const optional<filesystem::path>& executable, const CommandLine& cmdLine,
414 const ProcessRunner::Options& options, const Streams::InputStream::Ptr<byte>& in,
416#elif qStroika_Foundation_Common_Platform_Windows
417 static void Process_Runner_Windows_ (const shared_ptr<DetailedRunnableRep_>& runneeDetails,
418 const optional<filesystem::path>& executable, const CommandLine& cmdLine,
419 const ProcessRunner::Options& options, const Streams::InputStream::Ptr<byte>& in,
421#endif
422
423 private:
424 optional<filesystem::path> fExecutable_; // if omitted, derived from fArgs[0]
425 CommandLine fArgs_;
426 Options fOptions_;
427 Streams::InputStream::Ptr<byte> fStdIn_; // just while we support deprecated API
430 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
431
432 public:
433 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
434 const filesystem::path& executable, const CommandLine& args, const Streams::InputStream::Ptr<byte>& in,
435 const Streams::OutputStream::Ptr<byte>& out = nullptr, const Streams::OutputStream::Ptr<byte>& error = nullptr);
436 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
437 const CommandLine& args, const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
438 const Streams::OutputStream::Ptr<byte>& error = nullptr);
439 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
440 const String& commandLine, const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
441 const Streams::OutputStream::Ptr<byte>& error = nullptr)
442 : ProcessRunner{commandLine}
443 {
444 this->fStdIn_ = in;
445 this->fStdOut_ = out;
446 this->fStdErr_ = error;
447 }
448
449 [[deprecated ("Since Stroika v3.0d12 - use other overloads for ProcessRunner")]] ProcessRunner (
450 const filesystem::path& executable, const Containers::Sequence<String>& args, const Streams::InputStream::Ptr<byte>& in = nullptr,
451 const Streams::OutputStream::Ptr<byte>& out = nullptr, const Streams::OutputStream::Ptr<byte>& error = nullptr)
452 : ProcessRunner{executable, CommandLine{args}}
453 {
454 this->fStdIn_ = in;
455 this->fStdOut_ = out;
456 this->fStdErr_ = error;
457 }
458
459 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
460 Run (optional<ProcessResultType>* processResult, ProgressMonitor::Updater progress = nullptr, Time::DurationSeconds timeout = Time::kInfinity);
461
462 [[deprecated ("Since Stroika v3.0d12 pass in/out/error(can be nullptr) in RunInbackground() method")]] BackgroundProcess
463 RunInBackground (ProgressMonitor::Updater progress);
464
465 [[deprecated ("Since Stroika v3.0d23d")]] BackgroundProcess RunInBackground (const Streams::InputStream::Ptr<byte>& in,
468 [[maybe_unused]] ProgressMonitor::Updater progress);
469
470 public:
471 //DEPRECATED
472 [[deprecated ("Since Stroika v3.0d23d")]] void Run (const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out,
473 const Streams::OutputStream::Ptr<byte>& error, ProgressMonitor::Updater progress,
474 Time::DurationSeconds timeout = Time::kInfinity);
475 [[deprecated ("Since Stroika v3.0d23d")]] tuple<Characters::String, Characters::String>
476 Run (const Characters::String& cmdStdInValue, const StringOptions& stringOpts, ProgressMonitor::Updater progress,
477 Time::DurationSeconds timeout = Time::kInfinity);
478
479 public:
480 /**
481 */
482 [[deprecated ("Since Stroika v3.0d12 - use GetOptions().fWorkingDirectory")]] optional<filesystem::path> GetWorkingDirectory () const;
483 [[deprecated ("Since Stroika v3.0d12 - use SetOptions({.fWorkingDirectory})")]] void SetWorkingDirectory (const optional<filesystem::path>& d);
484
485 public:
486 /**
487 * If empty, stdin will not be empty (redirected from /dev/null).
488 *
489 * Otherwise, the stream will be 'read' by the ProcessRunner and 'fed' downstream to
490 * the running subprocess.
491 */
492 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::InputStream::Ptr<byte>
493 GetStdIn () const;
494 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
495 SetStdIn (const Streams::InputStream::Ptr<byte>& in);
496
497 public:
498 /**
499 * If empty, stdout will not be captured (redirected to /dev/null)
500 */
501 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::OutputStream::Ptr<byte>
502 GetStdOut () const;
503 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
504 SetStdOut (const Streams::OutputStream::Ptr<byte>& out);
505
506 public:
507 /**
508 * If empty, stderr will not be captured (redirected to /dev/null)
509 */
510 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::OutputStream::Ptr<byte>
511 GetStdErr () const;
512 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
513 SetStdErr (const Streams::OutputStream::Ptr<byte>& err);
514 };
515
516 /**
517 * Exceptions generated by ProcessRunner are typically of this sort.
518 */
520 private:
522
523 public:
524 /**
525 */
526 Exception (const String& failureMessage, const optional<String>& stderrFragment = nullopt,
527 const optional<ExitStatusType>& wExitStatus = nullopt, const optional<SignalID>& wTermSig = nullopt);
528
529 public:
530 /**
531 * High level summary - not including stuff like exit status, stderr results etc
532 */
534
535 public:
536 const optional<String> fStderrFragment;
537
538 public:
539 const optional<ExitStatusType> fExitStatus;
540
541 public:
542 const optional<SignalID> fTermSignal;
543
544 private:
545 static String mkMsg_ (const String& errorMessage, const optional<String>& stderrSubset, const optional<ExitStatusType>& wExitStatus,
546 const optional<SignalID>& wTermSig);
547 };
548
549 /**
550 * Support more controlled running of sub-process, where wait timeouts don't necessarily kill the child process.
551 *
552 * \note it is perfectly legal to launch a subprocess, and not track it in any way, just ignoring (not saving)
553 * the BackgroundProcess object.
554 */
556 private:
558
559 public:
560 BackgroundProcess (const BackgroundProcess&) = default;
561
562 public:
563 /**
564 * Return missing if process still running, and if completed, return the results.
565 */
566 nonvirtual optional<ProcessResultType> GetProcessResult () const;
567
568 public:
569 /**
570 * \brief maybe missing if process not yet (or ever successfully) launched. Child process may have
571 * already exited by the time this is returned.
572 */
573 optional<pid_t> GetChildProcessID () const;
574
575 public:
576 /**
577 * \brief wait until GetChildProcessID () returns a valid answer, or until the process failed to start
578 * (in which case calls PropagateIfException).
579 */
580 nonvirtual void WaitForStarted (Time::DurationSeconds timeout = Time::kInfinity) const;
581
582 public:
583 /**
584 *
585 * @see Join ()
586 * @see JoinUntil ()
587 */
588 nonvirtual void WaitForDone (Time::DurationSeconds timeout = Time::kInfinity) const;
589
590 public:
591 /**
592 * \brief Join () does WaitForDone () and throw exception if there was any error (see PropagateIfException).
593 *
594 * \note Aliases - this used to be called WaitForDoneAndPropagateErrors; but used the name Join () to mimic the name used with Threads - NOT
595 * because that's used in the implementation, but because its essentially logically the same thing.
596 *
597 * @see JoinUntil ()
598 * @see WaitForDone ()
599 */
600 nonvirtual void Join (Time::DurationSeconds timeout = Time::kInfinity) const;
601
602 public:
603 /**
604 * \brief WaitForDoneUntil () and throw exception if there was any error (see PropagateIfException).
605 *
606 * @see Join ()
607 * @see WaitForDone ()
608 */
609 nonvirtual void JoinUntil (Time::TimePointSeconds timeoutAt) const;
610
611 public:
612 /**
613 * If the process has completed with an error, throw exception reflecting that failure.
614 *
615 * \note if the process has not completed, this likely does nothing.
616 */
617 nonvirtual void PropagateIfException () const;
618
619 public:
620 /**
621 * If the process is still running, terminate it.
622 */
623 nonvirtual void Terminate ();
624
625 public:
626 /**
627 * Purely for debugging / diagnostic purposes. Don't count on this format.
628 */
629 nonvirtual String ToString () const;
630
631 private:
632 struct Rep_ {
633 virtual ~Rep_ () = default;
634 Thread::CleanupPtr fProcessRunner{Thread::CleanupPtr::eAbortBeforeWaiting};
635 shared_ptr<DetailedRunnableRep_> fDetailedRunnableRep_;
636 };
637 shared_ptr<Rep_> fRep_;
638 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
639
640 private:
641 friend class ProcessRunner;
642 };
643
644}
645
646/*
647 ********************************************************************************
648 ***************************** Implementation Details ***************************
649 ********************************************************************************
650 */
651#include "ProcessRunner.inl"
652
653#endif /*_Stroika_Foundation_Execution_ProcessRunner_h_*/
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
nonvirtual void WaitForStarted(Time::DurationSeconds timeout=Time::kInfinity) const
wait until GetChildProcessID () returns a valid answer, or until the process failed to start (in whic...
nonvirtual optional< ProcessResultType > GetProcessResult() const
nonvirtual void WaitForDone(Time::DurationSeconds timeout=Time::kInfinity) const
nonvirtual void Join(Time::DurationSeconds timeout=Time::kInfinity) const
Join () does WaitForDone () and throw exception if there was any error (see PropagateIfException).
nonvirtual void JoinUntil(Time::TimePointSeconds timeoutAt) const
WaitForDoneUntil () and throw exception if there was any error (see PropagateIfException).
optional< pid_t > GetChildProcessID() const
maybe missing if process not yet (or ever successfully) launched. Child process may have already exit...
Run the given command, and optionally support stdin/stdout/stderr as streams (either sync with Run,...
Streams::InputStream::Ptr< byte > GetStdIn() const
Streams::OutputStream::Ptr< byte > GetStdOut() const
nonvirtual void Run(const Streams::InputStream::Ptr< byte > &in, const Streams::OutputStream::Ptr< byte > &out=nullptr, const Streams::OutputStream::Ptr< byte > &error=nullptr, Time::DurationSeconds timeout=Time::kInfinity)
Run the given external command/process (set by constructor) - with the given arguments,...
ProcessRunner()=delete
Construct ProcessRunner with a CommandLine to run (doesn't actually RUN til you call Run or RunInBack...
nonvirtual BackgroundProcess RunInBackground(const Streams::InputStream::Ptr< byte > &in=nullptr, const Streams::OutputStream::Ptr< byte > &out=nullptr, const Streams::OutputStream::Ptr< byte > &error=nullptr)
Run the given external command/process (set by constructor) - with the given arguments in the backgro...
Streams::OutputStream::Ptr< byte > GetStdErr() const
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
EXPECTED::value_type ThrowIfFailed(const EXPECTED &e)
Definition Throw.inl:158
Run () options for mapping Strings - what code page converters to use.