#![doc = include_str!("../examples/write_and_read_thread.rs")]
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
use std::sync::atomic::{fence, AtomicPtr, AtomicUsize};
use std::sync::{Arc, Mutex};
#[cfg(test)]
mod tests;
pub struct SyncCow<T: Clone> {
write_lock: Mutex<()>,
latest: AtomicUsize,
atomic_red: (AtomicPtr<Arc<T>>, AtomicUsize),
atomic_green: (AtomicPtr<Arc<T>>, AtomicUsize),
}
const RED: usize = 0;
const GREEN: usize = 1;
impl<T: Clone> SyncCow<T> {
pub fn edit<F>(&self, edit_fn: F)
where
F: FnOnce(&mut T),
{
let _lck = self.write_lock.lock().unwrap();
let latest = self.latest.load(Relaxed);
let ((old_ptr, old_cnt), latest_ptr) = match latest {
RED => (&self.atomic_green, &self.atomic_red.0),
GREEN => (&self.atomic_red, &self.atomic_green.0),
_ => panic!("Latest does not exist. This should never happen."),
};
let load_ptr = latest_ptr.load(Relaxed);
let obj = unsafe { &*load_ptr };
let mut cloned = Box::new(Arc::new(obj.as_ref().clone()));
edit_fn(Arc::get_mut(cloned.as_mut()).unwrap());
let new_ptr = Box::into_raw(cloned);
let old_ptr = old_ptr.swap(new_ptr, Release);
fence(SeqCst);
while old_cnt.load(Relaxed) != 0 {
std::thread::yield_now();
}
fence(Acquire);
self.latest.store((latest + 1) % 2, Release);
let _ = unsafe { Box::from_raw(old_ptr) };
}
pub fn read(&self) -> Arc<T> {
let latest = self.latest.load(Acquire);
let (ptr, cnt) = match latest {
RED => &self.atomic_red,
GREEN => &self.atomic_green,
_ => panic!("Latest does not exist. This should never happen."),
};
cnt.fetch_add(1, Relaxed);
fence(SeqCst);
let arc = unsafe { &*ptr.load(Acquire) }.clone();
cnt.fetch_sub(1, Release);
arc
}
pub fn new(obj: T) -> SyncCow<T> {
let red = Box::new(Arc::new(obj.clone()));
let green = Box::new(Arc::new(obj.clone()));
SyncCow {
latest: AtomicUsize::new(0),
write_lock: Mutex::new(()),
atomic_red: (AtomicPtr::new(Box::into_raw(red)), AtomicUsize::new(0)),
atomic_green: (AtomicPtr::new(Box::into_raw(green)), AtomicUsize::new(0)),
}
}
}
impl<T: Clone> Drop for SyncCow<T> {
fn drop(&mut self) {
let _ = unsafe { Box::from_raw(self.atomic_red.0.load(Relaxed)) };
let _ = unsafe { Box::from_raw(self.atomic_green.0.load(Relaxed)) };
}
}