pub enum ExtensionHostState {
Stopped,
Starting,
Running,
Unhealthy,
Restarting,
Stopping,
Crashed,
}Expand description
STATE MACHINE: ExtensionHostProcess
Tracks the lifecycle of the Node.js extension host child process.
State Diagram:
Stopped ──────► Starting ──────► Running ▲ │ │ │ (spawn fail)│ (unexpected) │ (stop() called) │ ▼ (exit) ▼ │ Crashed ◄──── Unhealthy Stopping │ ▲ │ │ │ (max retries)│ │ (within │ │ │ │ budget) │ │ │ ▼ │ │ └──────── Restarting │ │ │ └─────────────────────────────────────────────┘
Transitions: Stopped -> Starting (start() called) Starting -> Running (process spawned, stdout/stderr threads launched) Starting -> Crashed (Command::spawn() failed) Running -> Unhealthy (process exited unexpectedly, detected by try_wait) Running -> Stopping (stop() called for graceful shutdown) Unhealthy -> Restarting (restart_if_needed(), within MAX_RESTART_ATTEMPTS) Unhealthy -> Crashed (restart_if_needed(), attempts exhausted) Restarting -> Starting (backoff elapsed, re-entering start flow) Stopping -> Stopped (process killed and waited successfully) Crashed -> Starting (explicit start() after crash, resets restart_count)
Concurrency Invariant:
All access to ExtensionHostManager is serialized by the
Arc<tokio::sync::Mutex
Interruption Table: ┌─────────────┬──────────────────────────────────────────────────────────┐ │ State │ What happens + recovery path │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Stopped │ Safe resting state. No child process, no resources. │ │ │ Can call start() to launch a new host. │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Starting │ Child process spawning or just spawned. │ │ │ If Tauri crashes: child process orphaned, OS reaps. │ │ │ If child spawn fails: -> Crashed immediately. │ │ │ If child exits within 5s: next state check -> Unhealthy.│ │ │ stdout/stderr reader threads may be running — they exit │ │ │ naturally when child’s pipes close. │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Running │ Normal operation. Process alive and healthy. │ │ │ If child exits unexpectedly: detected on next │ │ │ check_and_update_state() call -> Unhealthy. │ │ │ All pending IPC requests are orphaned (oneshot senders │ │ │ dropped, receivers get RecvErr or timeout). │ │ │ Frontend NOT notified — stale state until next command. │ │ │ Backend SessionState still shows extensions as active. │ │ │ TODO: Add proactive health monitoring (periodic check). │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Unhealthy │ Process has exited. No child process running. │ │ │ restart_if_needed() should be called to attempt │ │ │ recovery. If within budget (3 attempts): │ │ │ -> Restarting with exponential backoff (1s, 2s, 4s). │ │ │ If budget exhausted: -> Crashed (terminal). │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Restarting │ Waiting for backoff timer before re-spawn. │ │ │ If Tauri crashes during wait: no cleanup needed (no │ │ │ child process exists yet). │ │ │ After backoff: -> Starting -> Running (or -> Crashed). │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Stopping │ Graceful shutdown in progress. Kill signal sent. │ │ │ If process ignores SIGTERM: force kill + wait. │ │ │ -> Stopped after process exits. │ │ │ If Tauri crashes during stop: child may linger │ │ │ (no SIGCHLD handler). OS eventually reaps. │ ├─────────────┼──────────────────────────────────────────────────────────┤ │ Crashed │ Terminal failure state. Max restart attempts exhausted. │ │ │ All extensions in this host are effectively dead. │ │ │ Backend state still shows extensions as active (stale). │ │ │ Frontend shows stale extension state. │ │ │ Recovery: explicit start() call resets restart_count │ │ │ and re-enters Starting. Alternatively, app restart. │ └─────────────┴──────────────────────────────────────────────────────────┘
Variants§
Trait Implementations§
Source§impl Clone for ExtensionHostState
impl Clone for ExtensionHostState
Source§fn clone(&self) -> ExtensionHostState
fn clone(&self) -> ExtensionHostState
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ExtensionHostState
impl Debug for ExtensionHostState
Source§impl PartialEq for ExtensionHostState
impl PartialEq for ExtensionHostState
impl Copy for ExtensionHostState
impl Eq for ExtensionHostState
impl StructuralPartialEq for ExtensionHostState
Auto Trait Implementations§
impl Freeze for ExtensionHostState
impl RefUnwindSafe for ExtensionHostState
impl Send for ExtensionHostState
impl Sync for ExtensionHostState
impl Unpin for ExtensionHostState
impl UnsafeUnpin for ExtensionHostState
impl UnwindSafe for ExtensionHostState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more