diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 0eb1afe..42ead33 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -4,21 +4,21 @@ //! It is marked as non-sendable because the garbage collection only occurs //! thread-locally. -#![cfg_attr(feature = "nightly", - feature(coerce_unsized, - optin_builtin_traits, - unsize, - specialization))] +#![cfg_attr( + feature = "nightly", + feature(coerce_unsized, optin_builtin_traits, unsize, specialization) +)] use gc::GcBox; +use std::alloc::Layout; use std::cell::{Cell, UnsafeCell}; use std::cmp::Ordering; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use std::mem; -use std::ptr::NonNull; +use std::mem::{self, align_of_val}; use std::ops::{Deref, DerefMut}; +use std::ptr::{self, NonNull}; use std::rc::Rc; #[cfg(feature = "nightly")] @@ -31,8 +31,8 @@ mod trace; // We re-export the Trace method, as well as some useful internal methods for // managing collections or configuring the garbage collector. +pub use gc::{finalizer_safe, force_collect}; pub use trace::{Finalize, Trace}; -pub use gc::{force_collect, finalizer_safe}; //////// // Gc // @@ -121,6 +121,77 @@ impl Gc { } } +impl Gc { + /// Consumes the `Gc`, returning the wrapped pointer. + /// + /// To avoid a memory leak, the pointer must be converted back into a `Gc` + /// using [`Gc::from_raw`][from_raw]. + /// + /// [from_raw]: struct.Gc.html#method.from_raw + /// + /// # Examples + /// + /// ``` + /// use gc::Gc; + /// + /// let x = Gc::new(22); + /// let x_ptr = Gc::into_raw(x); + /// assert_eq!(unsafe { *x_ptr }, 22); + /// ``` + pub fn into_raw(this: Self) -> *const T { + let ptr: *const T = &*this; + mem::forget(this); + ptr + } + + /// Constructs an `Gc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to a + /// [`Gc::into_raw`][into_raw]. + /// + /// This function is unsafe because improper use may lead to memory + /// problems. For example, a use-after-free will occur if the function is + /// called twice on the same raw pointer. + /// + /// [into_raw]: struct.Gc.html#method.into_raw + /// + /// # Examples + /// + /// ``` + /// use gc::Gc; + /// + /// let x = Gc::new(22); + /// let x_ptr = Gc::into_raw(x); + /// + /// unsafe { + /// // Convert back to an `Gc` to prevent leak. + /// let x = Gc::from_raw(x_ptr); + /// assert_eq!(*x, 22); + /// + /// // Further calls to `Gc::from_raw(x_ptr)` would be memory unsafe. + /// } + /// + /// // The memory can be freed at any time after `x` went out of scope above + /// // (when the collector is run), which would result in `x_ptr` dangling! + /// ``` + pub unsafe fn from_raw(ptr: *const T) -> Self { + // Align the unsized value to the end of the GcBox. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + padding_needed_for_gc(align)) as isize; + + // Reverse the offset to find the original GcBox. + let fake_ptr = ptr as *mut GcBox; + let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + + Gc { + ptr_root: Cell::new(NonNull::new_unchecked(rc_ptr)), + marker: PhantomData, + } + } +} + impl Finalize for Gc {} unsafe impl Trace for Gc { @@ -361,7 +432,6 @@ impl BorrowFlag { } } - /// A mutable memory location with dynamically checked borrow rules /// that can be used inside of a garbage-collected pointer. /// @@ -489,7 +559,6 @@ unsafe impl Trace for GcCell { } } - /// A wrapper type for an immutably borrowed value from a `GcCell`. pub struct GcCellRef<'a, T: Trace + ?Sized + 'static> { flags: &'a Cell, @@ -518,7 +587,6 @@ impl<'a, T: Trace + ?Sized + Debug> Debug for GcCellRef<'a, T> { } } - /// A wrapper type for a mutably borrowed value from a `GcCell`. pub struct GcCellRefMut<'a, T: Trace + ?Sized + 'static> { flags: &'a Cell, @@ -562,7 +630,6 @@ impl<'a, T: Trace + ?Sized + Debug> Debug for GcCellRefMut<'a, T> { } } - unsafe impl Send for GcCell {} impl Clone for GcCell { @@ -630,16 +697,37 @@ impl Ord for GcCell { impl Debug for GcCell { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.flags.get().borrowed() { - BorrowState::Unused | BorrowState::Reading => { - f.debug_struct("GcCell") - .field("value", &self.borrow()) - .finish() - } - BorrowState::Writing => { - f.debug_struct("GcCell") - .field("value", &"") - .finish() - } + BorrowState::Unused | BorrowState::Reading => f + .debug_struct("GcCell") + .field("value", &self.borrow()) + .finish(), + BorrowState::Writing => f + .debug_struct("GcCell") + .field("value", &"") + .finish(), } } } + +// Sets the data pointer of a `?Sized` raw pointer. +// +// For a slice/trait object, this sets the `data` field and leaves the rest +// unchanged. For a sized raw pointer, this simply sets the pointer. +unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + ptr +} + +fn padding_needed_for_gc(align: usize) -> usize { + assert!(align.count_ones() == 1, "align must be a power-of-two!"); + + // Determine how much our header is misaligned for `align`. + // `align - 1` is a mask of all bits less than the alignment, + // as `align` is a power-of-two. + let misaligned = mem::size_of::>() & (align - 1); + if misaligned > 0 { + align - misaligned + } else { + 0 + } +}