use std::sync::Arc;
use parking_lot::RwLock;
pub struct Snapshot<T> {
inner: RwLock<Arc<T>>,
}
impl<T> Snapshot<T> {
pub fn new(value: T) -> Self {
Self { inner: RwLock::new(Arc::new(value)) }
}
pub fn load(&self) -> Arc<T> {
self.inner.read().clone()
}
pub fn store(&self, value: T) {
*self.inner.write() = Arc::new(value);
}
pub fn rcu<F>(&self, f: F)
where
F: FnOnce(&T) -> T,
{
let mut g = self.inner.write();
let next = f(&g);
*g = Arc::new(next);
}
}
impl<T: Default> Default for Snapshot<T> {
fn default() -> Self {
Self::new(T::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc as StdArc;
#[test]
fn load_and_store_round_trip() {
let s = Snapshot::new(vec![1, 2, 3]);
let snap = s.load();
assert_eq!(*snap, vec![1, 2, 3]);
s.store(vec![10, 20]);
let next = s.load();
assert_eq!(*next, vec![10, 20]);
assert_eq!(*snap, vec![1, 2, 3]);
}
#[test]
fn rcu_mutates_atomically() {
let s = Snapshot::new(0u32);
for _ in 0..10 {
s.rcu(|cur| cur + 1);
}
assert_eq!(*s.load(), 10);
}
#[test]
fn many_readers_no_blocking() {
let s = StdArc::new(Snapshot::new(0u64));
let counter = StdArc::new(AtomicU32::new(0));
let mut handles = Vec::new();
for _ in 0..8 {
let s = s.clone();
let c = counter.clone();
handles.push(std::thread::spawn(move || {
for _ in 0..1000 {
let _ = s.load();
c.fetch_add(1, Ordering::Relaxed);
}
}));
}
for h in handles {
h.join().unwrap();
}
assert_eq!(counter.load(Ordering::Relaxed), 8000);
}
#[test]
fn default_constructs_via_t_default() {
let s: Snapshot<Vec<u32>> = Snapshot::default();
assert!(s.load().is_empty());
}
}