use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::string::String;
use core::cell::{Cell, RefCell};
use super::super::core::DriverOutput;
#[cfg(test)]
#[path = "signal_port_tests.rs"]
mod tests;
pub const MAX_PENDING_SIGNALS: usize = 256;
pub type AdapterSignalSink = Box<dyn FnMut(AdapterSignal)>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AdapterSignal {
Output(DriverOutput),
Fault(AdapterFault),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AdapterFault {
SeamReentered,
CommandExecutionFailed {
description: String,
},
SignalsLost {
overflowed: u64,
unqueued: u64,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct SignalPortStats {
pub peak_pending: usize,
pub overflowed: u64,
pub unqueued: u64,
pub drain_conflicts: u64,
}
pub(super) struct SignalPort {
queue: RefCell<VecDeque<AdapterSignal>>,
sink: RefCell<AdapterSignalSink>,
stats: Cell<SignalPortStats>,
owed: Cell<(u64, u64)>,
}
impl SignalPort {
pub(super) fn new(sink: AdapterSignalSink) -> Self {
Self {
queue: RefCell::new(VecDeque::new()),
sink: RefCell::new(sink),
stats: Cell::new(SignalPortStats::default()),
owed: Cell::new((0, 0)),
}
}
pub(super) fn stats(&self) -> SignalPortStats {
self.stats.get()
}
fn update(&self, change: impl FnOnce(&mut SignalPortStats)) {
let mut stats = self.stats.get();
change(&mut stats);
self.stats.set(stats);
}
pub(super) fn emit(&self, signal: AdapterSignal) {
match self.queue.try_borrow_mut() {
Ok(mut queue) if queue.len() < MAX_PENDING_SIGNALS => {
queue.push_back(signal);
let pending = queue.len();
self.update(|stats| stats.peak_pending = stats.peak_pending.max(pending));
}
Ok(_) => {
self.update(|stats| stats.overflowed = stats.overflowed.saturating_add(1));
let (overflowed, unqueued) = self.owed.get();
self.owed.set((overflowed.saturating_add(1), unqueued));
}
Err(_) => {
self.update(|stats| stats.unqueued = stats.unqueued.saturating_add(1));
let (overflowed, unqueued) = self.owed.get();
self.owed.set((overflowed, unqueued.saturating_add(1)));
}
}
self.drain();
}
pub(super) fn drain(&self) {
loop {
let Ok(mut sink) = self.sink.try_borrow_mut() else {
return;
};
let next = if let Ok(mut queue) = self.queue.try_borrow_mut() {
queue.pop_front()
} else {
self.update(|stats| {
stats.drain_conflicts = stats.drain_conflicts.saturating_add(1);
});
return;
};
let signal = match next {
Some(signal) => signal,
None => match self.owed.replace((0, 0)) {
(0, 0) => return,
(overflowed, unqueued) => AdapterSignal::Fault(AdapterFault::SignalsLost {
overflowed,
unqueued,
}),
},
};
sink(signal);
}
}
}