Stroika Library 3.0d24
 
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 an external command, with stdin/stdout/stderr as strings or as streams - like perl backticks
97 *
98 * There are two ways to run, and the difference is all you really need to know:
99 *
100 * o Run () is synchronous. It returns the output, and THROWS on any failure - including the
101 * process merely exiting non-zero. Nothing to check; if it returns, it worked.
102 * o RunInBackground () hands back a BackgroundProcess you wait on and interrogate - exit status,
103 * signal, child pid - so you can treat a failed run as data instead of as an exception.
104 *
105 * Run () behaves as if it were RunInBackground (), then waiting, then
106 * ProcessResultType::ThrowIfFailed ().
107 *
108 * \note ProcessRunner searches the PATH for the given executable: it need not be a full or even relative to
109 * cwd path.
110 *
111 * \par Example Usage - Run (), the simple case
112 * \code
113 * String name = get<0> (ProcessRunner{"uname"}.Run (String {})).Trim ();
114 *
115 * ProcessRunner pr{"echo hi mom"};
116 * auto [stdOutStr, stdErrStr] = pr.Run (""); // throws if echo fails or exits non-zero
117 * EXPECT_EQ (stdOutStr.Trim (), "hi mom");
118 * \endcode
119 *
120 * \par Example Usage - RunInBackground (), when the exit status is data rather than an error
121 * \code
122 * ProcessRunner::BackgroundProcess bp = ProcessRunner{"grep pattern somefile"}.RunInBackground ();
123 * bp.WaitForDone ();
124 * // grep exits 1 for 'no match', which is an answer and not a failure
125 * optional<ProcessRunner::ProcessResultType> r = bp.GetProcessResult ();
126 * bool matched = r and r->fExitStatus == 0;
127 * \endcode
128 *
129 * \note Historical note: the idea came from KDJ - do something like python/perl subprocess handling,
130 * as a simple portable wrapper.
131 */
133 public:
134 static constexpr CommandLine::WrapInShell kDefaultShell =
135#if qStroika_Foundation_Common_Platform_Windows
136 CommandLine::WrapInShell::eWindowsCMD
137#else
138 CommandLine::WrapInShell::eBash
139#endif
140 ;
141
142 public:
143 /**
144 */
145 struct Options {
146 /**
147 * \brief pwd/cwd of the created process
148 * defaults to 'missing'. If missing, then WellKnownDirectories::GetTemporary () is
149 * used (since this is a generally safe place to run an executable); use filesystem::current_path () if that is the intention.
150 */
151 optional<filesystem::path> fWorkingDirectory;
152
153 /**
154 * If provided, child executed with this replacing its 'environment'; Variant 'Sequence<path>' means just the PATH part replaced.
155 * If Mapping<String,...> converted to SDKString codepage, and if SDKString provided, used as-is.
156 */
157 optional<variant<Sequence<filesystem::path>, Mapping<String, String>, Mapping<Characters::SDKString, Characters::SDKString>>> fEnvironment;
158
159 /**
160 * If true, then any nullptr input / output pipes are replaced with /dev/null (or equivalent)
161 * And any 'terminal' associated with the calling process is eliminated from the child
162 * process.
163 *
164 * Case POSIX:
165 * This also detaches from the terminal driver, to avoid spurious SIGHUP
166 * and SIGTTIN and SIGTTOU (setsid);
167 *
168 * Case Windoze:
169 * From: https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
170 * DETACHED_PROCESS - the new process does not inherit its parent's console
171 * 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
172 *
173 * \note replaces the Stroika v2.1 DetachedProcessRunner API
174 *
175 * \note as of Stroika v3.0d23, Run () API is not compatible with fDetached=true - use RunInBackground () API instead.
176 * This restriction may be lifted in a future release (if I can figure out what it means cleanly).
177 */
178 bool fDetached{false};
179
180#if qStroika_Foundation_Common_Platform_POSIX
181 /**
182 * \brief set umask of child process
183 *
184 * mostly harmless, not clearly needed, but suggested in http://codingfreak.blogspot.com/2012/03/daemon-izing-process-in-linux.html
185 */
186 optional<mode_t> fChildUMask{027};
187#endif
188
189#if qStroika_Foundation_Common_Platform_Windows
190 /**
191 * From: https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
192 * CONSOLE handle from this app not passed to child process. Obviates fDetachConsole.
193 */
194 bool fCreateNoWindow : 1 {true};
195
196#endif
197 };
198
199 public:
200 /**
201 * \brief Construct ProcessRunner with a CommandLine to run (doesn't actually RUN til you call Run or RunInBackground).
202 *
203 * \note overload with executable allows specifying an alternate executable to run, even though args[0] will be what is reported
204 * to that application (a somewhat common trick in unix-land).
205 *
206 * \note overload with String commandLine:
207 * Simple commands are run directly, and strings with apparent shell-isms, like pipes and quotes etc, are run through kDefaultShell.
208 * This overload is handy, but easy to explicitly control shell used with CommandLine argument instead.
209 */
210 ProcessRunner () = delete;
211 ProcessRunner (const ProcessRunner&) = delete;
212
213#if qCompilerAndStdLib_DefaultMemberInitializerNeededEnclosingForDefaultFunArg_Buggy
214 ProcessRunner (const filesystem::path& executable, const CommandLine& args);
215 ProcessRunner (const CommandLine& args);
216 ProcessRunner (const String& commandLine);
217 ProcessRunner (const filesystem::path& executable, const CommandLine& args, const Options& o);
218 ProcessRunner (const CommandLine& args, const Options& o);
219 ProcessRunner (const String& commandLine, const Options& o);
220#else
221 ProcessRunner (const filesystem::path& executable, const CommandLine& args, const Options& o = {});
222 ProcessRunner (const CommandLine& args, const Options& o = {});
223 ProcessRunner (const String& commandLine, const Options& o = {});
224#endif
225
226 public:
227 nonvirtual ProcessRunner& operator= (const ProcessRunner&) = delete;
228
229 public:
230#if qStroika_Foundation_Common_Platform_POSIX
231 using ExitStatusType = uint8_t;
232#elif qStroika_Foundation_Common_Platform_Windows
233 using ExitStatusType = DWORD;
234#else
235 using ExitStatusType = int;
236#endif
237
238 public:
239 class Exception;
240
241 public:
242 /**
243 */
244 nonvirtual CommandLine GetCommandLine () const;
245 nonvirtual void SetCommandLine (const CommandLine& args);
246
247 public:
248 /**
249 */
250 nonvirtual Options GetOptions () const;
251 nonvirtual void SetOptions (const Options& o);
252
253 public:
254 /**
255 * Zero means success. Run() returns optional<ProcessResultType> by reference, and that
256 * value is only provided if the child process exited. If exited, we return the exit
257 * status and signal number (if any) - see waitpid - http://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html
258 */
259 struct [[nodiscard]] ProcessResultType {
260 optional<ExitStatusType> fExitStatus;
261 optional<SignalID> fTerminatedByUncaughtSignalNumber;
262
263 nonvirtual void ThrowIfFailed ();
264
265 /**
266 * Purely for debugging / diagnostic purposes. Don't count on this format.
267 */
268 nonvirtual String ToString () const;
269 };
270
271 public:
272 /**
273 * \brief Run () options for mapping Strings - what code page converters to use.
274 *
275 * \note defaults now are UTF-8, but don't count on this if you care about the encoding used (subject to change).
276 */
278 /**
279 * Input refers to the input of the sub-process being run. So this conversion is applied before sending the data to
280 * that process.
281 */
282 optional<Characters::CodeCvt<>> fInputCodeCvt;
283
284 /**
285 * Output refers to the output of the sub-process being run. So this conversion is applied to the data retrieved
286 * from that process.
287 */
288 optional<Characters::CodeCvt<>> fOutputCodeCvt;
289 };
290
291 public:
292 /**
293 * \brief Run the command synchronously, returning its output, and THROW on any failure
294 *
295 * o STRING overload: pass stdin as a string, get back {stdout, stderr}. Run () with no
296 * argument means Run (""). The simplest form.
297 * o STREAMS overload: pass binary streams instead. Any of the three left nullptr is
298 * redirected to /dev/null.
299 *
300 * \note Exceptions - failure is ONLY reported by throwing ProcessRunner::Exception, whether that is
301 * something going wrong before the process starts, a non-zero exit (Exception::fExitStatus),
302 * or death by an uncaught signal (Exception::fTermSignal). The string overload also captures
303 * the child's stderr into Exception::fStderrFragment, usually the only thing that says WHY.
304 * To read an exit status instead of catching it, use RunInBackground ().
305 *
306 * \note if this is called with a timeout, and it times out, the child is killed immediately upon timeout.
307 * To avoid this behavior, use RunInBackground
308 *
309 * \par Example Usage (using strings in/out)
310 * \code
311 * String name = get<0> (ProcessRunner{"uname"}.Run (String {})).Trim ();
312 * \endcode
313 *
314 * \par Example Usage (using binary streams)
315 * \code
316 * ProcessRunner pr{"cat"};
317 * Memory::BLOB kData_{ Memory::BLOB::FromRaw ("this is a test") };
318 * Streams::MemoryStream::Ptr<byte> processStdIn = Streams::MemoryStream::New<byte> (kData_);
319 * Streams::MemoryStream::Ptr<byte> processStdOut = Streams::MemoryStream::New<byte> ();
320 * pr.Run (processStdIn, processStdOut);
321 * EXPECT_EQ (processStdOut.ReadAll (), kData_);
322 * \endcode
323 *
324 * @see RunInBackground
325 */
326 nonvirtual void Run (const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
327 const Streams::OutputStream::Ptr<byte>& error = nullptr, Time::DurationSeconds timeout = Time::kInfinity);
328 nonvirtual tuple<Characters::String, Characters::String> Run (const Characters::String& cmdStdInValue = ""sv,
329 const StringOptions& stringOpts = {},
330 Time::DurationSeconds timeout = Time::kInfinity);
331
332 public:
333 class BackgroundProcess;
334
335 public:
336 /**
337 * \brief Run the given external command/process (set by constructor) - with the given arguments in the background,
338 * and return a handle to the results.
339 *
340 * This function is generally quick, and non-blocking - just creates a thread todo the work.
341 *
342 * \note it is perfectly legal to launch a subprocess, and not track it in any way, just ignoring (not saving)
343 * the BackgroundProcess object.
344 *
345 * \note if options.fDetached is true, as of Stroika v3.0d23, then we REQUIRE (in==nullptr, out==nullptr, and err==nullptr)
346 * and this means no data is written to the detached process, and no data is read from it.
347 *
348 * @see Run
349 */
351 const Streams::OutputStream::Ptr<byte>& out = nullptr,
352 const Streams::OutputStream::Ptr<byte>& error = nullptr);
353
354 private:
355 /**
356 * @brief like CreateDetailedRunnable_, but doesnt track pid or result - just creates runnable to execute
357 */
358 nonvirtual function<void ()> CreateSimpleRunnable_ ();
359
360 private:
361 /**
362 * Capture the process results and running PID. NOTE - this uses Synchronized, since its generally looked at and set from
363 * two different threads.
364 */
365 struct DetailedRunnableRep_ {
367 Synchronized<optional<pid_t>> fRunningPID;
368 };
369
370 private:
371 /**
372 * @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
373 *
374 * Note that 'in' will be sent to the stdin of the subprocess, 'out' will be read from the
375 * stdout of the subprocess and error will be read from the stderr of the subprocess.
376 *
377 * Each of these CAN be null, and will if so, that will be interpreted as an empty stream
378 * (for in/stdin), and for out/error, just means the results will be redirected to /dev/null.
379 *
380 * Note the runnable holds onto shared_ptr<DetailedRunnableRep_> so it doesn't NEED to be by the caller - just hold
381 * onto it if you want to see the results.
382 */
383 nonvirtual tuple<function<void ()>, shared_ptr<DetailedRunnableRep_>> CreateDetailedRunnable_ ();
384
385 private:
386#if qStroika_Foundation_Common_Platform_POSIX
387 static void Process_Runner_POSIX_ (const shared_ptr<DetailedRunnableRep_>& runneeDetails,
388 [[maybe_unused]] const optional<filesystem::path>& executable, const CommandLine& cmdLine,
389 const ProcessRunner::Options& options, const Streams::InputStream::Ptr<byte>& in,
391#elif qStroika_Foundation_Common_Platform_Windows
392 static void Process_Runner_Windows_ (const shared_ptr<DetailedRunnableRep_>& runneeDetails,
393 const optional<filesystem::path>& executable, const CommandLine& cmdLine,
394 const ProcessRunner::Options& options, const Streams::InputStream::Ptr<byte>& in,
396#endif
397
398 private:
399 optional<filesystem::path> fExecutable_; // if omitted, derived from fArgs[0]
400 CommandLine fArgs_;
401 Options fOptions_;
402 Streams::InputStream::Ptr<byte> fStdIn_; // just while we support deprecated API
406
407 public:
408 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
409 const filesystem::path& executable, const CommandLine& args, const Streams::InputStream::Ptr<byte>& in,
410 const Streams::OutputStream::Ptr<byte>& out = nullptr, const Streams::OutputStream::Ptr<byte>& error = nullptr);
411 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
412 const CommandLine& args, const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
413 const Streams::OutputStream::Ptr<byte>& error = nullptr);
414 [[deprecated ("Since Stroika v3.0d12 - pass stdin/stdout/stderr to ProcessRunner Run() method (if needed)")]] ProcessRunner (
415 const String& commandLine, const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out = nullptr,
416 const Streams::OutputStream::Ptr<byte>& error = nullptr)
417 : ProcessRunner{commandLine}
418 {
419 this->fStdIn_ = in;
420 this->fStdOut_ = out;
421 this->fStdErr_ = error;
422 }
423
424 [[deprecated ("Since Stroika v3.0d12 - use other overloads for ProcessRunner")]] ProcessRunner (
425 const filesystem::path& executable, const Containers::Sequence<String>& args, const Streams::InputStream::Ptr<byte>& in = nullptr,
426 const Streams::OutputStream::Ptr<byte>& out = nullptr, const Streams::OutputStream::Ptr<byte>& error = nullptr)
427 : ProcessRunner{executable, CommandLine{args}}
428 {
429 this->fStdIn_ = in;
430 this->fStdOut_ = out;
431 this->fStdErr_ = error;
432 }
433
434 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
435 Run (optional<ProcessResultType>* processResult, ProgressMonitor::Updater progress = nullptr, Time::DurationSeconds timeout = Time::kInfinity);
436
437 [[deprecated ("Since Stroika v3.0d12 pass in/out/error(can be nullptr) in RunInbackground() method")]] BackgroundProcess
438 RunInBackground (ProgressMonitor::Updater progress);
439
440 [[deprecated ("Since Stroika v3.0d23d")]] BackgroundProcess RunInBackground (const Streams::InputStream::Ptr<byte>& in,
443 [[maybe_unused]] ProgressMonitor::Updater progress);
444
445 public:
446 //DEPRECATED
447 [[deprecated ("Since Stroika v3.0d23d")]] void Run (const Streams::InputStream::Ptr<byte>& in, const Streams::OutputStream::Ptr<byte>& out,
448 const Streams::OutputStream::Ptr<byte>& error, ProgressMonitor::Updater progress,
449 Time::DurationSeconds timeout = Time::kInfinity);
450 [[deprecated ("Since Stroika v3.0d23d")]] tuple<Characters::String, Characters::String>
451 Run (const Characters::String& cmdStdInValue, const StringOptions& stringOpts, ProgressMonitor::Updater progress,
452 Time::DurationSeconds timeout = Time::kInfinity);
453
454 public:
455 /**
456 */
457 [[deprecated ("Since Stroika v3.0d12 - use GetOptions().fWorkingDirectory")]] optional<filesystem::path> GetWorkingDirectory () const;
458 [[deprecated ("Since Stroika v3.0d12 - use SetOptions({.fWorkingDirectory})")]] void SetWorkingDirectory (const optional<filesystem::path>& d);
459
460 public:
461 /**
462 * If empty, stdin will not be empty (redirected from /dev/null).
463 *
464 * Otherwise, the stream will be 'read' by the ProcessRunner and 'fed' downstream to
465 * the running subprocess.
466 */
467 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::InputStream::Ptr<byte>
468 GetStdIn () const;
469 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
470 SetStdIn (const Streams::InputStream::Ptr<byte>& in);
471
472 public:
473 /**
474 * If empty, stdout will not be captured (redirected to /dev/null)
475 */
476 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::OutputStream::Ptr<byte>
477 GetStdOut () const;
478 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
479 SetStdOut (const Streams::OutputStream::Ptr<byte>& out);
480
481 public:
482 /**
483 * If empty, stderr will not be captured (redirected to /dev/null)
484 */
485 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] Streams::OutputStream::Ptr<byte>
486 GetStdErr () const;
487 [[deprecated ("Since Stroika v3.0d12 - pass in/out/error streams(can be nullptr) to Run method instead of CTOR")]] void
488 SetStdErr (const Streams::OutputStream::Ptr<byte>& err);
489 };
490
491 /**
492 * Exceptions generated by ProcessRunner are typically of this sort.
493 */
495 private:
497
498 public:
499 /**
500 */
503
504 public:
505 /**
506 * High level summary - not including stuff like exit status, stderr results etc
507 */
509
510 public:
511 const optional<String> fStderrFragment;
512
513 public:
514 const optional<ExitStatusType> fExitStatus;
515
516 public:
517 const optional<SignalID> fTermSignal;
518
519 private:
522 };
523
524 /**
525 * Support more controlled running of sub-process, where wait timeouts don't necessarily kill the child process.
526 *
527 * \note it is perfectly legal to launch a subprocess, and not track it in any way, just ignoring (not saving)
528 * the BackgroundProcess object.
529 */
531 private:
533
534 public:
535 BackgroundProcess (const BackgroundProcess&) = default;
536
537 public:
538 /**
539 * Return missing if process still running, and if completed, return the results.
540 */
541 nonvirtual optional<ProcessResultType> GetProcessResult () const;
542
543 public:
544 /**
545 * \brief maybe missing if process not yet (or ever successfully) launched. Child process may have
546 * already exited by the time this is returned.
547 */
548 optional<pid_t> GetChildProcessID () const;
549
550 public:
551 /**
552 * \brief wait until GetChildProcessID () returns a valid answer, or until the process failed to start
553 * (in which case calls PropagateIfException).
554 */
555 nonvirtual void WaitForStarted (Time::DurationSeconds timeout = Time::kInfinity) const;
556
557 public:
558 /**
559 *
560 * @see Join ()
561 * @see JoinUntil ()
562 */
563 nonvirtual void WaitForDone (Time::DurationSeconds timeout = Time::kInfinity) const;
564
565 public:
566 /**
567 * \brief Join () does WaitForDone () and throw exception if there was any error (see PropagateIfException).
568 *
569 * \note Aliases - this used to be called WaitForDoneAndPropagateErrors; but used the name Join () to mimic the name used with Threads - NOT
570 * because that's used in the implementation, but because its essentially logically the same thing.
571 *
572 * @see JoinUntil ()
573 * @see WaitForDone ()
574 */
575 nonvirtual void Join (Time::DurationSeconds timeout = Time::kInfinity) const;
576
577 public:
578 /**
579 * \brief WaitForDoneUntil () and throw exception if there was any error (see PropagateIfException).
580 *
581 * @see Join ()
582 * @see WaitForDone ()
583 */
584 nonvirtual void JoinUntil (Time::TimePointSeconds timeoutAt) const;
585
586 public:
587 /**
588 * If the process has completed with an error, throw exception reflecting that failure.
589 *
590 * \note if the process has not completed, this likely does nothing.
591 */
592 nonvirtual void PropagateIfException () const;
593
594 public:
595 /**
596 * If the process is still running, terminate it.
597 */
598 nonvirtual void Terminate ();
599
600 public:
601 /**
602 * Purely for debugging / diagnostic purposes. Don't count on this format.
603 */
604 nonvirtual String ToString () const;
605
606 private:
607 struct Rep_ {
608 virtual ~Rep_ () = default;
609 Thread::CleanupPtr fProcessRunner{Thread::CleanupPtr::eAbortBeforeWaiting};
610 shared_ptr<DetailedRunnableRep_> fDetailedRunnableRep_;
611 };
612 shared_ptr<Rep_> fRep_;
614
615 private:
616 friend class ProcessRunner;
617 };
618
619}
620
621/*
622 ********************************************************************************
623 ***************************** Implementation Details ***************************
624 ********************************************************************************
625 */
626#include "ProcessRunner.inl"
627
628#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
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:443
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
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 an external command, with stdin/stdout/stderr as strings or as streams - like perl backticks.
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 command synchronously, returning its output, and THROW on any failure.
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.