pub struct ExecutorNodeRuntime { /* private fields */ }Expand description
Executor-backed component runtime.
Owns the Executor and one slot per registered component. The
register / spin lifecycle:
from_executorwraps an openExecutor.register_nodebuilds the component’sState, runsNode::registerover an internalNodeRuntimeadapter that materialises nodes / pubs / subs / timers on the real executor, and wires each subscription + timer callback to dispatch intoExecutableNode::on_callbackwith the rightCallbackId.spin/spin_oncedrive the executor; between iterations every registered component’sExecutableNode::tickruns.
Implementations§
Source§impl ExecutorNodeRuntime
impl ExecutorNodeRuntime
Sourcepub fn from_executor(executor: Executor<'static>) -> Self
pub fn from_executor(executor: Executor<'static>) -> Self
Sourcepub unsafe fn new_in(
executor: Executor<'static>,
backing: &'static mut [MaybeUninit<u64>],
sizing: RuntimeSizing,
) -> Self
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.
Sourcepub fn executor_mut(&mut self) -> &mut Executor<'static>
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.
Sourcepub 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>,
)
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_us → SchedClass::Sporadic;
best_effort → SchedClass::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).
Sourcepub fn component_count(&self) -> usize
pub fn component_count(&self) -> usize
Number of registered components.
Sourcepub fn register_node<C: ExecutableNode + 'static>(
&mut self,
) -> NodeResult<RegisteredNode<C>>where
C::State: 'static,
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.
Sourcepub fn spin_once(&mut self, timeout: Duration) -> Result<(), ExecutorError>
pub fn spin_once(&mut self, timeout: Duration) -> Result<(), ExecutorError>
Drive one executor iteration + a tick per registered
component.
Sourcepub fn spin_once_counted(
&mut self,
timeout: Duration,
) -> Result<SpinOnceResult, ExecutorError>
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.
Sourcepub fn dispatch_callback(&mut self, cb_id: &str, ctx: &mut CallbackCtx<'_>)
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.
Sourcepub fn spin(&mut self) -> Result<(), ExecutorError>
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.
Trait Implementations§
Source§impl NodeDispatchRuntime for ExecutorNodeRuntime
Available on crate feature alloc only.
impl NodeDispatchRuntime for ExecutorNodeRuntime
alloc only.Source§fn spin_once(&mut self, timeout_ms: u32) -> Result<(), ()>
fn spin_once(&mut self, timeout_ms: u32) -> Result<(), ()>
timeout_ms
milliseconds. Ok(()) on a clean spin (including timeout);
Err(()) if the executor surfaces a spin error. Read moreSource§fn executor_handle(&mut self) -> *mut c_void
fn executor_handle(&mut self) -> *mut c_void
*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-platform → nros layering wall cleanly (the concrete
ExecutorNodeRuntime lives in nros; this trait can’t name it). Read moreSource§fn observed_callback_counts(&self) -> (usize, usize)
fn observed_callback_counts(&self) -> (usize, usize)
Source§fn apply_lifecycle(&mut self, autostart: u8) -> Result<(), &'static str>
fn apply_lifecycle(&mut self, autostart: u8) -> Result<(), &'static str>
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 moreSource§fn apply_param_services(
&mut self,
params: &[(&str, &str)],
) -> Result<(), &'static str>
fn apply_param_services( &mut self, params: &[(&str, &str)], ) -> Result<(), &'static str>
<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 moreSource§fn signal_callback(&mut self, _cb: SignaledCallback<'_>)
fn signal_callback(&mut self, _cb: SignaledCallback<'_>)
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
fn dispatch_strategy(&self) -> DispatchStrategy
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.