use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::task::Waker;
use ridl_rt::contract::{InterfaceNo, Ordinal};
use ridl_rt::correlate::{Forgotten, Settled, Table, Waiters};
use ridl_rt::error::{CallError, Transport};
use ridl_rt::port::{
Changed, Claim, ClaimId, Correlation, Interest, RawOccurrence, RawSample, ReadError, SendError,
SettleError, Watermark,
};
use ridl_rt::sample::{Cause, Envelope, Freshness, Provenance, Timestamp};
pub(crate) type Key = (InterfaceNo, Ordinal);
type Calls = Table<{ crate::Loopback::SLOTS }>;
struct SignalEntry {
bytes: Vec<u8>,
envelope: Envelope,
invalid: bool,
changed_at: u64,
}
pub(crate) enum Staged {
Set(Vec<u8>),
Invalidate,
Touch,
}
struct QueuedEvent {
iface: InterfaceNo,
ord: Ordinal,
bytes: Vec<u8>,
envelope: Envelope,
}
#[derive(Default)]
struct SourceState {
subscribed: BTreeSet<Key>,
queue: VecDeque<QueuedEvent>,
waiters: Waiters,
}
#[derive(Default)]
struct HandlerState {
served: Vec<Key>,
waiters: Waiters,
}
impl HandlerState {
fn serves(&self, key: Key) -> bool {
self.served.is_empty() || self.served.contains(&key)
}
}
struct ClaimOwner {
call: Correlation,
handler: usize,
taken: bool,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum CallKind {
Command,
Query,
}
struct CallEntry {
correlation: Correlation,
caller: usize,
kind: CallKind,
iface: InterfaceNo,
ord: Ordinal,
args: Vec<u8>,
envelope: Envelope,
sent: u64,
reply: Vec<u8>,
forgotten: bool,
claim: Option<u64>,
}
pub(crate) struct Store {
now: Timestamp,
signals: BTreeMap<Key, SignalEntry>,
generations: BTreeMap<InterfaceNo, u64>,
fixed: BTreeMap<Key, Vec<u8>>,
sources: BTreeMap<usize, SourceState>,
next_source_id: usize,
table: Calls,
calls: BTreeMap<usize, CallEntry>,
pending: VecDeque<Correlation>,
next_sent: u64,
callers: BTreeMap<usize, Waiters>,
next_caller_id: usize,
claims: BTreeMap<u64, ClaimOwner>,
next_claim_id: u64,
handlers: BTreeMap<usize, HandlerState>,
next_handler_id: usize,
fail_next_settle: bool,
}
impl Store {
pub(crate) fn new() -> Self {
Store {
now: Timestamp(0),
signals: BTreeMap::new(),
generations: BTreeMap::new(),
fixed: BTreeMap::new(),
sources: BTreeMap::new(),
next_source_id: 0,
table: Calls::new(None),
calls: BTreeMap::new(),
pending: VecDeque::new(),
next_sent: 0,
callers: BTreeMap::new(),
next_caller_id: 0,
claims: BTreeMap::new(),
next_claim_id: 0,
handlers: BTreeMap::new(),
next_handler_id: 0,
fail_next_settle: false,
}
}
pub(crate) fn now(&self) -> Timestamp {
self.now
}
pub(crate) fn advance(&mut self, by: ridl_rt::sample::Duration) {
assert!(by.0 >= 0, "the clock advances forward: `by` is {}", by.0);
self.now = Timestamp(self.now.0.saturating_add(by.0));
}
pub(crate) fn read(
&self,
iface: InterfaceNo,
ord: Ordinal,
out: &mut [u8],
) -> Result<RawSample, ReadError> {
let Some(entry) = self.signals.get(&(iface, ord)) else {
return Ok(unpublished());
};
if out.len() < entry.bytes.len() {
return Err(ReadError::Short {
needed: entry.bytes.len(),
});
}
out[..entry.bytes.len()].copy_from_slice(&entry.bytes);
Ok(sample_of(entry))
}
pub(crate) fn commit(
&mut self,
staged: &mut BTreeMap<Key, Staged>,
seqs: &mut BTreeMap<Key, u64>,
) {
let stamp = self.now;
staged.retain(|key, op| !matches!(op, Staged::Touch) || self.signals.contains_key(key));
let mut bumped = BTreeSet::new();
for (iface, _) in staged.keys() {
if bumped.insert(*iface) {
*self.generations.entry(*iface).or_insert(0) += 1;
}
}
for (key, op) in std::mem::take(staged) {
let seq = {
let counter = seqs.entry(key).or_insert(0);
*counter += 1;
*counter
};
let changed_at = self.generations[&key.0];
let envelope = Envelope { stamp, seq };
let previous = self.signals.get(&key);
let (bytes, invalid) = match op {
Staged::Set(bytes) => (bytes, false),
Staged::Invalidate => (previous.map(|e| e.bytes.clone()).unwrap_or_default(), true),
Staged::Touch => {
let entry = previous.expect("an unpublished touch was dropped above");
(entry.bytes.clone(), entry.invalid)
}
};
self.signals.insert(
key,
SignalEntry {
bytes,
envelope,
invalid,
changed_at,
},
);
}
}
pub(crate) fn generation(&self, iface: InterfaceNo) -> u64 {
self.generations.get(&iface).copied().unwrap_or(0)
}
pub(crate) fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize {
let mut written = 0usize;
for mark in marks.iter_mut() {
let current = self.generation(mark.iface);
if mark.generation >= current {
continue;
}
let changes = self
.signals
.iter()
.filter(|((iface, _), entry)| {
*iface == mark.iface && entry.changed_at > mark.generation
})
.map(|((iface, ord), entry)| Changed {
iface: *iface,
ord: *ord,
seq: entry.envelope.seq,
});
let staging: Vec<Changed> = changes.collect();
if staging.len() > out.len() - written {
return written;
}
for change in &staging {
out[written] = *change;
written += 1;
}
mark.generation = current;
if let Some(highest) = staging.iter().map(|change| change.seq).max() {
mark.seq = highest;
}
}
written
}
pub(crate) fn read_coherent(
&self,
iface: InterfaceNo,
ords: &[Ordinal],
out: &mut [u8],
samples: &mut [RawSample],
) -> Result<usize, ReadError> {
if samples.len() < ords.len() {
return Err(ReadError::TooFewSamples { needed: ords.len() });
}
let needed: usize = ords
.iter()
.map(|ord| {
self.signals
.get(&(iface, *ord))
.map_or(0, |entry| entry.bytes.len())
})
.sum();
if out.len() < needed {
return Err(ReadError::Short { needed });
}
let mut written = 0usize;
for (index, ord) in ords.iter().enumerate() {
match self.signals.get(&(iface, *ord)) {
None => samples[index] = unpublished(),
Some(entry) => {
out[written..written + entry.bytes.len()].copy_from_slice(&entry.bytes);
written += entry.bytes.len();
samples[index] = sample_of(entry);
}
}
}
Ok(written)
}
pub(crate) fn provision_fixed(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) {
self.fixed.insert((iface, ord), bytes.to_vec());
}
pub(crate) fn read_fixed(
&self,
iface: InterfaceNo,
ord: Ordinal,
out: &mut [u8],
) -> Result<usize, ReadError> {
let Some(bytes) = self.fixed.get(&(iface, ord)) else {
return Err(ReadError::Contract(
ridl_rt::error::Contract::UnknownInteraction,
));
};
if out.len() < bytes.len() {
return Err(ReadError::Short {
needed: bytes.len(),
});
}
out[..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
}
pub(crate) fn open_source(&mut self) -> usize {
let id = self.next_source_id;
self.next_source_id += 1;
self.sources.insert(id, SourceState::default());
id
}
pub(crate) fn close_source(&mut self, id: usize) -> Option<Waiters> {
self.sources.remove(&id).map(|state| state.waiters)
}
pub(crate) fn subscribe(&mut self, id: usize, iface: InterfaceNo, ords: &[Ordinal]) {
let Some(state) = self.sources.get_mut(&id) else {
return;
};
for ord in ords {
state.subscribed.insert((iface, *ord));
}
}
pub(crate) fn unsubscribe(&mut self, id: usize, iface: InterfaceNo, ords: &[Ordinal]) {
let Some(state) = self.sources.get_mut(&id) else {
return;
};
let SourceState {
subscribed, queue, ..
} = state;
for ord in ords {
subscribed.remove(&(iface, *ord));
}
queue.retain(|event| subscribed.contains(&(event.iface, event.ord)));
}
pub(crate) fn raise(
&mut self,
iface: InterfaceNo,
ord: Ordinal,
bytes: &[u8],
seq: u64,
wake: &mut Vec<Waker>,
) {
let envelope = Envelope {
stamp: self.now,
seq,
};
for state in self.sources.values_mut() {
if state.subscribed.contains(&(iface, ord)) {
state.queue.push_back(QueuedEvent {
iface,
ord,
bytes: bytes.to_vec(),
envelope,
});
wake.extend(state.waiters.take(Interest::Event(iface)));
}
}
}
pub(crate) fn wait_event(
&mut self,
id: usize,
what: Interest,
waker: &Waker,
wake: &mut Vec<Waker>,
) {
let Some(state) = self.sources.get_mut(&id) else {
wake.push(waker.clone());
return;
};
let ready = !state.queue.is_empty();
register(&mut state.waiters, what, waker, ready, wake);
}
pub(crate) fn next_event(
&mut self,
id: usize,
out: &mut [u8],
) -> Result<Option<RawOccurrence>, ReadError> {
let Some(state) = self.sources.get_mut(&id) else {
return Ok(None);
};
let Some(front) = state.queue.front() else {
return Ok(None);
};
if out.len() < front.bytes.len() {
return Err(ReadError::Short {
needed: front.bytes.len(),
});
}
let event = state.queue.pop_front().expect("the front was just read");
out[..event.bytes.len()].copy_from_slice(&event.bytes);
Ok(Some(RawOccurrence {
iface: event.iface,
ord: event.ord,
envelope: event.envelope,
len: event.bytes.len(),
}))
}
pub(crate) fn send(
&mut self,
caller: usize,
kind: CallKind,
(iface, ord): Key,
args: &[u8],
seq: u64,
wake: &mut Vec<Waker>,
) -> Result<Correlation, SendError> {
let c = self.table.insert(0).ok_or(SendError::Busy)?;
let sent = self.next_sent;
self.next_sent += 1;
let envelope = Envelope {
stamp: self.now,
seq,
};
self.calls.insert(
Calls::slot(c),
CallEntry {
correlation: c,
caller,
kind,
iface,
ord,
args: args.to_vec(),
envelope,
sent,
reply: Vec::new(),
forgotten: false,
claim: None,
},
);
self.pending.push_back(c);
self.wake_handlers_serving((iface, ord), wake);
Ok(c)
}
fn entry(&self, c: Correlation) -> &CallEntry {
&self.calls[&Calls::slot(c)]
}
pub(crate) fn wait_outcome(&mut self, c: Correlation, waker: &Waker, wake: &mut Vec<Waker>) {
wake.extend(self.table.wake_on(c, waker));
}
pub(crate) fn ack(&self, c: Correlation) -> Option<Result<(), CallError>> {
let outcome = self.table.outcome(c)?;
if self.entry(c).kind != CallKind::Command {
return None;
}
Some(outcome)
}
pub(crate) fn reply(
&self,
c: Correlation,
out: &mut [u8],
) -> Result<Option<Result<usize, CallError>>, ReadError> {
match self.table.outcome(c) {
None => Ok(None),
Some(Err(error)) => Ok(Some(Err(error))),
Some(Ok(())) => {
let bytes = &self.entry(c).reply;
if out.len() < bytes.len() {
return Err(ReadError::Short {
needed: bytes.len(),
});
}
out[..bytes.len()].copy_from_slice(bytes);
Ok(Some(Ok(bytes.len())))
}
}
}
pub(crate) fn forget(&mut self, c: Correlation, wake: &mut Vec<Waker>) {
if !self.offered(c)
&& let Some(at) = self.pending.iter().position(|waiting| *waiting == c)
{
self.pending.remove(at);
self.withdraw(c, wake);
return;
}
match self.table.forget(c) {
Forgotten::Reclaimed => self.reclaimed(c, wake),
Forgotten::Marked(waker) => {
wake.extend(waker);
self.calls
.get_mut(&Calls::slot(c))
.expect("a call in flight has its entry")
.forgotten = true;
}
Forgotten::Unknown => {}
}
}
fn withdraw(&mut self, c: Correlation, wake: &mut Vec<Waker>) {
match self
.table
.settle(c, Err(CallError::Transport(Transport::Undelivered)))
{
Settled::Recorded(waker) => {
wake.extend(waker);
match self.table.forget(c) {
Forgotten::Reclaimed => self.reclaimed(c, wake),
Forgotten::Marked(_) | Forgotten::Unknown => {
unreachable!("the call was settled just above")
}
}
}
Settled::Reclaimed => self.reclaimed(c, wake),
Settled::Unknown => unreachable!("a withdrawn call is in flight"),
}
}
fn reclaimed(&mut self, c: Correlation, wake: &mut Vec<Waker>) {
self.calls.remove(&Calls::slot(c));
for waiters in self.callers.values_mut() {
wake.extend(waiters.take(Interest::Slot));
}
}
pub(crate) fn open_caller(&mut self) -> usize {
let id = self.next_caller_id;
self.next_caller_id += 1;
self.callers.insert(id, Waiters::new());
id
}
pub(crate) fn close_caller(&mut self, id: usize, wake: &mut Vec<Waker>) -> Option<Waiters> {
let waiters = self.callers.remove(&id);
let sent: Vec<Correlation> = self
.calls
.values()
.filter(|entry| entry.caller == id)
.map(|entry| entry.correlation)
.collect();
for c in sent {
self.forget(c, wake);
}
waiters
}
pub(crate) fn wait_slot(&mut self, id: usize, waker: &Waker, wake: &mut Vec<Waker>) {
let ready = self.calls.len() < crate::Loopback::SLOTS;
let Some(waiters) = self.callers.get_mut(&id) else {
wake.push(waker.clone());
return;
};
register(waiters, Interest::Slot, waker, ready, wake);
}
pub(crate) fn open_handler(&mut self) -> usize {
let id = self.next_handler_id;
self.next_handler_id += 1;
self.handlers.insert(id, HandlerState::default());
id
}
pub(crate) fn close_handler(&mut self, id: usize, wake: &mut Vec<Waker>) -> Option<Waiters> {
let waiters = self.handlers.remove(&id).map(|state| state.waiters);
let held: Vec<u64> = self
.claims
.iter()
.filter(|(_, owner)| owner.handler == id)
.map(|(claim, _)| *claim)
.collect();
for claim in held {
let owner = self.claims.remove(&claim).expect("listed above");
let entry = self.entry(owner.call);
if entry.forgotten {
if !owner.taken
&& let Some(at) = self.pending.iter().position(|c| *c == owner.call)
{
self.pending.remove(at);
}
self.withdraw(owner.call, wake);
continue;
}
if !owner.taken {
continue;
}
let (sent, key) = (entry.sent, (entry.iface, entry.ord));
let at = self
.pending
.partition_point(|call| self.calls[&Calls::slot(*call)].sent < sent);
self.pending.insert(at, owner.call);
self.wake_handlers_serving(key, wake);
}
waiters
}
fn wake_handlers_serving(&mut self, key: Key, wake: &mut Vec<Waker>) {
for state in self.handlers.values_mut() {
if state.serves(key) {
wake.extend(state.waiters.take(Interest::Claim(key.0)));
}
}
}
pub(crate) fn serve(
&mut self,
handler: usize,
iface: InterfaceNo,
ords: &[Ordinal],
wake: &mut Vec<Waker>,
) {
let Some(state) = self.handlers.get_mut(&handler) else {
return;
};
for ord in ords {
if !state.served.contains(&(iface, *ord)) {
state.served.push((iface, *ord));
}
}
let Some(waker) = state.waiters.take(Interest::Claim(iface)) else {
return;
};
if self.claim_waiting(handler) {
wake.push(waker);
} else {
let state = self.handlers.get_mut(&handler).expect("looked up above");
let displaced = state.waiters.register(Interest::Claim(iface), &waker);
debug_assert!(displaced.is_none(), "the kind was empty");
}
}
pub(crate) fn wait_claim(
&mut self,
handler: usize,
what: Interest,
waker: &Waker,
wake: &mut Vec<Waker>,
) {
let ready = self.claim_waiting(handler);
let Some(state) = self.handlers.get_mut(&handler) else {
wake.push(waker.clone());
return;
};
register(&mut state.waiters, what, waker, ready, wake);
}
fn claim_waiting(&self, handler: usize) -> bool {
let Some(state) = self.handlers.get(&handler) else {
return false;
};
self.pending.iter().any(|c| {
let entry = self.entry(*c);
state.serves((entry.iface, entry.ord))
})
}
pub(crate) fn next_claim(
&mut self,
handler: usize,
out: &mut [u8],
) -> Result<Option<Claim>, ReadError> {
let Some(state) = self.handlers.get(&handler) else {
return Ok(None);
};
let position = self.pending.iter().position(|c| {
let entry = self.entry(*c);
state.serves((entry.iface, entry.ord))
});
let Some(position) = position else {
return Ok(None);
};
let c = self.pending[position];
let claim_id = match self.calls[&Calls::slot(c)].claim {
Some(id) => id,
None => {
let id = self.next_claim_id;
self.next_claim_id += 1;
self.calls
.get_mut(&Calls::slot(c))
.expect("a waiting call has its entry")
.claim = Some(id);
id
}
};
let entry = &self.calls[&Calls::slot(c)];
if out.len() < entry.args.len() {
let needed = entry.args.len();
self.claims.insert(
claim_id,
ClaimOwner {
call: c,
handler,
taken: false,
},
);
return Err(ReadError::ShortClaim {
claim: ClaimId(claim_id),
needed,
});
}
out[..entry.args.len()].copy_from_slice(&entry.args);
let claim = Claim {
id: ClaimId(claim_id),
iface: entry.iface,
ord: entry.ord,
envelope: entry.envelope,
remaining: None,
len: entry.args.len(),
};
self.pending.remove(position);
self.claims.insert(
claim_id,
ClaimOwner {
call: c,
handler,
taken: true,
},
);
Ok(Some(claim))
}
pub(crate) fn settle(
&mut self,
handler: usize,
claim: ClaimId,
outcome: Result<&[u8], CallError>,
wake: &mut Vec<Waker>,
) -> Result<(), SettleError> {
let (c, taken) = match self.claims.get(&claim.0) {
Some(owner) if owner.handler == handler => (owner.call, owner.taken),
_ => return Err(SettleError::UnknownClaim),
};
if self.fail_next_settle {
self.fail_next_settle = false;
return Err(SettleError::TooLarge { cap: 0 });
}
self.claims.remove(&claim.0);
if !taken && let Some(at) = self.pending.iter().position(|waiting| *waiting == c) {
self.pending.remove(at);
}
match self.table.settle(c, outcome.map(|_| ())) {
Settled::Recorded(waker) => {
if let Ok(bytes) = outcome {
self.calls
.get_mut(&Calls::slot(c))
.expect("a claim names a call the table holds")
.reply = bytes.to_vec();
}
wake.extend(waker);
}
Settled::Reclaimed => self.reclaimed(c, wake),
Settled::Unknown => unreachable!("a claim names a call in flight"),
}
Ok(())
}
pub(crate) fn fail_next_settle(&mut self) {
self.fail_next_settle = true;
}
fn offered(&self, c: Correlation) -> bool {
self.calls
.get(&Calls::slot(c))
.and_then(|entry| entry.claim)
.and_then(|id| self.claims.get(&id))
.is_some_and(|owner| !owner.taken)
}
}
fn register(
waiters: &mut Waiters,
what: Interest,
waker: &Waker,
ready: bool,
wake: &mut Vec<Waker>,
) {
wake.extend(waiters.register(what, waker));
if ready {
wake.extend(waiters.take(what));
}
}
fn unpublished() -> RawSample {
RawSample {
provenance: Provenance::Init,
freshness: Freshness::Unbounded,
envelope: Envelope {
stamp: Timestamp(0),
seq: 0,
},
len: 0,
}
}
fn sample_of(entry: &SignalEntry) -> RawSample {
RawSample {
provenance: if entry.invalid {
Provenance::Invalid(Cause::Declared)
} else {
Provenance::Live
},
freshness: Freshness::Unbounded,
envelope: entry.envelope,
len: entry.bytes.len(),
}
}