use std::collections::BTreeSet;
pub trait TideDemandJoinV0: Clone + PartialEq {
fn bottom() -> Self;
fn is_bottom(&self) -> bool;
fn join(&mut self, other: Self);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct TideSifDemandV0 {
owed: bool,
}
impl TideSifDemandV0 {
pub fn refresh() -> Self {
Self { owed: true }
}
}
impl TideDemandJoinV0 for TideSifDemandV0 {
fn bottom() -> Self {
Self { owed: false }
}
fn is_bottom(&self) -> bool {
!self.owed
}
fn join(&mut self, other: Self) {
self.owed |= other.owed;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TideRepublishDemandV0 {
None,
Cone(BTreeSet<String>),
All,
}
impl TideRepublishDemandV0 {
pub fn cone(seeds: impl IntoIterator<Item = String>) -> Self {
let seeds: BTreeSet<String> = seeds.into_iter().collect();
if seeds.is_empty() {
Self::None
} else {
Self::Cone(seeds)
}
}
}
impl TideDemandJoinV0 for TideRepublishDemandV0 {
fn bottom() -> Self {
Self::None
}
fn is_bottom(&self) -> bool {
matches!(self, Self::None)
}
fn join(&mut self, other: Self) {
*self = match (std::mem::replace(self, Self::None), other) {
(Self::All, _) | (_, Self::All) => Self::All,
(Self::None, other) => other,
(current, Self::None) => current,
(Self::Cone(mut seeds), Self::Cone(more)) => {
seeds.extend(more);
Self::Cone(seeds)
}
};
}
}
#[derive(Debug, Clone, Copy)]
pub struct TideGateInputsV0 {
pub frontier_passed: bool,
pub idle: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct TideLaneConfigV0 {
pub aging_bound_ticks: u64,
}
#[derive(Debug, PartialEq, Eq)]
pub struct TideFlushV0<D> {
pub generation: u64,
pub demand: D,
}
#[derive(Debug)]
struct TideInFlightV0<D> {
demand: D,
oldest_deposit_tick: Option<u64>,
}
#[derive(Debug)]
pub struct TideLaneV0<D: TideDemandJoinV0> {
demand: D,
in_flight: Option<TideInFlightV0<D>>,
generation: u64,
oldest_deposit_tick: Option<u64>,
starvation_alarmed: bool,
starvation_alarm_count: u64,
}
impl<D: TideDemandJoinV0> Default for TideLaneV0<D> {
fn default() -> Self {
Self {
demand: D::bottom(),
in_flight: None,
generation: 0,
oldest_deposit_tick: None,
starvation_alarmed: false,
starvation_alarm_count: 0,
}
}
}
impl<D: TideDemandJoinV0> TideLaneV0<D> {
pub fn deposit(&mut self, demand: D, now_tick: u64) -> bool {
if demand.is_bottom() {
return false;
}
if self.oldest_deposit_tick.is_none() {
self.oldest_deposit_tick = Some(now_tick);
}
let before = self.demand.clone();
self.demand.join(demand);
self.demand != before
}
pub fn has_demand(&self) -> bool {
!self.demand.is_bottom()
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn in_flight(&self) -> bool {
self.in_flight.is_some()
}
pub fn in_flight_demand(&self) -> Option<&D> {
self.in_flight.as_ref().map(|in_flight| &in_flight.demand)
}
pub fn starvation_alarm_count(&self) -> u64 {
self.starvation_alarm_count
}
pub fn oldest_deposit_age_ticks(&self, now_tick: u64) -> Option<u64> {
self.oldest_deposit_tick
.map(|oldest| now_tick.saturating_sub(oldest))
}
pub fn try_flush(
&mut self,
inputs: TideGateInputsV0,
now_tick: u64,
config: &TideLaneConfigV0,
) -> Option<TideFlushV0<D>> {
if self.in_flight.is_some() || self.demand.is_bottom() {
return None;
}
let aged = self
.oldest_deposit_tick
.is_some_and(|oldest| now_tick.saturating_sub(oldest) >= config.aging_bound_ticks);
if !inputs.frontier_passed {
if aged && !self.starvation_alarmed {
self.starvation_alarmed = true;
self.starvation_alarm_count = self.starvation_alarm_count.saturating_add(1);
}
return None;
}
if !inputs.idle && !aged {
return None;
}
self.generation = self.generation.saturating_add(1);
self.starvation_alarmed = false;
let demand = std::mem::replace(&mut self.demand, D::bottom());
self.in_flight = Some(TideInFlightV0 {
demand: demand.clone(),
oldest_deposit_tick: self.oldest_deposit_tick.take(),
});
Some(TideFlushV0 {
generation: self.generation,
demand,
})
}
pub fn reopen_window(&mut self) -> u64 {
if let Some(disowned) = self.in_flight.take() {
self.generation = self.generation.saturating_add(1);
self.demand.join(disowned.demand);
self.oldest_deposit_tick =
match (self.oldest_deposit_tick, disowned.oldest_deposit_tick) {
(Some(current), Some(carried)) => Some(current.min(carried)),
(tick, None) | (None, tick) => tick,
};
}
self.generation
}
pub fn tide_completed(&mut self, generation: u64) {
if generation == self.generation {
self.in_flight = None;
}
}
}