pub struct ProcessHandle<Stdout, Stderr = Stdout>where
Stdout: OutputStream,
Stderr: OutputStream,{ /* private fields */ }Expand description
A handle to a spawned process with captured stdout/stderr streams.
This type provides methods for waiting on process completion, terminating the process,
and accessing its output streams. By default, processes must be explicitly waited on
or terminated before being dropped (see ProcessHandle::must_be_terminated).
If applicable, a process handle can be wrapped in a crate::TerminateOnDrop to be terminated
automatically upon being dropped. Note that this requires a multi-threaded runtime!
Implementations§
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub fn must_be_terminated(&mut self)
pub fn must_be_terminated(&mut self)
Sets a panic-on-drop mechanism for this ProcessHandle.
This method enables a safeguard that ensures that the process represented by this
ProcessHandle is properly terminated or awaited before being dropped.
If must_be_terminated is set and the ProcessHandle is
dropped without successfully terminating, killing, waiting for, or explicitly detaching the
process, an intentional panic will occur to prevent silent failure-states, ensuring that
system resources are handled correctly.
You typically do not need to call this, as every ProcessHandle is marked by default.
Call must_not_be_terminated to clear this safeguard to explicitly allow dropping the
process without terminating it.
Calling this method while the safeguard is already enabled is safe and has no effect beyond
keeping the handle armed.
§Panic
If the ProcessHandle is dropped without being awaited or terminated successfully
after calling this method, a panic will occur with a descriptive message
to inform about the incorrect usage.
Sourcepub fn must_not_be_terminated(&mut self)
pub fn must_not_be_terminated(&mut self)
Disables the kill/panic-on-drop safeguards for this handle.
Dropping the handle after calling this method will no longer signal, kill, or panic. However, this does not keep the library-owned stdio pipes alive. If the child still depends on stdin, stdout, or stderr being open, dropping the handle may still affect it.
Use plain tokio::process::Command directly when you need a child process that can
outlive the original handle without depending on captured stdio pipes.
Also, the right opt-out after terminate returns an unrecoverable error
and the caller chooses to accept the failure instead of retrying or escalating to
kill.
Sourcepub fn terminate_on_drop(
self,
timeouts: GracefulTimeouts,
) -> TerminateOnDrop<Stdout, Stderr>
pub fn terminate_on_drop( self, timeouts: GracefulTimeouts, ) -> TerminateOnDrop<Stdout, Stderr>
Wrap this process handle in a TerminateOnDrop instance, terminating the controlled process
automatically when this handle is dropped.
timeouts carries the same per-platform graceful budget as Self::terminate; see
GracefulTimeouts for how to construct it.
SAFETY: This only works when your code is running in a multithreaded tokio runtime!
Prefer manual termination of the process or awaiting it and relying on the (automatically
configured) must_be_terminated logic, raising a panic when a process was neither awaited
nor terminated before being dropped.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: TrySubscribable,
Stderr: TrySubscribable,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: TrySubscribable,
Stderr: TrySubscribable,
Sourcepub async fn wait_for_completion_with_output(
&mut self,
timeout: Duration,
output_options: LineOutputOptions,
) -> Result<WaitForCompletionResult<ProcessOutput<CollectedLines>>, WaitWithOutputError>
pub async fn wait_for_completion_with_output( &mut self, timeout: Duration, output_options: LineOutputOptions, ) -> Result<WaitForCompletionResult<ProcessOutput<CollectedLines>>, WaitWithOutputError>
Waits for the process to complete while collecting line output.
Collectors are attached when this method is called. If the stream was configured with
.no_replay(), output produced before attachment may be discarded; configure replay before
spawning when startup output must be included.
Any still-open stdin handle is closed before the terminal wait begins, matching
tokio::process::Child::wait.
timeout bounds both process completion and stdout/stderr collection.
§Errors
Returns WaitWithOutputError if waiting for the process or collecting output
fails after the process completes. Process timeout is returned as
WaitForCompletionResult::Timeout.
Sourcepub async fn wait_for_completion_with_raw_output(
&mut self,
timeout: Duration,
output_options: RawOutputOptions,
) -> Result<WaitForCompletionResult<ProcessOutput<CollectedBytes>>, WaitWithOutputError>
pub async fn wait_for_completion_with_raw_output( &mut self, timeout: Duration, output_options: RawOutputOptions, ) -> Result<WaitForCompletionResult<ProcessOutput<CollectedBytes>>, WaitWithOutputError>
Waits for the process to complete while collecting raw byte output.
Any still-open stdin handle is closed before the terminal wait begins, matching
tokio::process::Child::wait.
timeout bounds both process completion and stdout/stderr collection.
§Errors
Returns WaitWithOutputError if waiting for the process or collecting output
fails after the process completes. Process timeout is returned as
WaitForCompletionResult::Timeout.
Sourcepub async fn wait_for_completion_with_output_or_terminate(
&mut self,
options: WaitForCompletionOrTerminateOptions,
output_options: LineOutputOptions,
) -> Result<WaitForCompletionOrTerminateResult<ProcessOutput<CollectedLines>>, WaitWithOutputError>
pub async fn wait_for_completion_with_output_or_terminate( &mut self, options: WaitForCompletionOrTerminateOptions, output_options: LineOutputOptions, ) -> Result<WaitForCompletionOrTerminateResult<ProcessOutput<CollectedLines>>, WaitWithOutputError>
Waits for completion within wait_timeout, terminating the process if needed, while
collecting line output.
Any still-open stdin handle is closed before the initial terminal wait begins, matching
tokio::process::Child::wait.
Output collection is bounded by wait_timeout plus the per-platform graceful budget
(interrupt_timeout + terminate_timeout on Unix, graceful_timeout on Windows), plus a
fixed 3-second post-kill confirmation wait when force-kill fallback is required.
§Errors
Returns WaitWithOutputError if waiting, termination, or output
collection fails. Timeout-triggered cleanup success is returned as
WaitForCompletionOrTerminateResult::TerminatedAfterTimeout.
Sourcepub async fn wait_for_completion_with_raw_output_or_terminate(
&mut self,
options: WaitForCompletionOrTerminateOptions,
output_options: RawOutputOptions,
) -> Result<WaitForCompletionOrTerminateResult<ProcessOutput<CollectedBytes>>, WaitWithOutputError>
pub async fn wait_for_completion_with_raw_output_or_terminate( &mut self, options: WaitForCompletionOrTerminateOptions, output_options: RawOutputOptions, ) -> Result<WaitForCompletionOrTerminateResult<ProcessOutput<CollectedBytes>>, WaitWithOutputError>
Waits for completion within wait_timeout, terminating the process if needed, while
collecting raw byte output.
Any still-open stdin handle is closed before the initial terminal wait begins, matching
tokio::process::Child::wait.
Output collection is bounded by wait_timeout plus the per-platform graceful budget
(interrupt_timeout + terminate_timeout on Unix, graceful_timeout on Windows), plus a
fixed 3-second post-kill confirmation wait when force-kill fallback is required.
§Errors
Returns WaitWithOutputError if waiting, termination, or output
collection fails. Timeout-triggered cleanup success is returned as
WaitForCompletionOrTerminateResult::TerminatedAfterTimeout.
Source§impl<StdoutD, Stderr> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, Stderr>where
StdoutD: Delivery,
Stderr: OutputStream,
impl<StdoutD, Stderr> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, Stderr>where
StdoutD: Delivery,
Stderr: OutputStream,
Sourcepub fn seal_stdout_replay(&self)
pub fn seal_stdout_replay(&self)
Seals stdout replay history for future subscribers.
Source§impl<StdoutD, Stderr> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, Stderr>where
StdoutD: Delivery,
Stderr: OutputStream,
impl<StdoutD, Stderr> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, Stderr>where
StdoutD: Delivery,
Stderr: OutputStream,
Sourcepub fn seal_stdout_replay(&self)
pub fn seal_stdout_replay(&self)
Seals stdout replay history for future subscribers.
Source§impl<Stdout, StderrD> ProcessHandle<Stdout, BroadcastOutputStream<StderrD, ReplayEnabled>>where
Stdout: OutputStream,
StderrD: Delivery,
impl<Stdout, StderrD> ProcessHandle<Stdout, BroadcastOutputStream<StderrD, ReplayEnabled>>where
Stdout: OutputStream,
StderrD: Delivery,
Sourcepub fn seal_stderr_replay(&self)
pub fn seal_stderr_replay(&self)
Seals stderr replay history for future subscribers.
Source§impl<Stdout, StderrD> ProcessHandle<Stdout, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>where
Stdout: OutputStream,
StderrD: Delivery,
impl<Stdout, StderrD> ProcessHandle<Stdout, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>where
Stdout: OutputStream,
StderrD: Delivery,
Sourcepub fn seal_stderr_replay(&self)
pub fn seal_stderr_replay(&self)
Seals stderr replay history for future subscribers.
Source§impl<StdoutD, StderrD> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, BroadcastOutputStream<StderrD, ReplayEnabled>>
impl<StdoutD, StderrD> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, BroadcastOutputStream<StderrD, ReplayEnabled>>
Sourcepub fn seal_output_replay(&self)
pub fn seal_output_replay(&self)
Seals stdout and stderr replay history for replay-enabled streams.
Source§impl<StdoutD, StderrD> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>
impl<StdoutD, StderrD> ProcessHandle<BroadcastOutputStream<StdoutD, ReplayEnabled>, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>
Sourcepub fn seal_output_replay(&self)
pub fn seal_output_replay(&self)
Seals stdout and stderr replay history for replay-enabled streams.
Source§impl<StdoutD, StderrD> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, BroadcastOutputStream<StderrD, ReplayEnabled>>
impl<StdoutD, StderrD> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, BroadcastOutputStream<StderrD, ReplayEnabled>>
Sourcepub fn seal_output_replay(&self)
pub fn seal_output_replay(&self)
Seals stdout and stderr replay history for replay-enabled streams.
Source§impl<StdoutD, StderrD> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>
impl<StdoutD, StderrD> ProcessHandle<SingleSubscriberOutputStream<StdoutD, ReplayEnabled>, SingleSubscriberOutputStream<StderrD, ReplayEnabled>>
Sourcepub fn seal_output_replay(&self)
pub fn seal_output_replay(&self)
Seals stdout and stderr replay history for replay-enabled streams.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub async fn kill(&mut self) -> Result<(), TerminationError>
pub async fn kill(&mut self) -> Result<(), TerminationError>
Forces the process to exit. Most users should call ProcessHandle::terminate instead.
This is equivalent to sending SIGKILL on Unix or calling TerminateProcess on Windows,
followed by wait. On other Tokio-supported platforms it forwards to
tokio::process::Child::start_kill.
Any still-open stdin handle is closed before Tokio performs that kill-and-wait sequence,
matching tokio::process::Child::kill semantics.
A successful call waits for the child to exit and disarms the drop cleanup and panic guards,
so the handle can be dropped safely afterward.
kill is a reasonable next step when terminate returns Err and the
caller is not interested in further graceful escalation.
§Errors
Returns TerminationError if Tokio cannot kill or wait for the child process.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub fn send_interrupt_signal(&mut self) -> Result<(), TerminationError>
pub fn send_interrupt_signal(&mut self) -> Result<(), TerminationError>
Manually send SIGINT to this process’s process group via killpg.
SIGINT is the dedicated user-interrupt signal, distinct from the SIGTERM delivered by
Self::send_terminate_signal. The signal targets the child’s process group, so any
grandchildren the child has fork-execed are signaled together with the leader.
If the process has already exited, this reaps it and returns Ok(()) instead of
attempting to signal a stale PID or process group. If the signal send fails because the
child exited after the preflight check, this also reaps it and returns Ok(()).
Prefer to call terminate instead, if you want to make sure this process is terminated.
This method is Unix-only because Windows has no targetable SIGINT analogue:
GenerateConsoleCtrlEvent only accepts CTRL_BREAK_EVENT for nonzero process groups.
On Windows, use send_ctrl_break_signal instead.
§Errors
Returns TerminationError if the process status could not be checked or if SIGINT
could not be sent.
Sourcepub fn send_terminate_signal(&mut self) -> Result<(), TerminationError>
pub fn send_terminate_signal(&mut self) -> Result<(), TerminationError>
Manually send SIGTERM to this process’s process group via killpg.
SIGTERM is the conventional “asked to terminate” signal sent by service supervisors and
the operating system at shutdown. The signal targets the child’s process group, so any
grandchildren the child has fork-execed are signaled together with the leader.
If the process has already exited, this reaps it and returns Ok(()) instead of
attempting to signal a stale PID or process group. If the signal send fails because the
child exited after the preflight check, this also reaps it and returns Ok(()).
Prefer to call terminate instead, if you want to make sure this process is terminated.
This method is Unix-only because Windows has no targetable SIGTERM analogue:
GenerateConsoleCtrlEvent only accepts CTRL_BREAK_EVENT for nonzero process groups.
On Windows, use send_ctrl_break_signal instead.
§Errors
Returns TerminationError if the process status could not be checked or if SIGTERM
could not be sent.
Sourcepub async fn terminate(
&mut self,
timeouts: GracefulTimeouts,
) -> Result<ExitStatus, TerminationError>
pub async fn terminate( &mut self, timeouts: GracefulTimeouts, ) -> Result<ExitStatus, TerminationError>
Terminates this process by sending platform graceful shutdown signals first, then killing the process if it does not complete after receiving them.
The signature is the same on every supported platform; the shape of timeouts is
platform-conditional. See GracefulTimeouts for how to construct one.
- On Unix this is a 3-phase escalation:
SIGINT-> waittimeouts.interrupt_timeout->SIGTERM-> waittimeouts.terminate_timeout->SIGKILL. The two distinct graceful signals matter in practice: idiomatic async Rust binaries usetokio::signal::ctrl_c()(which on Unix listens only forSIGINT), and Python child processes turnSIGINTinto aKeyboardInterruptexception that runstry/finallycleanup, whileSIGTERMfalls through to the runtime’s default handler. - On Windows this is a 2-phase termination:
CTRL_BREAK_EVENT-> waittimeouts.graceful_timeout->TerminateProcess. Only oneCTRL_BREAK_EVENTis ever sent.GenerateConsoleCtrlEventcan only target a child’s process group withCTRL_BREAK_EVENT(sendingCTRL_C_EVENTwould requiredwProcessGroupId = 0and broadcast to the parent), so a second graceful send would be the same event and cannot do more than the first send already did.
The forceful kill fallback adds one fixed 3-second wait on top of the graceful timeouts.
§Windows interop note
tokio::signal::ctrl_c() on Windows registers only for CTRL_C_EVENT; it does not catch
CTRL_BREAK_EVENT. A child Rust binary that listens only on the cross-platform
tokio::signal::ctrl_c() will not respond to this graceful step on Windows and will be
terminated forcefully after graceful_timeout. To interoperate, such a child should
additionally listen on tokio::signal::windows::ctrl_break(), or expose another
shutdown channel (stdin sentinel, IPC, or a command protocol).
§Timeouts
Each per-phase timeout in timeouts bounds the post-signal wait of its phase:
- Signal send succeeds: wait up to the user-supplied timeout, then escalate.
- Signal send fails: replace the user timeout with a fixed 100 ms grace so Tokio’s
reaper can catch up to a child that just exited (the OS rejects signals to a not-yet-
reaped process group with
EPERMon macOS orESRCHon Linux). Real permission denials still surface as an error after the grace elapses.
Duration::from_secs(0) disables the post-signal wait entirely and effectively forces
the call into the forceful kill (SIGKILL on Unix, TerminateProcess on Windows).
Prefer small but non-zero values (e.g. 100 ms to a few seconds).
§Drop guards on Ok vs Err
On Ok, the drop cleanup and panic guards are disarmed and the handle can be dropped
safely. On Err (or if the future is canceled), the guards stay armed: the library cannot
verify cleanup from the outside, so dropping would leak a process. Recover by retrying
terminate, escalating to kill, calling
must_not_be_terminated to accept the failure, or
propagating the error and letting the panic-on-drop surface the leak.
§Errors
Returns TerminationError if signalling or waiting for process termination fails.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub async fn wait_for_completion(
&mut self,
timeout: Duration,
) -> Result<WaitForCompletionResult, WaitError>
pub async fn wait_for_completion( &mut self, timeout: Duration, ) -> Result<WaitForCompletionResult, WaitError>
Wait for this process to run to completion within timeout.
Any still-open stdin handle is closed before waiting begins, matching
tokio::process::Child::wait and helping avoid deadlocks where the child is waiting for
input while the parent is waiting for exit.
If the timeout is reached before the process terminated, a timeout outcome is returned and the
process keeps running without being signalled or killed. Its stdin has still been closed
before the wait started.
Use ProcessHandle::wait_for_completion_or_terminate if you want immediate termination.
This does not provide the processes output. Use ProcessHandle::stdout and
ProcessHandle::stderr to inspect, watch over, or capture the process output.
§Errors
Returns WaitError if waiting for the process fails.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub async fn wait_for_completion_or_terminate(
&mut self,
options: WaitForCompletionOrTerminateOptions,
) -> Result<WaitForCompletionOrTerminateResult, WaitOrTerminateError>
pub async fn wait_for_completion_or_terminate( &mut self, options: WaitForCompletionOrTerminateOptions, ) -> Result<WaitForCompletionOrTerminateResult, WaitOrTerminateError>
Wait for this process to run to completion within wait_timeout.
Any still-open stdin handle is closed before the initial wait begins, matching
tokio::process::Child::wait.
If the timeout is reached before the process terminated normally, external termination is
forced through ProcessHandle::terminate and a
WaitForCompletionOrTerminateResult::TerminatedAfterTimeout outcome is returned when
cleanup succeeds. If waiting fails for a non-timeout reason, cleanup termination is still
attempted and the original wait failure is preserved in the returned error.
Total wall-clock time can exceed wait_timeout plus the per-platform graceful budget
carried by options.graceful_timeouts (interrupt_timeout + terminate_timeout on Unix,
graceful_timeout on Windows) by one additional fixed 3-second wait when the force-kill
fallback is required.
§Errors
Returns WaitOrTerminateError if waiting fails or if timeout-triggered termination is
required and then fails.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub fn stdin(&mut self) -> &mut Stdin
pub fn stdin(&mut self) -> &mut Stdin
Returns a mutable reference to the (potentially already closed) stdin stream.
Use this to write data to the child process’s stdin. The stdin stream implements
tokio::io::AsyncWrite, allowing you to use methods like write_all() and flush().
§Example
AutoName, DEFAULT_MAX_BUFFERED_CHUNKS, DEFAULT_READ_CHUNK_SIZE, Process,
};
// The stream backend does not make a difference here.
let mut process = Process::new(Command::new("cat"))
.name(AutoName::program_only())
.stdout_and_stderr(|stream| {
stream
.broadcast()
.best_effort_delivery()
.no_replay()
.read_chunk_size(DEFAULT_READ_CHUNK_SIZE)
.max_buffered_chunks(DEFAULT_MAX_BUFFERED_CHUNKS)
})
.spawn()
.unwrap();
// Write to stdin.
if let Some(stdin) = process.stdin().as_mut() {
stdin.write_all(b"Hello, process!\n").await.unwrap();
stdin.flush().await.unwrap();
}
// Close stdin to signal EOF.
process.stdin().close();Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub fn id(&self) -> Option<u32>
pub fn id(&self) -> Option<u32>
Returns the OS process ID if the process hasn’t exited yet.
Once this process has been polled to completion this will return None.
Sourcepub fn is_running(&mut self) -> RunningState
pub fn is_running(&mut self) -> RunningState
Checks if the process is currently running.
Returns RunningState::Running if the process is still running,
RunningState::Terminated if it has exited, or RunningState::Uncertain
if the state could not be determined. Each call re-runs the underlying
try_wait, so a transient probing failure observed once does not become permanent.
This is a pure status query: it does not disarm the drop-cleanup or panic guards, even
when it observes that the process has exited. A handle whose status reads
RunningState::Terminated still panics on drop until one of the lifecycle methods has
closed it. Use Self::wait_for_completion, Self::terminate, or Self::kill to
close the lifecycle through a successful terminal call, or call
Self::must_not_be_terminated explicitly to detach the handle without termination.
Source§impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
impl<Stdout, Stderr> ProcessHandle<Stdout, Stderr>where
Stdout: OutputStream,
Stderr: OutputStream,
Sourcepub fn into_inner(self) -> (Child, Stdin, Stdout, Stderr)
pub fn into_inner(self) -> (Child, Stdin, Stdout, Stderr)
Consumes this handle to provide the wrapped tokio::process::Child instance, stdin handle,
and stdout/stderr output streams.
The returned Child no longer owns its stdin field because this crate separates piped
stdin into Stdin. Keep the returned Stdin alive to keep the child’s stdin pipe open.
Dropping Stdin::Open closes the pipe, so the child may observe EOF and exit or otherwise
change behavior.