use std::cell::UnsafeCell;
use vasi::VirtualAddressSpaceIndependent;
use super::{Root, Tag};
#[derive(Debug, VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct RootedCell<T> {
tag: Tag,
val: UnsafeCell<T>,
}
impl<T> RootedCell<T> {
#[inline]
pub fn new(root: &Root, val: T) -> Self {
Self {
tag: root.tag(),
val: UnsafeCell::new(val),
}
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.val.get() }
}
#[inline]
pub fn set(&self, root: &Root, val: T) {
drop(self.replace(root, val))
}
#[inline]
pub fn replace(&self, root: &Root, val: T) -> T {
assert_eq!(
root.tag, self.tag,
"Expected {:?} Got {:?}",
self.tag, root.tag
);
unsafe { self.val.get().replace(val) }
}
#[inline]
pub fn into_inner(self) -> T {
self.val.into_inner()
}
}
impl<T: Copy> RootedCell<T> {
#[inline]
pub fn get(&self, root: &Root) -> T {
assert_eq!(
root.tag, self.tag,
"Expected {:?} Got {:?}",
self.tag, root.tag
);
unsafe { *self.val.get() }
}
}
unsafe impl<T: Send> Send for RootedCell<T> where T: Copy {}
unsafe impl<T: Send> Sync for RootedCell<T> where T: Copy {}
#[cfg(test)]
mod test_rooted_cell {
use std::thread;
use super::*;
use crate::explicit_drop::ExplicitDrop;
use crate::rootedcell::rc::RootedRc;
#[test]
fn get() {
let root = Root::new();
let c = RootedCell::new(&root, 1);
assert_eq!(c.get(&root), 1);
}
#[test]
fn get_mut() {
let root = Root::new();
let mut c = RootedCell::new(&root, 1);
assert_eq!(*c.get_mut(), 1);
}
#[test]
fn set() {
let root = Root::new();
let c = RootedCell::new(&root, 1);
c.set(&root, 2);
assert_eq!(c.get(&root), 2);
}
#[test]
fn replace() {
let root = Root::new();
let c = RootedCell::new(&root, 1);
let old = c.replace(&root, 2);
assert_eq!(old, 1);
assert_eq!(c.get(&root), 2);
}
#[test]
fn share_with_worker_thread() {
let root = Root::new();
let rc = RootedRc::new(&root, RootedCell::new(&root, 0));
let root = {
let rc = { rc.clone(&root) };
thread::spawn(move || {
rc.set(&root, 3);
rc.explicit_drop(&root);
root
})
.join()
.unwrap()
};
assert_eq!(rc.get(&root), 3);
rc.explicit_drop(&root);
}
#[test]
fn worker_thread_get_mut() {
let root = Root::new();
let cell = RootedCell::new(&root, 0);
let cell = {
thread::spawn(move || {
let mut cell = cell;
*cell.get_mut() = 3;
cell
})
.join()
.unwrap()
};
assert_eq!(cell.get(&root), 3);
}
}