use std::collections::VecDeque;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll};
use futures::Stream;
use futures::task::AtomicWaker;
use super::super::protocol::{MAX_SLOT_INDEX, SlotId};
use crate::streaming::messenger_mux::flow_control::{CreditClass, SlotCredit};
pub(super) struct SlotGate {
closed: AtomicBool,
waker: AtomicWaker,
}
impl SlotGate {
fn new() -> Self {
Self {
closed: AtomicBool::new(false),
waker: AtomicWaker::new(),
}
}
fn close(&self) {
self.closed.store(true, Ordering::Release);
self.waker.wake();
}
fn is_closed(&self) -> bool {
self.closed.load(Ordering::Acquire)
}
}
#[derive(Debug)]
pub(super) enum SlotItem {
Frame(Vec<u8>),
InletClosed,
}
pub(super) struct SlotStream {
index: u32,
gate: Arc<SlotGate>,
inner: flume::r#async::RecvStream<'static, Vec<u8>>,
announced_close: bool,
}
impl Stream for SlotStream {
type Item = (u32, SlotItem);
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
if this.announced_close {
return Poll::Ready(None);
}
this.gate.waker.register(cx.waker());
if this.gate.is_closed() {
return Poll::Ready(None);
}
match Pin::new(&mut this.inner).poll_next(cx) {
Poll::Ready(Some(bytes)) => Poll::Ready(Some((this.index, SlotItem::Frame(bytes)))),
Poll::Ready(None) => {
this.announced_close = true;
Poll::Ready(Some((this.index, SlotItem::InletClosed)))
}
Poll::Pending => Poll::Pending,
}
}
}
pub(super) struct WithheldQueue {
records: VecDeque<Vec<u8>>,
bytes: u64,
cap: u64,
}
impl WithheldQueue {
fn new(cap: u32) -> Self {
Self {
records: VecDeque::new(),
bytes: 0,
cap: u64::from(cap),
}
}
pub(super) fn is_empty(&self) -> bool {
self.records.is_empty()
}
pub(super) fn len(&self) -> usize {
self.records.len()
}
pub(super) fn push(&mut self, record: Vec<u8>) -> Result<(), WithheldOverflow> {
let len = record.len() as u64;
if !self.records.is_empty() && self.bytes.saturating_add(len) > self.cap {
return Err(WithheldOverflow {
queued: self.bytes,
cap: self.cap,
});
}
self.bytes = self.bytes.saturating_add(len);
self.records.push_back(record);
Ok(())
}
pub(super) fn front(&self) -> Option<&[u8]> {
self.records.front().map(Vec::as_slice)
}
pub(super) fn clear(&mut self) {
self.records.clear();
self.bytes = 0;
}
pub(super) fn pop(&mut self) -> Option<Vec<u8>> {
let record = self.records.pop_front()?;
self.bytes = self.bytes.saturating_sub(record.len() as u64);
Some(record)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("withheld {queued} bytes on a slot capped at {cap}")]
pub(super) struct WithheldOverflow {
queued: u64,
cap: u64,
}
pub(super) struct EgressSlot {
pub(super) id: SlotId,
pub(super) credit: SlotCredit,
pub(super) next_seq: u32,
pub(super) withheld: WithheldQueue,
pub(super) inlet_closed: bool,
gate: Arc<SlotGate>,
fenced: bool,
starved: bool,
}
impl EgressSlot {
pub(super) const fn is_fenced(&self) -> bool {
self.fenced
}
pub(super) fn must_withhold(&self, class: CreditClass) -> bool {
!self.withheld.is_empty() || self.fenced || !self.credit.can_spend(class)
}
pub(super) fn note_starved(&mut self) -> bool {
!std::mem::replace(&mut self.starved, true)
}
pub(super) fn note_flowing(&mut self) {
self.starved = false;
}
pub(super) fn fence(&mut self) {
self.fenced = true;
}
pub(super) fn unfence(&mut self) {
self.fenced = false;
}
pub(super) fn take_seq(&mut self) -> u32 {
let seq = self.next_seq;
self.next_seq = self.next_seq.saturating_add(1);
seq
}
}
#[derive(Default)]
pub(super) struct EgressSlots {
entries: Vec<Option<EgressSlot>>,
generations: Vec<u8>,
free: Vec<u32>,
live: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub(crate) enum AllocError {
#[error("slot index space exhausted for this peer epoch")]
IndexSpaceExhausted,
}
impl EgressSlots {
pub(super) const fn live(&self) -> usize {
self.live
}
pub(super) fn allocate(
&mut self,
rx: flume::Receiver<Vec<u8>>,
credit: SlotCredit,
slot_byte_budget: u32,
) -> Result<(SlotId, SlotStream), AllocError> {
let index = match self.free.pop() {
Some(index) => index,
None => {
let index = u32::try_from(self.entries.len()).unwrap_or(u32::MAX);
if index > MAX_SLOT_INDEX {
return Err(AllocError::IndexSpaceExhausted);
}
self.entries.push(None);
self.generations.push(0);
index
}
};
let generation = self.generations[index as usize];
let id = SlotId::new(index, generation).ok_or(AllocError::IndexSpaceExhausted)?;
let gate = Arc::new(SlotGate::new());
let stream = SlotStream {
index,
gate: Arc::clone(&gate),
inner: rx.into_stream(),
announced_close: false,
};
self.entries[index as usize] = Some(EgressSlot {
id,
credit,
next_seq: 0,
withheld: WithheldQueue::new(slot_byte_budget),
inlet_closed: false,
gate,
fenced: false,
starved: false,
});
self.live += 1;
Ok((id, stream))
}
pub(super) fn get_mut(&mut self, index: u32) -> Option<&mut EgressSlot> {
self.entries.get_mut(index as usize)?.as_mut()
}
pub(super) fn get_mut_checked(&mut self, id: SlotId) -> Option<&mut EgressSlot> {
let slot = self.get_mut(id.index())?;
(slot.id == id).then_some(slot)
}
pub(super) fn close(&mut self, index: u32) -> bool {
let Some(slot) = self.entries.get_mut(index as usize).and_then(Option::take) else {
return false;
};
slot.gate.close();
self.generations[index as usize] = slot.id.generation().wrapping_add(1);
self.free.push(index);
self.live -= 1;
true
}
pub(super) fn close_all(&mut self) -> usize {
let closed = self.live;
for index in 0..self.entries.len() {
let index = index as u32;
self.close(index);
}
closed
}
}