#![deny(unsafe_code)]
use core::ops::Deref;
use core::sync::atomic::Ordering;
use super::ref_cnt::RefCnt;
use super::strategy::Strategy;
use super::ArcSwapAny;
pub trait Access<T> {
fn load(&mut self) -> &T;
}
#[derive(Clone, Debug)]
pub struct Cache<A, T> {
arc_swap: A,
cached: T,
}
impl<A, T, S> Cache<A, T>
where
A: Deref<Target = ArcSwapAny<T, S>>,
T: RefCnt,
S: Strategy<T>,
{
pub fn new(arc_swap: A) -> Self {
let cached = arc_swap.load_full();
Self { arc_swap, cached }
}
pub fn arc_swap(&self) -> &A::Target {
&self.arc_swap
}
#[inline]
pub fn load(&mut self) -> &T {
self.revalidate();
self.load_no_revalidate()
}
#[inline]
fn load_no_revalidate(&self) -> &T {
&self.cached
}
#[inline]
fn revalidate(&mut self) {
let cached_ptr = RefCnt::as_ptr(&self.cached);
let shared_ptr = self.arc_swap.ptr.load(Ordering::Relaxed);
if cached_ptr != shared_ptr {
self.cached = self.arc_swap.load_full();
}
}
pub fn map<F, U>(self, f: F) -> MapCache<A, T, F>
where
F: FnMut(&T) -> &U,
{
MapCache {
inner: self,
projection: f,
}
}
}
impl<A, T, S> Access<T::Target> for Cache<A, T>
where
A: Deref<Target = ArcSwapAny<T, S>>,
T: Deref<Target = <T as RefCnt>::Base> + RefCnt,
S: Strategy<T>,
{
fn load(&mut self) -> &T::Target {
self.load().deref()
}
}
impl<A, T, S> From<A> for Cache<A, T>
where
A: Deref<Target = ArcSwapAny<T, S>>,
T: RefCnt,
S: Strategy<T>,
{
fn from(arc_swap: A) -> Self {
Self::new(arc_swap)
}
}
#[derive(Clone, Debug)]
pub struct MapCache<A, T, F> {
inner: Cache<A, T>,
projection: F,
}
impl<A, T, S, F, U> Access<U> for MapCache<A, T, F>
where
A: Deref<Target = ArcSwapAny<T, S>>,
T: RefCnt,
S: Strategy<T>,
F: FnMut(&T) -> &U,
{
fn load(&mut self) -> &U {
(self.projection)(self.inner.load())
}
}
#[cfg(test)]
mod tests {
use alloc::sync::Arc;
use super::*;
use crate::{ArcSwap, ArcSwapOption};
#[test]
fn cached_value() {
let a = ArcSwap::from_pointee(42);
let mut c1 = Cache::new(&a);
let mut c2 = Cache::new(&a);
assert_eq!(42, **c1.load());
assert_eq!(42, **c2.load());
a.store(Arc::new(43));
assert_eq!(42, **c1.load_no_revalidate());
assert_eq!(43, **c1.load());
}
#[test]
fn cached_through_arc() {
let a = Arc::new(ArcSwap::from_pointee(42));
let mut c = Cache::new(Arc::clone(&a));
assert_eq!(42, **c.load());
a.store(Arc::new(0));
drop(a); }
#[test]
fn cache_option() {
let a = ArcSwapOption::from_pointee(42);
let mut c = Cache::new(&a);
assert_eq!(42, **c.load().as_ref().unwrap());
a.store(None);
assert!(c.load().is_none());
}
struct Inner {
answer: usize,
}
struct Outer {
inner: Inner,
}
#[test]
fn map_cache() {
let a = ArcSwap::from_pointee(Outer {
inner: Inner { answer: 42 },
});
let mut cache = Cache::new(&a);
let mut inner = cache.clone().map(|outer| &outer.inner);
let mut answer = cache.clone().map(|outer| &outer.inner.answer);
assert_eq!(42, cache.load().inner.answer);
assert_eq!(42, inner.load().answer);
assert_eq!(42, *answer.load());
a.store(Arc::new(Outer {
inner: Inner { answer: 24 },
}));
assert_eq!(24, cache.load().inner.answer);
assert_eq!(24, inner.load().answer);
assert_eq!(24, *answer.load());
}
}