penumbra_dex/component/router/
path_cache.rsuse std::{collections::BTreeMap, sync::Arc};
use cnidarium::{StateDelta, StateRead};
use parking_lot::Mutex;
use penumbra_asset::asset;
use super::Path;
pub(super) struct PathEntry<S: StateRead + 'static> {
pub path: Path<S>,
pub active: bool,
pub spill: Option<Path<S>>,
}
impl<S: StateRead + 'static> PathEntry<S> {
pub fn update(&mut self, new_path: Path<S>) {
if new_path < self.path {
tracing::debug!(new_price = %new_path.price, old_price = %self.path.price, "new path is better than best path, updating cache");
self.spill = Some(std::mem::replace(&mut self.path, new_path));
self.active = true;
} else {
self.update_spill(new_path);
}
}
fn update_spill(&mut self, new_path: Path<S>) {
match &self.spill {
Some(spill) if new_path.price < spill.price => {
tracing::debug!(new_spill_price = %new_path.price, old_spill_price = %spill.price, "new path is better than spill path, updating cache");
self.spill = Some(new_path);
self.active = true;
}
Some(spill) => {
tracing::debug!(new_spill_price = %new_path.price, old_spill_price = %spill.price, "new path is worse than spill path, ignore");
}
None => {
tracing::debug!(new_spill_price = %new_path.price, "new path is a suitable spill path, updating cache");
self.spill = Some(new_path);
self.active = true;
}
}
}
}
impl<S: StateRead + 'static> From<Path<S>> for PathEntry<S> {
fn from(path: Path<S>) -> Self {
Self {
path,
active: true,
spill: None,
}
}
}
pub(super) struct PathCache<S: StateRead + 'static>(pub(super) BTreeMap<asset::Id, PathEntry<S>>);
pub(super) type SharedPathCache<S> = Arc<Mutex<PathCache<S>>>;
impl<S: StateRead + 'static> PathCache<S> {
pub fn begin(start: asset::Id, state: StateDelta<S>) -> SharedPathCache<S> {
let mut cache = BTreeMap::new();
cache.insert(
start,
PathEntry {
path: Path::begin(start, state),
active: true,
spill: None,
},
);
Arc::new(Mutex::new(Self(cache)))
}
pub fn consider(&mut self, path: Path<S>) {
use std::collections::btree_map::Entry;
let span = path.span.clone();
span.in_scope(|| match self.0.entry(*path.end()) {
Entry::Occupied(mut entry) => {
entry.get_mut().update(path);
}
Entry::Vacant(entry) => {
tracing::debug!("inserting new path");
entry.insert(path.into());
}
})
}
pub fn extract_active(&mut self) -> Vec<Path<S>> {
self.0
.iter_mut()
.filter_map(|(_, entry)| {
if entry.active {
entry.active = false;
Some(entry.path.fork())
} else {
None
}
})
.collect()
}
}