diff --git a/crates/mun_gc/src/handle.rs b/crates/mun_gc/src/handle.rs index f43cb9c8a..5f751b4d0 100644 --- a/crates/mun_gc/src/handle.rs +++ b/crates/mun_gc/src/handle.rs @@ -1,3 +1,7 @@ +use crate::{GCRuntime, Type}; +use std::marker::PhantomData; +use std::sync::{Arc, Weak}; + /// A `GCHandle` is what you interact with outside of the allocator. It is a pointer to a piece of /// memory that points to the actual data stored in memory. /// @@ -16,14 +20,18 @@ unsafe impl Sync for GCHandle {} /// semantics used by the runtime. pub type RawGCHandle = *const *mut std::ffi::c_void; -impl GCHandle { +pub trait HasGCHandlePtr { /// Returns a pointer to the referenced memory. /// /// # Safety /// /// This method is unsafe because casting to a generic type T may be unsafe. We don't know the /// type of the stored data. - pub unsafe fn get_ptr(self) -> *mut T { + unsafe fn get_ptr(&self) -> *mut T; +} + +impl HasGCHandlePtr for GCHandle { + unsafe fn get_ptr(&self) -> *mut T { (*self.0).cast::() } } @@ -39,3 +47,64 @@ impl Into for RawGCHandle { GCHandle(self) } } + +/// A `GCHandle` that automatically roots and unroots its internal `GCHandle`. +pub struct GCRootHandle> { + handle: GCHandle, + runtime: Weak, + ty: PhantomData, +} + +impl> Clone for GCRootHandle { + fn clone(&self) -> Self { + if let Some(runtime) = self.runtime.upgrade() { + unsafe { runtime.root(self.handle) } + } + Self { + handle: self.handle, + runtime: self.runtime.clone(), + ty: Default::default(), + } + } +} + +impl> GCRootHandle { + /// Constructs a new GCRootHandle from a runtime and a handle + /// + /// # Safety + /// + /// This method is unsafe because the passed GCHandle could point to random memory. + pub unsafe fn new(runtime: &Arc, handle: GCHandle) -> Self { + runtime.root(handle); + Self { + handle, + runtime: Arc::downgrade(runtime), + ty: Default::default(), + } + } + + /// Returns the handle of this instance + pub fn handle(&self) -> GCHandle { + self.handle + } +} + +impl> Into for GCRootHandle { + fn into(self) -> GCHandle { + self.handle + } +} + +impl> Drop for GCRootHandle { + fn drop(&mut self) { + if let Some(runtime) = self.runtime.upgrade() { + unsafe { runtime.unroot(self.handle) } + } + } +} + +impl> HasGCHandlePtr for GCRootHandle { + unsafe fn get_ptr(&self) -> *mut R { + self.handle.get_ptr() + } +} diff --git a/crates/mun_gc/src/lib.rs b/crates/mun_gc/src/lib.rs index 4d8bd425d..a4fc65a43 100644 --- a/crates/mun_gc/src/lib.rs +++ b/crates/mun_gc/src/lib.rs @@ -1,7 +1,7 @@ mod handle; mod mark_sweep; -pub use handle::{GCHandle, RawGCHandle}; +pub use handle::{GCHandle, GCRootHandle, HasGCHandlePtr, RawGCHandle}; pub use mark_sweep::MarkSweep; /// A trait used by the GC to identify an object. @@ -33,10 +33,21 @@ pub trait GCRuntime: Send + Sync { unsafe fn object_type(&self, obj: GCHandle) -> T; /// Tell the runtime that the specified object should be considered a root which keeps all other - /// objects it references alive. + /// objects it references alive. Objects marked as root, must also be unrooted before they can + /// be collected. Internally this increments a root refcount. /// /// # Safety /// /// This method is unsafe because the passed GCHandle could point to random memory. - unsafe fn set_root(&self, obj: GCHandle, is_root: bool); + unsafe fn root(&self, obj: GCHandle); + + /// Tell the runtime that the specified object should unrooted which keeps all other + /// objects it references alive. Objects marked as root, must also be unrooted before they can + /// be collected. Internally this decrements a root refcount. When the refcount reaches 0, the + /// object is considered non-rooted. + /// + /// # Safety + /// + /// This method is unsafe because the passed GCHandle could point to random memory. + unsafe fn unroot(&self, obj: GCHandle); } diff --git a/crates/mun_gc/src/mark_sweep.rs b/crates/mun_gc/src/mark_sweep.rs index 92a8db049..c6fd1d098 100644 --- a/crates/mun_gc/src/mark_sweep.rs +++ b/crates/mun_gc/src/mark_sweep.rs @@ -80,7 +80,11 @@ impl GCRuntime for MarkSweep { src.ty.clone() } - unsafe fn set_root(&self, _obj: GCHandle, _is_root: bool) { + unsafe fn root(&self, _obj: GCHandle) { + // NOOP + } + + unsafe fn unroot(&self, _obj: GCHandle) { // NOOP } } diff --git a/crates/mun_runtime/src/allocator.rs b/crates/mun_runtime/src/allocator.rs index 6c2cb564b..e27a3a352 100644 --- a/crates/mun_runtime/src/allocator.rs +++ b/crates/mun_runtime/src/allocator.rs @@ -23,3 +23,7 @@ impl gc::Type for RawTypeInfo { /// Defines an allocator used by the `Runtime` pub type Allocator = gc::MarkSweep; + +pub use gc::GCHandle; + +pub type GCRootHandle = gc::GCRootHandle; diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 937b2211b..547d900af 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -245,8 +245,8 @@ impl Runtime { } /// Returns the runtime's allocator. - pub fn get_allocator(&self) -> Arc { - self.allocator.clone() + pub fn allocator(&self) -> &Arc { + &self.allocator } /// Updates the state of the runtime. This includes checking for file changes, and reloading diff --git a/crates/mun_runtime/src/struct.rs b/crates/mun_runtime/src/struct.rs index 63139181d..cc7558dac 100644 --- a/crates/mun_runtime/src/struct.rs +++ b/crates/mun_runtime/src/struct.rs @@ -1,3 +1,4 @@ +use crate::allocator::{GCHandle, GCRootHandle}; use crate::{ marshal::Marshal, reflection::{ @@ -5,8 +6,8 @@ use crate::{ }, Runtime, }; -use gc::GCHandle; use gc::GCRuntime; +use gc::HasGCHandlePtr; use std::cell::RefCell; use std::ptr::{self, NonNull}; use std::rc::Rc; @@ -29,7 +30,7 @@ impl RawStruct { /// TODO: Handle destruction of `struct(value)` pub struct StructRef { runtime: Rc>, - raw: RawStruct, + handle: GCRootHandle, info: abi::StructInfo, } @@ -40,16 +41,20 @@ impl StructRef { fn new(runtime: Rc>, type_info: &abi::TypeInfo, raw: RawStruct) -> StructRef { assert!(type_info.group.is_struct()); + let handle = { + let runtime_ref = runtime.borrow(); + unsafe { GCRootHandle::new(runtime_ref.allocator(), raw.0) } + }; Self { runtime, - raw, + handle, info: type_info.as_struct().unwrap().clone(), } } /// Consumes the `Struct`, returning a raw Mun struct. pub fn into_raw(self) -> RawStruct { - self.raw + RawStruct(self.handle.handle()) } /// Retrieves its struct information. @@ -65,7 +70,7 @@ impl StructRef { unsafe fn offset_unchecked(&self, field_idx: usize) -> NonNull { let offset = *self.info.field_offsets().get_unchecked(field_idx); // self.raw is never null - NonNull::new_unchecked(self.raw.get_ptr().add(offset as usize).cast::()) + NonNull::new_unchecked(self.handle.get_ptr::().add(offset as usize).cast::()) } /// Retrieves the value of the field corresponding to the specified `field_name`. @@ -144,7 +149,7 @@ impl ArgumentReflection for StructRef { } fn marshal(self) -> Self::Marshalled { - self.raw + self.into_raw() } } @@ -184,7 +189,10 @@ impl Marshal for RawStruct { // struct value. // Create a new object using the runtime's intrinsic - let gc_handle = runtime.borrow().get_allocator().alloc_object(type_info_ptr); + let gc_handle = { + let runtime_ref = runtime.borrow(); + runtime_ref.allocator().alloc_object(type_info_ptr) + }; // Construct let src = ptr.cast::().as_ptr() as *const _;