#[cfg(any(feature = "std", feature = "exclusion-set"))]
use core::any::TypeId;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
use once_cell::sync::Lazy;
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
use std::{
collections::HashSet,
sync::{Condvar, Mutex},
};
use super::Invariant;
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
static SINGLETON_CHECK: Lazy<Mutex<HashSet<TypeId>>> = Lazy::new(|| Mutex::new(HashSet::new()));
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
static SINGLETON_CHECK_CONDVAR: Lazy<Condvar> = Lazy::new(Condvar::new);
#[cfg(feature = "exclusion-set")]
static SINGLETON_CHECK_SET: exclusion_set::Set<TypeId> = exclusion_set::Set::new();
pub struct TCellOwner<Q: 'static> {
typ: PhantomData<Invariant<Q>>,
}
impl<Q: 'static> Drop for TCellOwner<Q> {
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
fn drop(&mut self) {
SINGLETON_CHECK.lock().unwrap().remove(&TypeId::of::<Q>());
SINGLETON_CHECK_CONDVAR.notify_all();
}
#[cfg(feature = "exclusion-set")]
fn drop(&mut self) {
unsafe {
SINGLETON_CHECK_SET.remove(&TypeId::of::<Q>());
}
}
#[cfg(not(any(feature = "std", feature = "exclusion-set")))]
fn drop(&mut self) {
unreachable!()
}
}
#[cfg(any(feature = "std", feature = "exclusion-set"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "exclusion-set"))))]
impl<Q: 'static> Default for TCellOwner<Q> {
fn default() -> Self {
TCellOwner::new()
}
}
impl<Q: 'static> TCellOwner<Q> {
#[cfg_attr(feature = "std", doc = "[`TCellOwner::wait_for_new`]")]
#[cfg_attr(not(feature = "std"), doc = "`TCellOwner::wait_for_new`")]
#[cfg(any(feature = "std", feature = "exclusion-set"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "exclusion-set"))))]
pub fn new() -> Self {
if let Some(owner) = TCellOwner::try_new() {
owner
} else {
panic!("Illegal to create two TCellOwner instances with the same marker type parameter")
}
}
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
pub fn try_new() -> Option<Self> {
if SINGLETON_CHECK.lock().unwrap().insert(TypeId::of::<Q>()) {
Some(Self { typ: PhantomData })
} else {
None
}
}
#[cfg(feature = "exclusion-set")]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "exclusion-set"))))]
pub fn try_new() -> Option<Self> {
if SINGLETON_CHECK_SET.try_insert(TypeId::of::<Q>()) {
Some(Self { typ: PhantomData })
} else {
None
}
}
#[cfg(all(feature = "std", not(feature = "exclusion-set")))]
pub fn wait_for_new() -> Self {
let hashset_guard = SINGLETON_CHECK.lock().unwrap();
let mut hashset_guard = SINGLETON_CHECK_CONDVAR
.wait_while(hashset_guard, |hashset| {
hashset.contains(&TypeId::of::<Q>())
})
.unwrap();
let inserted = hashset_guard.insert(TypeId::of::<Q>());
assert!(inserted);
Self { typ: PhantomData }
}
#[cfg(all(feature = "std", feature = "exclusion-set"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "std", all(feature = "exclusion-set", feature = "std"))))
)]
pub fn wait_for_new() -> Self {
SINGLETON_CHECK_SET.wait_to_insert(TypeId::of::<Q>());
Self { typ: PhantomData }
}
pub fn cell<T>(&self, value: T) -> TCell<Q, T> {
TCell::<Q, T>::new(value)
}
#[inline]
pub fn ro<'a, T: ?Sized>(&'a self, tc: &'a TCell<Q, T>) -> &'a T {
unsafe { &*tc.value.get() }
}
#[inline]
pub fn rw<'a, T: ?Sized>(&'a mut self, tc: &'a TCell<Q, T>) -> &'a mut T {
unsafe { &mut *tc.value.get() }
}
#[inline]
pub fn rw2<'a, T: ?Sized, U: ?Sized>(
&'a mut self,
tc1: &'a TCell<Q, T>,
tc2: &'a TCell<Q, U>,
) -> (&'a mut T, &'a mut U) {
assert!(
!core::ptr::eq(tc1 as *const _ as *const (), tc2 as *const _ as *const ()),
"Illegal to borrow same TCell twice with rw2()"
);
unsafe { (&mut *tc1.value.get(), &mut *tc2.value.get()) }
}
#[inline]
pub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>(
&'a mut self,
tc1: &'a TCell<Q, T>,
tc2: &'a TCell<Q, U>,
tc3: &'a TCell<Q, V>,
) -> (&'a mut T, &'a mut U, &'a mut V) {
assert!(
!core::ptr::eq(tc1 as *const _ as *const (), tc2 as *const _ as *const ())
&& !core::ptr::eq(tc2 as *const _ as *const (), tc3 as *const _ as *const ())
&& !core::ptr::eq(tc3 as *const _ as *const (), tc1 as *const _ as *const ()),
"Illegal to borrow same TCell twice with rw3()"
);
unsafe {
(
&mut *tc1.value.get(),
&mut *tc2.value.get(),
&mut *tc3.value.get(),
)
}
}
}
#[repr(transparent)]
pub struct TCell<Q, T: ?Sized> {
owner: PhantomData<Invariant<Q>>,
value: UnsafeCell<T>,
}
impl<Q, T> TCell<Q, T> {
#[inline]
pub const fn new(value: T) -> TCell<Q, T> {
TCell {
owner: PhantomData,
value: UnsafeCell::new(value),
}
}
#[inline]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
impl<Q, T: ?Sized> TCell<Q, T> {
#[inline]
pub fn ro<'a>(&'a self, owner: &'a TCellOwner<Q>) -> &'a T {
owner.ro(self)
}
#[inline]
pub fn rw<'a>(&'a self, owner: &'a mut TCellOwner<Q>) -> &'a mut T {
owner.rw(self)
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
}
impl<Q: 'static, T: Default> Default for TCell<Q, T> {
fn default() -> Self {
TCell::new(T::default())
}
}
unsafe impl<Q, T: Send + Sync + ?Sized> Sync for TCell<Q, T> {}
#[cfg(all(test, any(feature = "std", feature = "exclusion-set")))]
mod tests {
use super::{TCell, TCellOwner};
#[test]
#[should_panic]
fn tcell_singleton_1() {
struct Marker;
let _owner1 = TCellOwner::<Marker>::new();
let _owner2 = TCellOwner::<Marker>::new(); }
#[test]
fn tcell_singleton_2() {
struct Marker;
let owner1 = TCellOwner::<Marker>::new();
drop(owner1);
let _owner2 = TCellOwner::<Marker>::new();
}
#[test]
fn tcell_singleton_3() {
struct Marker1;
struct Marker2;
let _owner1 = TCellOwner::<Marker1>::new();
let _owner2 = TCellOwner::<Marker2>::new();
}
#[test]
fn tcell_singleton_try_new() {
struct Marker;
let owner1 = TCellOwner::<Marker>::try_new();
assert!(owner1.is_some());
let owner2 = TCellOwner::<Marker>::try_new();
assert!(owner2.is_none());
}
#[test]
fn tcell() {
struct Marker;
type ACellOwner = TCellOwner<Marker>;
type ACell<T> = TCell<Marker, T>;
let mut owner = ACellOwner::new();
let c1 = ACell::new(100u32);
let c2 = owner.cell(200u32);
(*owner.rw(&c1)) += 1;
(*owner.rw(&c2)) += 2;
let c1ref = owner.ro(&c1);
let c2ref = owner.ro(&c2);
let total = *c1ref + *c2ref;
assert_eq!(total, 303);
}
#[test]
#[should_panic]
fn tcell_threads() {
struct Marker;
type ACellOwner = TCellOwner<Marker>;
let (tx, rx) = std::sync::mpsc::sync_channel(0);
std::thread::spawn(move || {
let mut _owner = ACellOwner::new();
tx.send(()).unwrap();
let _ = tx.send(());
});
rx.recv().unwrap();
let mut _owner = ACellOwner::new(); let _ = rx.recv();
}
#[cfg(feature = "std")]
#[test]
fn tcell_wait_for_new_in_100_threads() {
use rand::Rng;
use std::sync::Arc;
struct Marker;
type ACellOwner = TCellOwner<Marker>;
type ACell = TCell<Marker, i32>;
let cell_arc = Arc::new(ACell::new(0));
let mut handles = vec![];
for _ in 0..100 {
let cell_arc_clone = cell_arc.clone();
let handle = std::thread::spawn(move || {
let mut rng = rand::thread_rng();
std::thread::sleep(std::time::Duration::from_millis(rng.gen_range(0..10)));
let mut owner = ACellOwner::wait_for_new();
let current_cell_val = *owner.ro(&*cell_arc_clone);
std::thread::sleep(std::time::Duration::from_millis(rng.gen_range(0..10)));
*owner.rw(&*cell_arc_clone) = current_cell_val + 1;
});
handles.push(handle);
}
for handle in handles {
assert!(handle.join().is_ok());
}
let owner = ACellOwner::wait_for_new();
assert_eq!(*owner.ro(&*cell_arc), 100);
}
#[cfg(feature = "std")]
#[test]
fn tcell_wait_for_new_timeout() {
fn assert_time_out<F>(d: std::time::Duration, f: F)
where
F: FnOnce(),
F: Send + 'static,
{
let (done_tx, done_rx) = std::sync::mpsc::channel();
let _handle = std::thread::spawn(move || {
let val = f();
done_tx.send(()).unwrap();
val
});
assert!(
done_rx.recv_timeout(d).is_err(),
"ACellOwner::wait_for_new completed (but it shouldn't have)"
);
}
assert_time_out(std::time::Duration::from_millis(1000), || {
struct Marker;
type ACellOwner = TCellOwner<Marker>;
let _owner1 = ACellOwner::new();
let _owner2 = ACellOwner::wait_for_new();
});
}
#[test]
fn tcell_get_mut() {
struct Marker;
type ACellOwner = TCellOwner<Marker>;
type ACell<T> = TCell<Marker, T>;
let owner = ACellOwner::new();
let mut cell = ACell::new(100u32);
let mut_ref = cell.get_mut();
*mut_ref = 50;
let cell_ref = owner.ro(&cell);
assert_eq!(*cell_ref, 50);
}
#[test]
fn tcell_into_inner() {
struct Marker;
type ACell<T> = TCell<Marker, T>;
let cell = ACell::new(100u32);
assert_eq!(cell.into_inner(), 100);
}
#[test]
fn tcell_unsized() {
struct Marker;
type ACellOwner = TCellOwner<Marker>;
type ACell<T> = TCell<Marker, T>;
let mut owner = ACellOwner::new();
struct Squares(u32);
struct Integers(u64);
trait Series {
fn step(&mut self);
fn value(&self) -> u64;
}
impl Series for Squares {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
(self.0 as u64) * (self.0 as u64)
}
}
impl Series for Integers {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
self.0
}
}
fn series(init: u32, is_squares: bool) -> Box<ACell<dyn Series>> {
if is_squares {
Box::new(ACell::new(Squares(init)))
} else {
Box::new(ACell::new(Integers(init as u64)))
}
}
let own = &mut owner;
let cell1 = series(4, false);
let cell2 = series(7, true);
let cell3 = series(3, true);
assert_eq!(cell1.ro(own).value(), 4);
cell1.rw(own).step();
assert_eq!(cell1.ro(own).value(), 5);
assert_eq!(own.ro(&cell2).value(), 49);
own.rw(&cell2).step();
assert_eq!(own.ro(&cell2).value(), 64);
let (r1, r2, r3) = own.rw3(&cell1, &cell2, &cell3);
r1.step();
r2.step();
r3.step();
assert_eq!(cell1.ro(own).value(), 6);
assert_eq!(cell2.ro(own).value(), 81);
assert_eq!(cell3.ro(own).value(), 16);
let (r1, r2) = own.rw2(&cell1, &cell2);
r1.step();
r2.step();
assert_eq!(cell1.ro(own).value(), 7);
assert_eq!(cell2.ro(own).value(), 100);
}
}