substrate_core/process_port.rs
1//! ProcessPort — cross-platform managed subprocess spawn/monitor/kill.
2//!
3//! Core defines the port contract and value types; `runtime-process` wraps a
4//! vetted process-group crate for platform-specific group semantics.
5
6use std::path::PathBuf;
7use std::time::Duration;
8
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13use crate::error::Result;
14
15/// Specification for spawning a managed child process.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct ProcessSpawnSpec {
18 /// Executable name or path.
19 pub program: String,
20 /// Arguments passed to the program.
21 pub args: Vec<String>,
22 /// Optional working directory for the child.
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub cwd: Option<PathBuf>,
25}
26
27/// Opaque handle to a managed child returned by [`ProcessPort::spawn`].
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29pub struct ProcessHandle {
30 /// Adapter-local process id.
31 pub id: Uuid,
32 /// OS process (or process-group leader) pid at spawn time.
33 pub pid: u32,
34}
35
36/// Observed lifecycle state of a managed child.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub enum ProcessState {
39 /// Child is still running.
40 Running {
41 /// Observed pid.
42 pid: u32,
43 },
44 /// Child has exited (naturally or after a kill).
45 Exited {
46 /// Observed pid.
47 pid: u32,
48 /// Exit code when available (`None` after kill-on-timeout).
49 code: Option<i32>,
50 },
51}
52
53/// General managed-subprocess port: spawn in a process group, poll status,
54/// wait with timeout (killing the group on expiry), and explicit group kill.
55#[async_trait]
56pub trait ProcessPort: Send + Sync {
57 /// Spawn `spec` in its own process group and return a handle + pid.
58 async fn spawn(&self, spec: &ProcessSpawnSpec) -> Result<ProcessHandle>;
59
60 /// Poll the current state without blocking for exit.
61 async fn status(&self, handle: &ProcessHandle) -> Result<ProcessState>;
62
63 /// Wait up to `timeout` for exit. On timeout the whole process group is
64 /// killed and [`ProcessState::Exited`] with `code: None` is returned.
65 async fn wait_with_timeout(
66 &self,
67 handle: &ProcessHandle,
68 timeout: Duration,
69 ) -> Result<ProcessState>;
70
71 /// Kill the managed process group immediately.
72 async fn kill_group(&self, handle: &ProcessHandle) -> Result<()>;
73}