use crate::types::*;
use std::fmt;
use std::mem::ManuallyDrop;
type OpsToReplay<T> = Mutex<Vec<Box<dyn FnOnce(&mut T) + Send>>>;
type OpsToReplayGuard<'w, T> = MutexGuard<'w, Vec<Box<dyn FnOnce(&mut T) + Send>>>;
pub struct AsLock<T> {
active_table: AtomicPtr<RwLock<T>>,
standby_table: AtomicPtr<RwLock<T>>,
ops_to_replay: OpsToReplay<T>,
}
pub struct AsLockWriteGuard<'w, T> {
active_table: &'w AtomicPtr<RwLock<T>>,
standby_table: &'w AtomicPtr<RwLock<T>>,
guard: ManuallyDrop<RwLockWriteGuard<'w, T>>,
ops_to_replay: OpsToReplayGuard<'w, T>,
}
pub type AsLockReadGuard<'r, T> = RwLockReadGuard<'r, T>;
impl<T> AsLock<T> {
pub fn from_identical(t1: T, t2: T) -> AsLock<T> {
AsLock {
active_table: AtomicPtr::new(Box::into_raw(Box::new(RwLock::new(t1)))),
standby_table: AtomicPtr::new(Box::into_raw(Box::new(RwLock::new(t2)))),
ops_to_replay: Mutex::default(),
}
}
pub fn read(&self) -> AsLockReadGuard<'_, T> {
unsafe { &*self.active_table.load(Ordering::SeqCst) }.read()
}
pub fn write(&self) -> AsLockWriteGuard<'_, T> {
let mut ops_to_replay = self.ops_to_replay.lock();
let mut wg = unsafe { &*self.standby_table.load(Ordering::SeqCst) }.write();
for op in ops_to_replay.drain(..) {
op(&mut wg);
}
ops_to_replay.clear();
AsLockWriteGuard {
guard: ManuallyDrop::new(wg),
active_table: &self.active_table,
standby_table: &self.standby_table,
ops_to_replay,
}
}
}
impl<T> Drop for AsLock<T> {
fn drop(&mut self) {
unsafe {
let _active_table = Box::from_raw(self.active_table.load(Ordering::SeqCst));
let _standby_table = Box::from_raw(self.standby_table.load(Ordering::SeqCst));
}
}
}
impl<T> AsLock<T>
where
T: Clone,
{
pub fn new(t: T) -> AsLock<T> {
Self::from_identical(t.clone(), t)
}
}
impl<T> Default for AsLock<T>
where
T: Default,
{
fn default() -> AsLock<T> {
Self::from_identical(T::default(), T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for AsLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let num_ops_to_replay = self.ops_to_replay.lock().len();
f.debug_struct("AsLock")
.field("num_ops_to_replay", &num_ops_to_replay)
.field("standby_table", &*self.write())
.field("active_table", &*self.read())
.finish()
}
}
impl<'w, T> AsLockWriteGuard<'w, T> {
pub fn update_tables<'a, R>(
&'a mut self,
mut update: impl UpdateTables<'a, T, R> + 'static + Sized + Send,
) -> R {
let res = update.apply_first(&mut self.guard);
self.ops_to_replay.push(Box::new(move |table| {
update.apply_second(table);
}));
res
}
pub fn update_tables_closure<R>(
&mut self,
update: impl Fn(&mut T) -> R + 'static + Sized + Send,
) -> R {
let res = update(&mut self.guard);
self.ops_to_replay.push(Box::new(move |table| {
update(table);
}));
res
}
}
impl<'w, T> Drop for AsLockWriteGuard<'w, T> {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.guard) };
fence(Ordering::SeqCst);
let active_table = self.active_table.load(Ordering::SeqCst);
let standby_table = self.standby_table.load(Ordering::SeqCst);
assert_ne!(active_table, standby_table);
let res = self.active_table.compare_exchange(
active_table,
standby_table,
Ordering::SeqCst,
Ordering::SeqCst,
);
assert_eq!(res, Ok(active_table));
let res = self.standby_table.compare_exchange(
standby_table,
active_table,
Ordering::SeqCst,
Ordering::SeqCst,
);
assert_eq!(res, Ok(standby_table));
}
}
impl<'w, T> std::ops::Deref for AsLockWriteGuard<'w, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&**self.guard
}
}
impl<'w, T: fmt::Debug> fmt::Debug for AsLockWriteGuard<'w, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use std::ops::Deref;
f.debug_struct("AsLockWriteGuard")
.field("num_ops_to_replay", &self.ops_to_replay.len())
.field("standby_table", self.deref())
.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::thread;
struct PushVec<T> {
value: T,
}
impl<'a, T> UpdateTables<'a, Vec<T>, ()> for PushVec<T>
where
T: Clone,
{
fn apply_first(&mut self, table: &'a mut Vec<T>) {
table.push(self.value.clone());
}
fn apply_second(self, table: &mut Vec<T>) {
table.push(self.value); }
}
struct PopVec {}
impl PopVec {
fn apply<T>(&mut self, table: &mut Vec<T>) -> Option<T> {
table.pop()
}
}
impl<'a, T> UpdateTables<'a, Vec<T>, Option<T>> for PopVec {
fn apply_first(&mut self, table: &'a mut Vec<T>) -> Option<T> {
self.apply(table)
}
fn apply_second(mut self, table: &mut Vec<T>) {
(&mut self).apply(table);
}
}
struct MutableRef {}
impl<'a, T> UpdateTables<'a, Vec<T>, &'a mut T> for MutableRef {
fn apply_first(&mut self, table: &'a mut Vec<T>) -> &'a mut T {
&mut table[0]
}
fn apply_second(self, table: &mut Vec<T>) {
let _ = &mut table[0];
}
}
#[test]
fn one_write_guard() {
let writer = AsLock::<Vec<i32>>::default();
let _wg = writer.write();
}
#[test]
fn publish_update() {
let aslock = Arc::new(AsLock::<Vec<i32>>::default());
assert_eq!(aslock.read().len(), 0);
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: 2 });
assert_eq!(wg.len(), 1);
{
let aslock = Arc::clone(&aslock);
assert!(thread::spawn(move || {
assert_eq!(aslock.read().len(), 0);
})
.join()
.is_ok());
}
}
assert_eq!(*aslock.read(), vec![2]);
}
#[test]
fn update_tables_closure() {
let aslock = Arc::new(AsLock::<Vec<i32>>::default());
assert_eq!(aslock.read().len(), 0);
{
let mut wg = aslock.write();
wg.update_tables_closure(|vec| vec.push(2));
assert_eq!(wg.len(), 1);
{
let aslock = Arc::clone(&aslock);
assert!(thread::spawn(move || {
assert_eq!(aslock.read().len(), 0);
})
.join()
.is_ok());
}
}
assert_eq!(*aslock.read(), vec![2]);
}
#[test]
fn multi_apply() {
let aslock = AsLock::<Vec<i32>>::default();
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: 2 });
wg.update_tables(PushVec { value: 3 });
wg.update_tables(PushVec { value: 4 });
wg.update_tables(PopVec {});
wg.update_tables(PushVec { value: 5 });
}
assert_eq!(*aslock.read(), vec![2, 3, 5]);
}
#[test]
fn multi_publish() {
let aslock = AsLock::<Vec<Box<i32>>>::default();
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: Box::new(2) });
wg.update_tables(PushVec { value: Box::new(3) });
wg.update_tables(PopVec {});
wg.update_tables(PushVec { value: Box::new(5) });
}
assert_eq!(*aslock.read(), vec![Box::new(2), Box::new(5)]);
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: Box::new(9) });
wg.update_tables(PushVec { value: Box::new(8) });
wg.update_tables(PopVec {});
wg.update_tables(PushVec { value: Box::new(7) });
}
assert_eq!(
*aslock.read(),
vec![Box::new(2), Box::new(5), Box::new(9), Box::new(7)]
);
{
let mut wg = aslock.write();
wg.update_tables(PopVec {});
}
assert_eq!(*aslock.read(), vec![Box::new(2), Box::new(5), Box::new(9)]);
}
#[test]
fn multi_thread() {
let aslock = Arc::new(AsLock::<Vec<i32>>::default());
let aslock2 = Arc::clone(&aslock);
let handler = thread::spawn(move || {
while *aslock2.read() != vec![2, 3, 5] {
assert_ne!(*aslock2.read(), vec![2, 3, 4]);
}
let aslock3 = Arc::clone(&aslock2);
let handler = thread::spawn(move || while *aslock3.read() != vec![2, 3, 5] {});
assert!(handler.join().is_ok());
});
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: 2 });
wg.update_tables(PushVec { value: 3 });
wg.update_tables(PushVec { value: 4 });
wg.update_tables(PopVec {});
wg.update_tables(PushVec { value: 5 });
}
assert!(handler.join().is_ok());
}
#[test]
fn debug_str() {
let aslock = AsLock::<Vec<i32>>::default();
assert_eq!(
format!("{:?}", aslock),
"AsLock { num_ops_to_replay: 0, standby_table: [], active_table: [] }"
);
{
let mut wg = aslock.write();
wg.update_tables(PushVec { value: 2 });
assert_eq!(
format!("{:?}", wg),
"AsLockWriteGuard { num_ops_to_replay: 1, standby_table: [2] }"
);
}
assert_eq!(
format!("{:?}", aslock),
"AsLock { num_ops_to_replay: 1, standby_table: [2], active_table: [2] }"
);
assert_eq!(format!("{:?}", aslock.read()), "[2]");
}
}