Skip to main content

gc_alloc/
boxed.rs

1use std::{ffi::c_void, ptr::NonNull};
2
3use crate::{GcToken, gc};
4
5pub fn alloc<T>(_token: &impl GcToken, val: T) -> GcBox<T> {
6    let ptr =
7        unsafe { gc::GC_memalign(std::mem::size_of::<T>(), std::mem::align_of::<T>()) as *mut T };
8    let ptr = NonNull::new(ptr).expect("GC_malloc failed");
9    unsafe { ptr.write(val) };
10
11    register_finalizer(ptr.as_ptr());
12    GcBox(ptr)
13}
14
15pub struct GcBox<T>(NonNull<T>);
16
17impl<T> GcBox<T> {
18    pub fn as_ptr(&self) -> *mut T {
19        self.0.as_ptr()
20    }
21
22    pub fn as_ref<'gc>(&self, _token: &'gc impl GcToken) -> &'gc T {
23        unsafe { &*self.as_ptr() }
24    }
25
26    #[allow(clippy::mut_from_ref)]
27    pub fn as_mut<'gc>(&mut self, _token: &'gc impl GcToken) -> &'gc mut T {
28        unsafe { &mut *self.as_ptr() }
29    }
30
31    /// # Safety
32    /// The returned reference cannot be used in a thread that is not registered with the GC.
33    pub unsafe fn as_ref_unconstrained(&self) -> &'static mut T {
34        unsafe { &mut *self.as_ptr() }
35    }
36}
37
38fn register_finalizer<T>(ptr: *mut T) {
39    if std::mem::needs_drop::<T>() {
40        extern "C" fn finalizer<T>(obj: *mut c_void, _: *mut c_void) {
41            let ptr = obj as *mut T;
42            unsafe { std::ptr::drop_in_place(ptr) };
43        }
44
45        unsafe {
46            gc::GC_register_finalizer(
47                ptr as *mut std::ffi::c_void,
48                Some(finalizer::<T>),
49                std::ptr::null_mut(),
50                std::ptr::null_mut(),
51                std::ptr::null_mut(),
52            );
53        }
54    }
55}