Skip to main content

ExecutorNodeRuntime

Struct ExecutorNodeRuntime 

Source
pub struct ExecutorNodeRuntime { /* private fields */ }
Expand description

Executor-backed component runtime.

Owns the Executor and one slot per registered component. The register / spin lifecycle:

  1. from_executor wraps an open Executor.
  2. register_node builds the component’s State, runs Node::register over an internal NodeRuntime adapter that materialises nodes / pubs / subs / timers on the real executor, and wires each subscription + timer callback to dispatch into ExecutableNode::on_callback with the right CallbackId.
  3. spin / spin_once drive the executor; between iterations every registered component’s ExecutableNode::tick runs.

Implementations§

Source§

impl ExecutorNodeRuntime

Source

pub fn from_executor(executor: Executor<'static>) -> Self

Wrap an already-built Executor, LEAKING the slot backing.

The convenience constructor — allocates RuntimeSizing::DEFAULT’s backing once and never frees it, exactly the relationship Executor::from_session has to Executor::open_in. Use new_in on an image that must not allocate.

Source

pub unsafe fn new_in( executor: Executor<'static>, backing: &'static mut [MaybeUninit<u64>], sizing: RuntimeSizing, ) -> Self

Wrap an already-built Executor over CALLER-SUPPLIED slot storage.

Size backing with crate::runtime_storage::RuntimeSizing::u64_len; a short one panics, naming both sizes (fail-loud on every profile — a short backing is silent corruption, the executor::storage::carve / issue #131 lesson).

§Safety

backing must be uniquely owned by this runtime for its whole life: slots carved from it are handed out as &'static mut, so aliasing it anywhere else is undefined behaviour.

Source

pub fn executor(&self) -> &Executor<'static>

Borrow the underlying executor.

Source

pub fn executor_mut(&mut self) -> &mut Executor<'static>

Mutably borrow the underlying executor — for advanced wiring (parameter services, custom guard conditions). Don’t use during spin from another thread; the runtime is single-threaded.

Source

pub fn apply_tier_sched_policy( &mut self, class: Option<&str>, period_us: Option<u64>, budget_us: Option<u64>, deadline_us: Option<u64>, deadline_policy: Option<&str>, )

RFC-0052 / phase-296 W5.4 — lower a tier’s RTOS-agnostic scheduling policy onto this executor’s DEFAULT scheduling context. One Executor per tier means “the tier’s policy” == “this executor’s default SC”; per-group/per-handle bindings still take precedence.

Portable across every board — call from each board’s run_tiers after building the runtime. Takes the tier fields as primitives (not a TierSpec) so nros needs no board/platform dependency; a board passes tier.class, tier.period_us, … straight through.

real_time + budget_us + period_usSchedClass::Sporadic; best_effortSchedClass::BestEffort; time_triggered + period_us → the cyclic dispatcher (major frame = period, window = budget_us or the whole frame); deadline_us sets the SC deadline and deadline_policy its action. A tier with no class/budget/deadline leaves the default Fifo SC untouched (byte-identical pre-W3 behavior).

Source

pub fn component_count(&self) -> usize

Number of registered components.

Source

pub fn register_node<C: ExecutableNode + 'static>( &mut self, ) -> NodeResult<RegisteredNode<C>>
where C::State: 'static,

Register a Node (which must also be ExecutableNode) into this runtime. Builds the component’s State (via ExecutableNode::init) and walks Node::register over the live executor — every declared node / pub / sub / timer materialises as a real executor handle, and subscription + timer callbacks are wired to dispatch into ExecutableNode::on_callback.

Source

pub fn spin_once(&mut self, timeout: Duration) -> Result<(), ExecutorError>

Drive one executor iteration + a tick per registered component.

Source

pub fn spin_once_counted( &mut self, timeout: Duration, ) -> Result<SpinOnceResult, ExecutorError>

Self::spin_once, returning the executor’s own per-iteration counts instead of discarding them — issue 0572.

spin_once throws away a SpinOnceResult that already carries exactly what a stalled tier needs to be diagnosed: how many timers fired, how many subscription callbacks ran, and how many errored. Without it, “the tier’s timer never fires” and “the tier’s callback runs and its publish fails” are the same observation from outside the guest — a silent topic.

Source

pub fn dispatch_callback(&mut self, cb_id: &str, ctx: &mut CallbackCtx<'_>)

Phase 216.B.3 / C.3 follow-up — route a signaled callback to every registered component slot.

The RTIC (nros-board-rtic-stm32f4) and Embassy (nros-board-embassy-stm32f4) dispatch tasks dequeue a nros_platform::SignaledCallback envelope from their SPSC queue / Embassy channel and need a routing entry point that hands the callback off to the right Node’s on_callback trampoline. This method is that entry point.

§Strategy — linear scan

Each registered slot’s dispatch_fn is the codegen-emitted d() trampoline from nros::node!() (see packages/core/nros-macros/src/lib.rs). That trampoline calls <NodeTy as ExecutableNode>::on_callback, whose body matches on the callback’s own tag set (Subscription / Timer / Service / Action ids) and is a no-op for non-matching cb_ids. So a linear scan across every slot is correct — each slot self-filters and at most one component actually acts on a given cb_id. A focused cb_id → slot index is a separate follow-up; the trampoline’s tag dispatch already gates the real work cheaply (string compare on statically known literals), so the linear scan is the minimum-viable wiring that closes the conceptual gap left by the B.3 / C.3 skeleton emits.

§Borrow semantics

Each ComponentCell’s slot lives behind a RefCell; the per-slot dispatch takes try_borrow_mut and is a no-op on re-entrancy. The runtime is single-threaded by construction (the dispatch task owns it via &mut self), so the borrow always succeeds in normal flow.

Source

pub fn spin(&mut self) -> Result<(), ExecutorError>

Spin until the executor’s halt flag is raised.

phase-359 W10 — this used to be #[cfg(feature = "std")] and described itself as hosted-only, but the body is Duration + spin_once + is_halted, none of which need an OS. The gate was describing a CONVENTION (a BSP usually wants its own loop so it can interleave board work) as if it were a requirement, and a bare-metal image that wants exactly this loop had to hand-roll it to get it.

It does need alloc: the halt flag is an Arc, so a core-only image has no flag to poll.

Source

pub fn halt(&self)

Halt a running spin. Idempotent.

Trait Implementations§

Source§

impl NodeDispatchRuntime for ExecutorNodeRuntime

Available on crate feature alloc only.
Source§

fn spin_once(&mut self, timeout_ms: u32) -> Result<(), ()>

Drive the underlying executor for at most timeout_ms milliseconds. Ok(()) on a clean spin (including timeout); Err(()) if the executor surfaces a spin error. Read more
Source§

fn executor_handle(&mut self) -> *mut c_void

Phase 258 (Track 2, 2a) — raw *mut Executor (as void*) for the owned-spin entry, so a Node pkg’s register(runtime) wrapper can call the uniform __nros_component_<pkg>_install(.., executor, ..) seam (nros::install_node_typed) instead of the retired opaque-fn-ptr register_dispatch_slot_dyn bridge. A pointer crosses the nros-platformnros layering wall cleanly (the concrete ExecutorNodeRuntime lives in nros; this trait can’t name it). Read more
Source§

fn observed_callback_counts(&self) -> (usize, usize)

Observability counters from hosted/runtime tests. Read more
Source§

fn apply_lifecycle(&mut self, autostart: u8) -> Result<(), &'static str>

Phase 264 W2 — register the REP-2002 lifecycle services on the underlying executor + drive boot autostart. autostart: 0 = none, 1 = configure, 2 = configure+activate. Default no-op (non-executor runtimes, or nros built without lifecycle-services); the ExecutorNodeRuntime impl in nros does the real work behind that feature. nros::main! calls this (via RuntimeCtx::apply_lifecycle) when system.toml declares [lifecycle], mirroring the bake’s generate.rs::render_lifecycle_fn. issue 0460 — the Err carries a STATIC REASON, not (). Read more
Source§

fn apply_param_services( &mut self, params: &[(&str, &str)], ) -> Result<(), &'static str>

Phase 264 W4b — register the 6 ROS 2 parameter services on the underlying executor + seed a volatile RAM param store with the launch-baked <param> initials (params is the aggregate (name, value) slice, value as the raw launch string — the impl infers the ParameterValue type). After this, ros2 param list/get/set works against the running node; reconfigured values live in RAM until the next boot (RFC-0004 §10; persistence is out of scope, issue 0080). Default no-op (non-executor runtimes, or nros built without param-services); the ExecutorNodeRuntime impl in nros does the real work behind that feature. nros::main! calls this (via RuntimeCtx::apply_param_services) when system.toml declares [param_services], mirroring the bake’s generate.rs::render_param_persistence_fn. issue 0460 — the Err carries a STATIC REASON, not (). Read more
Source§

fn signal_callback(&mut self, _cb: SignaledCallback<'_>)

Hand a signaled callback to the framework-side dispatcher (Phase 216.A.2). Only meaningful for DispatchStrategy::Deferred (RTIC / Embassy) runtimes — Inline runtimes drive callbacks directly from spin_once and never call this. The default panic surfaces the mis-wire loudly rather than silently dropping the callback signal.
Source§

fn dispatch_strategy(&self) -> DispatchStrategy

Declare how this runtime delivers callbacks (Phase 216.A.2). nros check (Phase 216.D.1) cross-validates each Node pkg’s Node::DISPATCH against this value. Defaults to Inline so every existing impl reports the historical behavior unchanged.

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