Skip to main content

Module handoff

Module handoff 

Source
Expand description

Phase 104.E.3 — cross-priority handoff queue.

Bridges that span priority boundaries (sub callback at priority A, pub callback at priority B) need a bounded handoff queue between the two so the high-priority sub doesn’t block on the lower-priority pub’s transport drain. The existing pattern is Arc<Mutex<heapless::Vec<M, N>>> + a timer-driven pub; this module wraps it in a small Handoff<M, N> type so bridge code stays terse.

Optional sugar — the manual pattern remains supported. The spec (Phase 104.E.3) explicitly lists this as “optional” convenience to avoid forcing every bridge to use the same shape.

use nros_node::executor::handoff::Handoff;
use std::sync::Arc;

// Shared bounded queue, N = 32, message type M.
let q: Arc<Handoff<MyMsg, 32>> = Arc::new(Handoff::new());

// High-priority ingress: push into the queue inside the
// sub callback. `push` is non-blocking — overflow returns
// `Err(msg)` so the high-pri side never stalls.
let q_pub = Arc::clone(&q);
executor.register_subscription::<MyMsg, _>(topic, move |msg: &MyMsg| {
    let _ = q_pub.push(msg.clone());  // drop on overflow
})?;

// Low-priority egress: timer drains the queue + publishes.
let q_sub = Arc::clone(&q);
let pub_out = ...;
executor.register_timer(period, move || {
    while let Some(msg) = q_sub.pop() {
        let _ = pub_out.publish(&msg);
    }
})?;

Cross-priority safety: every push / pop takes the internal mutex for the duration of one queue slot operation (O(1)). On PiCAS-aware dispatchers (Phase 110.F) the mutex inherits the holder’s effective priority, so the low-pri drain doesn’t priority-invert the high-pri push.

std-gated for now — the alloc-only path needs a lock-free SPSC queue (heapless::spsc requires a .split() call that doesn’t compose with Arc-sharing across callbacks). Tracked under follow-up if no_std bridges become a use case.

Structs§

Handoff
Bounded FIFO between two callbacks running on different SchedContexts. Generic over message type M (must be Send for cross-thread executors) and capacity N.