use std::{
future::{Future, poll_fn},
pin::pin,
sync::{Mutex, OnceLock, PoisonError},
};
use elsa::sync::FrozenVec;
use super::recursion;
use crate::context::{ContextRead, Cx, RequestContext};
struct Variant<V> {
value: V,
reads: Vec<ContextRead>,
}
impl<V> Variant<V> {
fn matches(&self, context: &RequestContext) -> bool {
self.reads.iter().all(|read| read.matches(context))
}
fn reuse(&self, cx: &Cx) -> &V {
if let Some(outer) = cx.tracker() {
outer.merge(&self.reads);
}
&self.value
}
}
struct Variants<V> {
first: OnceLock<Variant<V>>,
rest: FrozenVec<Box<Variant<V>>>,
}
impl<V> Variants<V> {
fn reuse(&self, cx: &Cx) -> Option<&V> {
let first = self.first.get()?;
if first.matches(cx.request_context()) {
return Some(first.reuse(cx));
}
self.rest
.iter()
.find(|variant| variant.matches(cx.request_context()))
.map(|variant| variant.reuse(cx))
}
fn insert(&self, cx: &Cx, value: V, reads: Vec<ContextRead>) -> &V {
let variant = Variant { value, reads };
let stored = match self.first.set(variant) {
Ok(()) => self.first.get().expect("the first variant was just set"),
Err(variant) => self.rest.push_get(Box::new(variant)),
};
stored.reuse(cx)
}
}
impl<V> Default for Variants<V> {
fn default() -> Self {
Self {
first: OnceLock::new(),
rest: FrozenVec::new(),
}
}
}
pub(super) struct SyncMemoizeCell<V> {
variants: Variants<V>,
gate: Mutex<()>,
recursion: recursion::Guard,
}
impl<V> SyncMemoizeCell<V> {
pub(super) fn reuse(&self, cx: &Cx) -> Option<&V> {
self.variants.reuse(cx)
}
pub(super) fn get_or_init<Marker, I>(&self, cx: &Cx, initialize: I) -> &V
where
I: FnOnce(&Cx) -> V,
{
if let Some(value) = self.variants.reuse(cx) {
return value;
}
self.recursion.assert_not_recursive::<Marker>();
let _gate = self.gate.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(value) = self.variants.reuse(cx) {
return value;
}
let (child, tracker) = cx.track();
let value = self.recursion.scope(|| initialize(&child));
self.variants.insert(cx, value, tracker.take_reads())
}
}
impl<V> Default for SyncMemoizeCell<V> {
fn default() -> Self {
Self {
variants: Variants::default(),
gate: Mutex::new(()),
recursion: recursion::Guard::default(),
}
}
}
pub(super) struct AsyncMemoizeCell<V> {
variants: Variants<V>,
gate: tokio::sync::Mutex<()>,
recursion: recursion::Guard,
}
impl<V> AsyncMemoizeCell<V> {
pub(super) async fn get_or_init<Marker, I>(&self, cx: &Cx, initialize: I) -> &V
where
I: AsyncFnOnce(&Cx) -> V,
{
if let Some(value) = self.variants.reuse(cx) {
return value;
}
self.recursion.assert_not_recursive::<Marker>();
let _gate = self.gate.lock().await;
if let Some(value) = self.variants.reuse(cx) {
return value;
}
let (child, tracker) = cx.track();
let mut future = pin!(self.recursion.scope(|| initialize(&child)));
let value = poll_fn(|task| self.recursion.scope(|| future.as_mut().poll(task))).await;
self.variants.insert(cx, value, tracker.take_reads())
}
}
impl<V> Default for AsyncMemoizeCell<V> {
fn default() -> Self {
Self {
variants: Variants::default(),
gate: tokio::sync::Mutex::new(()),
recursion: recursion::Guard::default(),
}
}
}