Expand description
ridl-loopback — the in-process reference runtime.
This crate implements the eleven port traits of
ridl_rt::port over one in-memory store. It is
the first runtime in this workspace (ADR-0020 decision 6, which names the
crate and fixes ridl-rt as its only dependency), and it is what the
generated interaction face runs against in a test, an example or a
single-process application.
What it is not:
- Not a transport. It carries no frame, opens no socket and has no wire format. A value published here is read here, in the same process.
- Not the engine. The store below is a map and a queue, not the seqlock store, the sans-IO session, the platform traits and the scheduler, which are outside this repository.
- Not a checker. Payload bytes are opaque to it: it carries them and
never verifies one.
Payload::verifyis the generated face’s, on both sides.
§The handles
A runtime presents one handle type per port role rather than one type
implementing them all, and may also offer an aggregate handle covering the
port set one interface’s face needs (ADR-0021 decision 12). This crate
offers both. Its six handles group the eleven roles the way that decision
derives the threading split: a handle each for the five roles with a
&mut self method, and one handle for the six whose methods all take
&self, which are exactly the roles several threads may hold at once.
Loopback is the aggregate: it implements all eleven port traits by delegating to the six
role handles it holds, and it is what a generated Client, Publisher or
dispatch is normally built over.
use ridl_loopback::Loopback;
use ridl_rt::contract::{CatalogHash, CatalogRef, InterfaceNo, Ordinal};
use ridl_rt::port::{SignalReader, SignalWriter};
let catalog = CatalogRef { name: "face.demo", hash: CatalogHash([0u8; 32]) };
let mut rt = Loopback::new(catalog);
rt.set(InterfaceNo(1), Ordinal(1), &[42]).expect("staged");
rt.commit();
let mut out = [0u8; 8];
let sample = rt.read(InterfaceNo(1), Ordinal(1), &mut out).expect("read");
assert_eq!(&out[..sample.len], &[42]);Loopback::split hands out the six role handles for a program that wants
them apart — one thread reading while another publishes, or two callers on
one provider. ReaderHandle is Send + Sync; the other five are Send
and driven by one thread each. Nothing here declares either: both follow
from the fields, and the assertions at the bottom of this file pin them.
§What it reports, and what it cannot
The loopback holds no catalog descriptor — the descriptor and the catalog
hash arrive with story E16.2 (driftsys/ridl#378) — so it has no member
table, and there is no ordinal it can call unknown, no member it can call
unowned, and no timing annotation it can measure a value’s freshness or a
call’s remaining time against. What it therefore never returns:
WriteError::NotOwner, RaiseError::NotOwner, ServeError::NotOwner, any
port error’s Contract variant except
FixedReader::read_fixed’s, and
Freshness::Fresh or Freshness::Stale. Nothing detaches, because every
handle holds the store alive, so Detached never appears either; and
nothing is bounded, so Busy and TooLarge do not appear outside
Loopback::fail_next_settle.
Attached::catalog returns the
CatalogRef the runtime was built with, unexamined. ADR-0021 decision 3
places a check of it against the interface’s own CATALOG in a generated
face’s constructor, once, when the face is built; the constructor the Rust
backend emits today performs no such check (driftsys/ridl#448). Either way
it is the face’s check and not the runtime’s: the loopback carries the
value and compares nothing.
The crate’s as-built design record, with the reasoning behind each of these
choices, is docs/design/ridl-loopback.md in this repository.
Structs§
- Caller
Handle - The
Callerport role. - Handler
Handle - The
Handlerport role. - Handles
- The six role handles of one runtime, as
Loopback::splithands them out. - Loopback
- The aggregate handle: one value implementing all eleven port traits by delegating to the six role handles it holds.
- Reader
Handle - The
&selfport roles:Attached,Clock,SignalReader,FixedReader, and the two signal extensions. - Sink
Handle - The
EventSinkport role. - Source
Handle - The
EventSourceport role: one subscription set and one queue, both in the store under this handle’s identity. - Writer
Handle - The
SignalWriterport role.