use crate::{
ExternRefHostDataId, ExternRefHostDataTable, SendSyncPtr, VMExternRef, VMGcHeader, VMGcRef,
};
use anyhow::Result;
use std::{any::Any, num::NonZeroUsize};
pub unsafe trait GcRuntime: 'static + Send + Sync {
fn new_gc_heap(&self) -> Result<Box<dyn GcHeap>>;
}
pub unsafe trait GcHeap: 'static + Send + Sync {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn enter_no_gc_scope(&mut self);
fn exit_no_gc_scope(&mut self);
fn header(&self, gc_ref: &VMGcRef) -> &VMGcHeader;
fn clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef;
fn drop_gc_ref(&mut self, host_data_table: &mut ExternRefHostDataTable, gc_ref: VMGcRef) {
let mut dest = Some(gc_ref);
self.write_gc_ref(host_data_table, &mut dest, None);
}
fn write_gc_ref(
&mut self,
host_data_table: &mut ExternRefHostDataTable,
destination: &mut Option<VMGcRef>,
source: Option<&VMGcRef>,
);
fn expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef);
fn need_gc_before_entering_wasm(&self, num_gc_refs: NonZeroUsize) -> bool;
fn alloc_externref(&mut self, host_data: ExternRefHostDataId) -> Result<Option<VMExternRef>>;
fn externref_host_data(&self, externref: &VMExternRef) -> ExternRefHostDataId;
fn gc<'a>(
&'a mut self,
roots: GcRootsIter<'a>,
host_data_table: &'a mut ExternRefHostDataTable,
) -> Box<dyn GarbageCollection<'a> + 'a>;
unsafe fn vmctx_gc_heap_base(&self) -> *mut u8;
unsafe fn vmctx_gc_heap_bound(&self) -> usize;
unsafe fn vmctx_gc_heap_data(&self) -> *mut u8;
#[cfg(feature = "pooling-allocator")]
fn reset(&mut self);
}
#[derive(Default)]
pub struct GcRootsList(Vec<RawGcRoot>);
#[derive(Clone, Copy)]
enum RawGcRoot {
Stack(SendSyncPtr<u64>),
NonStack(SendSyncPtr<VMGcRef>),
}
impl GcRootsList {
#[inline]
pub unsafe fn add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr<u64>) {
log::trace!(
"Adding Wasm stack root: {:#p}",
VMGcRef::from_r64(*ptr_to_root.as_ref()).unwrap().unwrap()
);
self.0.push(RawGcRoot::Stack(ptr_to_root));
}
#[inline]
pub unsafe fn add_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>) {
log::trace!(
"Adding non-stack root: {:#p}",
ptr_to_root.as_ref().unchecked_copy()
);
self.0.push(RawGcRoot::NonStack(ptr_to_root))
}
#[inline]
pub unsafe fn iter<'a>(&'a mut self) -> GcRootsIter<'a> {
GcRootsIter {
list: self,
index: 0,
}
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
}
pub struct GcRootsIter<'a> {
list: &'a mut GcRootsList,
index: usize,
}
impl<'a> Iterator for GcRootsIter<'a> {
type Item = GcRoot<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let root = GcRoot {
raw: self.list.0.get(self.index).copied()?,
_phantom: std::marker::PhantomData,
};
self.index += 1;
Some(root)
}
}
pub struct GcRoot<'a> {
raw: RawGcRoot,
_phantom: std::marker::PhantomData<&'a mut VMGcRef>,
}
impl GcRoot<'_> {
#[inline]
pub fn is_on_wasm_stack(&self) -> bool {
matches!(self.raw, RawGcRoot::Stack(_))
}
#[inline]
pub fn get(&self) -> VMGcRef {
match self.raw {
RawGcRoot::NonStack(ptr) => unsafe { std::ptr::read(ptr.as_ptr()) },
RawGcRoot::Stack(ptr) => unsafe {
let r64 = std::ptr::read(ptr.as_ptr());
VMGcRef::from_r64(r64)
.expect("valid r64")
.expect("non-null")
},
}
}
pub fn set(&mut self, new_ref: VMGcRef) {
match self.raw {
RawGcRoot::NonStack(ptr) => unsafe {
std::ptr::write(ptr.as_ptr(), new_ref);
},
RawGcRoot::Stack(ptr) => unsafe {
let r64 = new_ref.into_r64();
std::ptr::write(ptr.as_ptr(), r64);
},
}
}
}
pub trait GarbageCollection<'a>: Send + Sync {
fn collect_increment(&mut self) -> GcProgress;
fn collect(&mut self) {
loop {
match self.collect_increment() {
GcProgress::Continue => continue,
GcProgress::Complete => return,
}
}
}
}
pub enum GcProgress {
Continue,
Complete,
}
#[cfg(feature = "async")]
pub async fn collect_async<'a>(mut collection: Box<dyn GarbageCollection<'a> + 'a>) {
loop {
match collection.collect_increment() {
GcProgress::Continue => crate::Yield::new().await,
GcProgress::Complete => return,
}
}
}
#[cfg(all(test, feature = "async"))]
mod collect_async_tests {
use super::*;
#[test]
fn is_send_and_sync() {
fn _assert_send_sync<T: Send + Sync>(_: T) {}
fn _foo<'a>(collection: Box<dyn GarbageCollection<'a>>) {
_assert_send_sync(collect_async(collection));
}
}
}