Logo ProcessKit API Reference

RunningProcess Type

A live handle to a started process: stream its output, feed its stdin, wait for it, or collect it to completion. Disposing it reaps the whole process tree (kill-on-drop).

Instance members

Instance member Description

this.CpuTime

Full Usage: this.CpuTime

Returns: TimeSpan option

Cumulative CPU time (user + kernel) of the child right now, if the platform reports it and the process is still alive.

Returns: TimeSpan option

this.DroppedStreamLineCount

Full Usage: this.DroppedStreamLineCount

Returns: int

Lines dropped so far by a bounded streaming policy's `StreamFullMode.DropOldest`/`DropNewest` (always `0` unless `Command.StreamBuffer` is configured with one of those modes) — the streaming analogue of a buffered verb's `ProcessResult.Truncated`.

Returns: int

this.Elapsed

Full Usage: this.Elapsed

Returns: TimeSpan

Wall-clock time since the process started.

Returns: TimeSpan

this.FinishAsync

Full Usage: this.FinishAsync

Returns: Task<Result<Finished, ProcessError>>

After streaming stdout, wait for exit and return the captured stderr. Reaps the tree.

Returns: Task<Result<Finished, ProcessError>>

this.Kill

Full Usage: this.Kill

Signal the process tree to die without waiting (fire-and-forget, like `Process.Kill()`); the tree is fully reaped when the handle is disposed. For a blocking kill, dispose the handle.

this.OutputBytesAsync

Full Usage: this.OutputBytesAsync

Returns: Task<Result<ProcessResult<byte[]>, ProcessError>>

Run to completion, capturing stdout as raw bytes (no line splitting) and stderr as text. The configured `OutputBuffer` policy's **byte** controls apply to this raw stdout capture: `MaxBytes = Some cap` enforces the cap per `Overflow` — `Error` returns `ProcessError.OutputTooLarge` once the cumulative stdout exceeds the cap (the pipe is still drained), `DropOldest` keeps the last `cap` bytes, `DropNewest` keeps the first `cap` bytes, both setting `ProcessResult.Truncated` when anything was dropped. `MaxBytes = None` (the default) keeps the raw capture **unbounded** — there is no byte ceiling to enforce. `MaxLines` never applies to a raw byte stream (it has no line structure) and is ignored on stdout here; it still governs the line-pumped **stderr** capture. `Truncated` reflects truncation of stdout OR stderr, and `OutputTooLarge` fires if either stream trips its fail-loud ceiling. This is a deliberate, documented divergence from the Rust `ProcessKit-rs` reference, whose `output_bytes` bounds raw bytes only by `Timeout`, not by the buffer policy: a caller who set `MaxBytes`/`FailLoud` to bound memory would still get an unbounded stdout buffer otherwise.

Returns: Task<Result<ProcessResult<byte[]>, ProcessError>>

this.OutputEventsAsync

Full Usage: this.OutputEventsAsync

Returns: IAsyncEnumerable<OutputEvent>

Stream merged stdout+stderr line events as they arrive, each tagged with its origin (`OutputEvent.Stdout`/`OutputEvent.Stderr`). Under `Command.MergeStderr` the child has no separate stderr stream (it is folded into stdout at the OS level), so every event is an `OutputEvent.Stdout` — the stderr lines are already interleaved, in order, within the stdout byte stream.

Returns: IAsyncEnumerable<OutputEvent>

this.OutputStringAsync

Full Usage: this.OutputStringAsync

Returns: Task<Result<ProcessResult<string>, ProcessError>>

Run to completion, capturing stdout as decoded text. A non-zero exit is data; the tree is reaped when the call returns.

Returns: Task<Result<ProcessResult<string>, ProcessError>>

this.PeakMemoryBytes

Full Usage: this.PeakMemoryBytes

Returns: int64 option

Peak resident memory of the child in bytes, if reported (some platforms, e.g. macOS, may not) and the process is still alive.

Returns: int64 option

this.Pid

Full Usage: this.Pid

Returns: int option

The pid, when known.

Returns: int option

this.ProfileAsync

Full Usage: this.ProfileAsync

Returns: Task<RunProfile>

`ProfileAsync` sampling every 100 ms.

Returns: Task<RunProfile>

this.ProfileAsync

Full Usage: this.ProfileAsync

Parameters:
Returns: Task<RunProfile>

Run to completion while periodically sampling the child's CPU/memory every `interval`, and return a `RunProfile`. Drains and discards output (like `WaitAsync`) and reaps the tree.

interval : TimeSpan
Returns: Task<RunProfile>

this.StartTime

Full Usage: this.StartTime

Returns: DateTime

When the process was started.

Returns: DateTime

this.StderrLineCount

Full Usage: this.StderrLineCount

Returns: int

Total stderr lines pumped so far.

Returns: int

this.StdoutLineCount

Full Usage: this.StdoutLineCount

Returns: int

Total stdout lines pumped so far (counts dropped lines too).

Returns: int

this.StdoutLinesAsync

Full Usage: this.StdoutLinesAsync

Returns: IAsyncEnumerable<string>

Stream stdout line by line as it arrives. Call `FinishAsync` afterwards for stderr + outcome.

Returns: IAsyncEnumerable<string>

this.StopAsync

Full Usage: this.StopAsync

Returns: Task<Outcome>

`StopAsync` using the default 2-second grace window (matching `ProcessGroupOptions.ShutdownTimeout`).

Returns: Task<Outcome>

this.StopAsync

Full Usage: this.StopAsync

Parameters:
Returns: Task<Outcome>

Gracefully stop the process tree, then reap it: send the child a soft signal (SIGTERM), wait up to `gracePeriod` for it to exit on its own, then hard-kill whatever is still alive — the same graceful-kill machinery `Command.TimeoutGrace` and `ProcessGroup.ShutdownAsync` drive. Returns the honest `Outcome` of how the child *actually* concluded (a clean `Exited` if it obeyed the signal, otherwise a `Signalled`/`Exited` from the escalated kill); a non-zero or killed exit is data, never a raised error. Unlike the fire-and-forget `Kill()`, this awaits the stop and tears the tree down before returning, so it is a terminal verb like `WaitAsync`. This drains the child's stdout/stderr while it shuts down (a child blocked writing to a full pipe would otherwise ignore the soft signal until it could flush). If a streaming or capturing verb already owns the pipes, `StopAsync` reuses that session's wait rather than starting a second reader on them, so it is safe to call after `StdoutLinesAsync`/`OutputEventsAsync` or concurrently with an in-flight `FinishAsync`/`WaitAsync`. Idempotent and race-safe with `Kill`, `Dispose`, and a repeat `StopAsync`: the tree is reaped exactly once. **Platform / shared-group degradation (no new silent downgrade).** A soft signal needs a mechanism that has one. On **Windows** there is no per-tree graceful signal, so `gracePeriod` is skipped and this is the atomic Job kill — exactly as `Command.TimeoutGrace` and `ProcessGroup.ShutdownAsync` already degrade there (a console child can still get a best-effort CTRL+BREAK via `Command.WindowsCtrlSignals()` + `ProcessGroup.Signal`). On a **shared** group (a handle from `ProcessGroup.StartAsync`, where the group — not the handle — owns the tree) there is no per-child graceful signal either, so this immediately hard-kills just this child (like `Kill()`), matching the documented `TimeoutGrace` fallback for a shared group. A handle from the default runner (`Command.StartAsync()` / `IProcessRunner.SpawnAsync`) owns a private group and gets the full SIGTERM → grace → SIGKILL on Unix.

gracePeriod : TimeSpan
Returns: Task<Outcome>

this.TakeStdin

Full Usage: this.TakeStdin

Returns: ProcessStdin option

Take the interactive stdin handle — `Some` only when the command kept stdin open without a source attached, and only once.

Returns: ProcessStdin option

this.WaitAsync

Full Usage: this.WaitAsync

Returns: Task<Outcome>

Wait for the process to exit, discarding its output. Reaps the tree.

Returns: Task<Outcome>

this.WaitForAsync

Full Usage: this.WaitForAsync

Parameters:
Returns: Task<Result<unit, ProcessError>>

Poll `probe` until it returns true, or fail with `NotReady` once the shared `timeout` deadline elapses (or `Cancelled` if `cancellationToken` fires first). The deadline is honored even if `probe` never completes — or blocks synchronously without ever returning a task: the invocation is isolated on the thread pool and raced against the shared deadline, and the caller's token takes priority over a concurrent success. The API cannot force a caller-owned `probe` to stop, so an abandoned invocation keeps running in the background, but its late outcome is safely observed (a late fault never becomes an unobserved task exception). See `ReadinessProbe.waitForCore` for the full contract, including the ratified scheduler-bounded window at the deadline. Background-drains (and discards) the child's piped stdout/stderr for the duration of the poll, exactly like `WaitForPortAsync` — see its doc for what that does and doesn't compose with afterward.

probe : Func<Task<bool>>
timeout : TimeSpan
?cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

this.WaitForLineAsync

Full Usage: this.WaitForLineAsync

Parameters:
Returns: Task<Result<string, ProcessError>>

Wait until a stdout line satisfies `predicate`, or fail with `NotReady` after `timeout` (or `Cancelled` if `cancellationToken` fires first). Consumed lines are not re-delivered; a later `StdoutLinesAsync`/`FinishAsync` sees the rest.

predicate : Func<string, bool>
timeout : TimeSpan
?cancellationToken : CancellationToken
Returns: Task<Result<string, ProcessError>>

this.WaitForPortAsync

Full Usage: this.WaitForPortAsync

Parameters:
Returns: Task<Result<unit, ProcessError>>

Wait until a TCP connection to `endpoint` succeeds, or fail with `NotReady` once the shared `timeout` deadline elapses (or `Cancelled` if `cancellationToken` fires first). Every connect attempt and polling backoff shares that one deadline, so a slow or non-cooperative connect can never overrun a short `timeout` — see `ReadinessProbe.waitForPortUsing` for the full contract, including the ratified scheduler-bounded window at the deadline. Background-drains (and discards) the child's piped stdout/stderr for the duration of the poll — like `WaitForLineAsync`, so a child that writes more than one OS pipe buffer of startup output (~64 KiB on Linux) before becoming ready can't block in `write()` and spuriously time out this probe — but unlike `WaitForLineAsync`, the drained bytes are discarded rather than handed back, and draining stops once the probe concludes rather than continuing as an established streaming session. A capture verb (`OutputStringAsync`/`OutputBytesAsync`/`StdoutLinesAsync`/`OutputEventsAsync`) called AFTER this probe therefore only sees what the child wrote after the probe concluded, not the full run — the same "doesn't compose with a subsequent fresh capture" limitation `WaitForLineAsync` already documents, now uniform across all three readiness probes. If a buffered/streaming verb already claimed the pipes before this call, that verb's own pump is already draining them and this probe leaves them alone (no second reader).

endpoint : IPEndPoint
timeout : TimeSpan
?cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

Static members

Static member Description

RunningProcess.WaitAllAsync(processes)

Full Usage: RunningProcess.WaitAllAsync(processes)

Parameters:
Returns: Task<Outcome[]>

Wait for all of `processes` to exit; returns their outcomes in order. Does not reap them. `processes` must be non-null, non-empty, and free of null elements — each is a programmer error, not a process outcome, so it throws (`ArgumentNullException` for a null array, `ArgumentException` for an empty array or a null element) rather than reporting through a `Result`. Symmetric with `WaitAnyAsync` on all three axes: error channel, empty input, and null handling. If a pump backing one of the `ExitTask`s faults, that exception propagates unchanged from `Task.WhenAll` — also not wrapped in a `Result`.

processes : RunningProcess[]
Returns: Task<Outcome[]>

RunningProcess.WaitAnyAsync(processes)

Full Usage: RunningProcess.WaitAnyAsync(processes)

Parameters:
Returns: Task<WaitAnyResult>

Wait for the first of `processes` to exit; returns its index and outcome. Does not reap any of them — dispose them yourself. Safe to call on a handle a buffered verb (`OutputStringAsync`/ `OutputBytesAsync`/`WaitAsync`/`ProfileAsync`) already started: it reuses that verb's own wait (see `ExitTask`) rather than racing a second reader on the same pipes. `processes` must be non-null, non-empty, and free of null elements — each is a programmer error, not a process outcome, so it throws (`ArgumentNullException` for a null array, `ArgumentException` for an empty array or a null element) rather than reporting through a `Result`. Symmetric with `WaitAllAsync` on all three axes: error channel, empty input, and null handling. If a pump backing one of the raced `ExitTask`s faults, that exception propagates unchanged from the awaited task — also not wrapped in a `Result`.

processes : RunningProcess[]
Returns: Task<WaitAnyResult>

Type something to start searching.