use core::ops::{Deref, DerefMut};
#[cfg(feature = "std")]
type Inner<T> = std::sync::RwLock<T>;
#[cfg(not(feature = "std"))]
type Inner<T> = core::cell::RefCell<T>;
#[derive(Debug, Default)]
pub struct Stem<T>(Inner<T>);
impl<T> Stem<T> {
pub const fn new(t: T) -> Self {
Self(Inner::new(t))
}
pub fn map_ref<F, R>(&self, f: F) -> R
where F: for<'a> FnMut(&'a T) -> R
{
self.0.map_ref(f)
}
pub fn map_mut<F, R>(&self, f: F) -> R
where F: for<'a> FnMut(&'a mut T) -> R
{
self.0.map_mut(f)
}
}
pub trait StemCellBehavior<T> {
fn new(t: T) -> Self
where Self: Sized;
fn map_ref<F, R>(&self, f: F) -> R
where F: for<'a> FnMut(&'a T) -> R;
fn map_mut<F, R>(&self, f: F) -> R
where F: for<'a> FnMut(&'a mut T) -> R;
}
#[cfg(feature = "std")]
impl<T> StemCellBehavior<T> for std::sync::RwLock<T> {
fn new(t: T) -> Self {
Self::new(t)
}
fn map_ref<F, R>(&self, mut f: F) -> R
where F: for<'a> FnMut(&'a T) -> R
{
f(self.read().unwrap().deref())
}
fn map_mut<F, R>(&self, mut f: F) -> R
where F: for<'a> FnMut(&'a mut T) -> R
{
f(self.write().unwrap().deref_mut())
}
}
impl<T> StemCellBehavior<T> for core::cell::RefCell<T> {
fn new(t: T) -> Self {
Self::new(t)
}
fn map_ref<F, R>(&self, mut f: F) -> R
where F: for<'a> FnMut(&'a T) -> R
{
f(self.borrow().deref())
}
fn map_mut<F, R>(&self, mut f: F) -> R
where F: for<'a> FnMut(&'a mut T) -> R
{
f(self.borrow_mut().deref_mut())
}
}
#[cfg(test)]
mod test {
use core::cell::RefCell;
use std::sync::{Arc, Barrier, RwLock};
use super::*;
#[test]
fn refcell_modify() {
let s = RefCell::new(Vec::<usize>::new());
s.map_mut(|v| v.push(12));
s.map_ref(|v| assert_eq!(v, &vec![12usize]));
}
#[test]
fn refcell_concurrent_read_does_not_panic() {
let s = RefCell::new(Vec::<usize>::new());
s.map_ref(|_| s.map_ref(|_| ()));
}
#[test]
fn rwlock_modify() {
let s = RwLock::new(Vec::<usize>::new());
s.map_mut(|v| v.push(12));
s.map_ref(|v| assert_eq!(v, &vec![12usize]));
}
#[test]
fn rwlock_concurrent_read_does_not_panic() {
let s = RwLock::new(Vec::<usize>::new());
s.map_ref(|_| s.map_ref(|_| ()));
}
#[test]
fn stem_modify_blocks_until_refs_dropped() {
unsafe {
static VEC: Stem<Vec<usize>> = Stem::new(Vec::new());
static mut START: Option<Arc<Barrier>> = None;
static mut READING: Option<Arc<Barrier>> = None;
static mut READING_DONE: Option<Arc<Barrier>> = None;
static mut MODIFY_DONE: Option<Arc<Barrier>> = None;
START = Some(Arc::new(Barrier::new(3)));
READING = Some(Arc::new(Barrier::new(3)));
READING_DONE = Some(Arc::new(Barrier::new(2)));
MODIFY_DONE = Some(Arc::new(Barrier::new(3)));
macro_rules! wait {
($b:ident) => {
$b.as_ref().unwrap().clone().wait();
};
}
std::thread::spawn(|| {
VEC.map_ref(|v| {
wait!(START);
assert!(v.is_empty());
wait!(READING);
wait!(READING_DONE);
});
wait!(MODIFY_DONE);
});
std::thread::spawn(|| {
wait!(START);
wait!(READING);
VEC.map_mut(|v| v.push(12)); wait!(MODIFY_DONE);
});
wait!(START);
wait!(READING);
VEC.map_ref(|v| assert!(v.is_empty()));
wait!(READING_DONE);
wait!(MODIFY_DONE);
VEC.map_ref(|v| assert_eq!(v, &vec![12]));
}
}
}