use alloc::{
rc::{Rc, Weak},
vec::Vec,
};
use core::{fmt, mem};
use crate::barrier::unlock;
use crate::lock::RefLock;
use crate::{Collect, Gc, Mutation, Root, Rootable};
#[derive(Copy, Clone)]
pub struct DynamicRootSet<'gc>(Gc<'gc, Inner<'gc>>);
unsafe impl<'gc> Collect for DynamicRootSet<'gc> {
fn trace(&self, cc: &crate::Collection) {
self.0.trace(cc);
}
}
impl<'gc> DynamicRootSet<'gc> {
pub fn new(mc: &Mutation<'gc>) -> Self {
DynamicRootSet(Gc::new(
mc,
Inner {
handles: RefLock::new(Vec::new()),
set_id: Rc::new(SetId {}),
},
))
}
pub fn stash<R: for<'a> Rootable<'a>>(
&self,
mc: &Mutation<'gc>,
root: Root<'gc, R>,
) -> DynamicRoot<R> {
let handle = Rc::new(Handle {
set_id: self.0.set_id.clone(),
root,
});
let weak_handle = Rc::<Handle<Root<'gc, R>>>::downgrade(&handle);
let handles = unlock!(Gc::write(mc, self.0), Inner, handles);
handles.borrow_mut().push(weak_handle);
DynamicRoot {
handle: unsafe {
mem::transmute::<Rc<Handle<Root<'gc, R>>>, Rc<Handle<Root<'static, R>>>>(handle)
},
}
}
#[inline]
pub fn fetch<'a, R: for<'r> Rootable<'r>>(&self, root: &'a DynamicRoot<R>) -> &'a Root<'gc, R> {
if self.contains(root) {
unsafe { &*root.as_ptr() }
} else {
panic!("mismatched root set")
}
}
#[inline]
pub fn try_fetch<'a, R: for<'r> Rootable<'r>>(
&self,
root: &'a DynamicRoot<R>,
) -> Result<&'a Root<'gc, R>, MismatchedRootSet> {
if self.contains(root) {
unsafe { Ok(&*root.as_ptr()) }
} else {
Err(MismatchedRootSet(()))
}
}
#[inline]
pub fn contains<R: for<'r> Rootable<'r>>(&self, root: &DynamicRoot<R>) -> bool {
let ours = Rc::as_ptr(&self.0.set_id);
let theirs = Rc::as_ptr(&root.handle.set_id);
ours == theirs
}
}
pub struct DynamicRoot<R: for<'gc> Rootable<'gc>> {
handle: Rc<Handle<Root<'static, R>>>,
}
impl<R: for<'gc> Rootable<'gc>> Clone for DynamicRoot<R> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
}
}
}
impl<R: for<'gc> Rootable<'gc>> DynamicRoot<R> {
#[inline]
pub fn as_ptr<'gc>(&self) -> *const Root<'gc, R> {
unsafe { mem::transmute::<&Root<'static, R>, &Root<'gc, R>>(&self.handle.root) as *const _ }
}
}
#[derive(Debug)]
pub struct MismatchedRootSet(());
impl fmt::Display for MismatchedRootSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("mismatched root set")
}
}
#[cfg(feature = "std")]
impl std::error::Error for MismatchedRootSet {}
struct SetId {}
type HandleList<'gc> = Vec<Weak<Handle<dyn Collect + 'gc>>>;
struct Inner<'gc> {
handles: RefLock<HandleList<'gc>>,
set_id: Rc<SetId>,
}
unsafe impl<'gc> Collect for Inner<'gc> {
fn trace(&self, cc: &crate::Collection) {
let handles = unsafe { self.handles.as_ref_cell() };
handles.borrow_mut().retain(|handle| {
if let Some(handle) = handle.upgrade() {
handle.root.trace(cc);
true
} else {
false
}
});
}
}
struct Handle<T: ?Sized> {
set_id: Rc<SetId>,
root: T,
}