Skip to main content

Supervisor

Struct Supervisor 

Source
pub struct Supervisor<const N: usize> { /* private fields */ }
Expand description

Orchestrates a set of managed tasks across spawn / teardown / bring-up.

Owned by a single supervisor task. Concurrent access from other tasks goes through each TaskNode’s own atomic state, not the Supervisor struct.

Implementations§

Source§

impl<const N: usize> Supervisor<N>

Source

pub async fn run_pools(&self, spawner: Spawner)

Drive the registered elastic pools (from GRAPH.pools) forever: run their policies, then park until the next status signal (SCALE_REQ) or a pool’s deferred deadline. Never completes — meant to be selected against the application’s control / teardown futures in the supervisor task. When another arm wins this future is dropped, which is safe: a half-applied stop is re-driven on the next pass.

Source§

impl<const N: usize> Supervisor<N>

Source

pub const fn new(graph: &'static Graph<N>) -> Self

Build a supervisor from a precomputed Graph — the GRAPH that supervisor_graph! emits (node slots, dependency-index table, compile-time topological order, and the elastic pools). A dependency cycle is a compile error, so construction is infallible and does no work — start / teardown / respawn_terminate just iterate.

Source

pub async fn start(&self, spawner: Spawner) -> Result<(), SpawnError>

Spawn every boot node in dependency order. Called once at boot. Mode::OnDemand nodes are skipped — they’re brought up at runtime by start_node. A parked node (no spawn fn) is spawned externally by main() (with hardware handles main owns); it’s still marked running here. Disabled nodes, and #[cfg]-ed-out slots, are skipped.

Async because an executor: NAME node first awaits its SpawnerSlot::ready (bounded by SLOT_READY_TIMEOUT — the rendezvous with a tier or second core that comes up asynchronously); a slot still empty at the deadline fails the bring-up with SpawnError::Busy. A node with no executor: slot never touches the timer.

Source

pub async fn start_node( &self, node: &'static TaskNode, spawner: Spawner, ) -> Result<(), SpawnError>

Start a single node at runtime — e.g. growing an elastic pool. Resets the handle, spawns one instance via the node’s spawn fn (which must launch exactly one), and marks it running. Returns SpawnError::Busy if the underlying embassy task pool is exhausted (the ceiling), which the caller treats as “can’t grow”.

Source

pub async fn stop_node(&self, node: &'static TaskNode)

Stop a single running node at runtime — e.g. shrinking an elastic pool. Signals shutdown, waits for the ack, clears running. No-op if the node isn’t running, or is detached (self-managed — the supervisor never stops it). Panics if it doesn’t ack within the timeout.

Source

pub async fn teardown(&self)

Signal every running node to shut down in reverse topological order, awaiting each node’s ack before moving to its dependency. Down OnDemand nodes are skipped (no instance to ack). Pause-mode nodes ack and park on wait_resume(); Terminate/OnDemand nodes exit. Panics if a running node fails to ack within SHUTDOWN_ACK_TIMEOUT_MS.

Source

pub fn resume_pausable(&self)

Signal every Pause-mode node to resume. Cheap and synchronous — the tasks were parked on wait_resume() and pick up immediately. Called separately from respawn_terminate so the application can fire resume independently of the respawn step. Disabled (manually-paused) nodes are skipped so a manual pause sticks, and detached (self-managed) Pause nodes are left parked; there is intentionally no dependency gate here.

Source

pub async fn respawn_terminate( &self, spawner: Spawner, ) -> Result<(), SpawnError>

Reset and re-spawn every Terminate-mode node in dependency order. Pause-mode nodes are untouched (use resume_pausable); OnDemand nodes are left down — they re-grow under load via start_node. Disabled nodes are skipped so a manual stop sticks across the bring-up. Detached nodes are skipped too: teardown never brought them down, so they are still running and re-spawning would double-spawn them (see TaskNode::set_detached). The reset happens before the spawn so newly-running tasks see a clean handle.

Source§

impl<const N: usize> Supervisor<N>

Source

pub async fn apply_control(&self, cmd: ControlCommand, spawner: Spawner)

Apply one control command, honoring pool membership and the dependency graph. Run from the supervisor’s driver loop (never concurrently with itself), so the cascade is atomic from the application’s perspective.

Auto Trait Implementations§

§

impl<const N: usize> !RefUnwindSafe for Supervisor<N>

§

impl<const N: usize> !UnwindSafe for Supervisor<N>

§

impl<const N: usize> Freeze for Supervisor<N>

§

impl<const N: usize> Send for Supervisor<N>

§

impl<const N: usize> Sync for Supervisor<N>

§

impl<const N: usize> Unpin for Supervisor<N>

§

impl<const N: usize> UnsafeUnpin for Supervisor<N>

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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, 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.