Skip to main content

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}