Skip to main content

ExtensionHostState

Enum ExtensionHostState 

Source
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> held in SessionManager. No method on this struct should be called without holding that mutex. This means state transitions are inherently atomic — no TOCTOU possible as long as callers hold the mutex across check-then-act sequences.

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§

§

Stopped

§

Starting

§

Running

§

Unhealthy

§

Restarting

§

Stopping

§

Crashed

Trait Implementations§

Source§

impl Clone for ExtensionHostState

Source§

fn clone(&self) -> ExtensionHostState

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ExtensionHostState

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ExtensionHostState

Source§

fn eq(&self, other: &ExtensionHostState) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Copy for ExtensionHostState

Source§

impl Eq for ExtensionHostState

Source§

impl StructuralPartialEq for ExtensionHostState

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more