Skip to content

executor: provide a minimal Rust executor pollable from a C super-loop #121

Description

@JustinKovacich

Context

The simple_someip API as of 0.8.0 exposes the protocol state machine as a single 'static future per side (Server::run / run_with_buffers, plus the run-future returned from Client::new_with_deps_local). These futures are deliberately executor-agnostic — anything that can poll a Future will drive them.

We use tokio on std-side tests and examples/client_server, and we exercise embassy via simple-someip-embassy-net's loopback test. Neither is viable on the TC4 target. The TC4 host runs a C super-loop that schedules QM/ASIL tasks via hardware interrupts; the planned integration is a Rust executor polled cooperatively from C land.

"We have an input queue and an output queue, if those aren't empty, do work, else return."

— design discussion from the TC4 bring-up planning meeting

Goal

Provide a small async executor — target footprint ~30 LOC of Rust plus a thin C-FFI shim — that:

  1. Owns one or more long-lived futures (the Server::run future, the Client run-future, possibly a UDS run-future) in static storage.
  2. Exposes extern "C" fn rust_tick() (name TBD) that the C super-loop calls each iteration. The function polls every owned future once, returns control when none can make further progress.
  3. Is no_alloc-clean: must not require a heap allocator. Uses the same static-pool primitives the bare-metal Client / Server already use.
  4. Is small enough to self-certify if we eventually take simple_someip down a certification path.

Sketch

// In a new crate (or a `c_host` module of `simple-someip`).

static SERVER_RUN: StaticCell<Pin<&'static mut dyn Future<Output = Result<(), Error>>>> = StaticCell::new();

#[no_mangle]
pub extern "C" fn simple_someip_init(/* config refs */) -> i32 {
    // Build server + client via `new_with_handles` (sync constructors).
    // Stash their run-futures in static slots.
}

#[no_mangle]
pub extern "C" fn simple_someip_tick() {
    let waker = noop_waker(); // or a "set a wake-flag" waker if we want to be smart
    let mut cx = Context::from_waker(&waker);
    for fut in pinned_iter() {
        let _ = fut.as_mut().poll(&mut cx);
    }
}

Strawman waker is a no-op (every tick polls every future); smarter waker is a per-slot AtomicBool "needs poll" flag set from inside futures' wake-up paths. Smarter waker only matters if the polling cost becomes measurable; busy-poll-each-tick is fine for the message rates simple_someip handles.

Open questions

  • Use lilos vs. hand-roll: lilos is a small, tested embedded executor by Cliff Biffle — but it's designed as a primary RTOS, not a child executor inside another scheduler. Its scheduling primitives may not align with "called once per super-loop iteration, return as soon as everything's pending." A hand-rolled 30-LOC executor that does exactly that may be a better fit. Evaluate both.
  • Where it lives: inside simple-someip (as a feature-gated c_host module), in a sibling crate (mirroring simple-someip-embassy-net's pattern), or in the integrating firmware tree. Sibling crate is the cleanest separation; firmware tree keeps the integration concerns out of the protocol library entirely.
  • Waker design: no-op vs wake-flag. Affects how often we waste cycles polling a future that has nothing to do.
  • Construction-time async: Server::new_with_deps is async fn (the bind step needs to await on tokio). For C-host construction we go through new_with_handles (sync — caller provides pre-bound sockets). This is fine; just noting that the executor doesn't need to drive an async constructor.

Blocking dependency

TC4 build infrastructure must be live before this can be exercised on hardware. See #117 for the CI matrix and the upstream-Rust-vs-HighTec toolchain question. This issue can be prototyped on thumbv7em-none-eabihf (the no_std proxy target we use today) without TC4 access — just need the executor and a smoke test that polls a stub future from a C harness.

Pairing

Companion to #121 (C-FFI queue surface). The executor polls run-futures; the queue is how C-land submits work to and reads results from those futures.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsimple_someipIssue related to the simple_someip crate

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions