mod cell;
use crate::cell::CloneCell;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, RwLock, RwLockWriteGuard};
pub struct SnapshotLock<T> {
inner: RwLock<(T, usize)>,
latest: CloneCell<Arc<T>>,
latest_version: AtomicUsize,
}
unsafe impl<T: Send> Send for SnapshotLock<T> {}
unsafe impl<T: Send> Sync for SnapshotLock<T> {}
impl<T: Clone> SnapshotLock<T> {
pub fn new(inner: T) -> Self {
Self {
inner: RwLock::new((inner.clone(), 0)),
latest: CloneCell::new(Arc::new(inner)),
latest_version: AtomicUsize::new(0),
}
}
pub fn write(&self) -> SnapshotLockWriteGuard<'_, T> {
let mut guard = self.inner.write().unwrap();
guard.1 += 1; SnapshotLockWriteGuard(guard)
}
pub fn read(&self) -> SnapshotLockReadGuard<T> {
if let Ok(read) = self.inner.try_read() {
let (data, version) = read.deref();
if self.latest_version.swap(*version, Ordering::Acquire) < *version {
self.latest.set(Arc::new(data.clone()));
}
}
SnapshotLockReadGuard(self.latest.get())
}
}
pub struct SnapshotLockReadGuard<T>(pub Arc<T>);
impl<T> Deref for SnapshotLockReadGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
pub struct SnapshotLockWriteGuard<'a, T>(pub RwLockWriteGuard<'a, (T, usize)>);
impl<'a, T> Deref for SnapshotLockWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let (value, _) = self.0.deref();
value
}
}
impl<'a, T> DerefMut for SnapshotLockWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
let (value, _) = self.0.deref_mut();
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lock_should_not_ub_on_read_after_write_drop() {
let lock = SnapshotLock::new(String::from("Hello"));
let mut writer = lock.write();
writer.push_str(", World!");
let reader = lock.read();
assert_eq!(*reader, "Hello", "Should read under write lock");
drop(writer);
assert_eq!(
*reader, "Hello",
"Should read old data after the drop of write lock"
);
}
}