Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add raw pointer interface #71

Merged
merged 7 commits into from
Dec 18, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 110 additions & 22 deletions gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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 //
Expand Down Expand Up @@ -121,6 +121,77 @@ impl<T: Trace + ?Sized> Gc<T> {
}
}

impl<T: Trace + ?Sized> Gc<T> {
/// 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::<GcBox<()>>();
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<T>;
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,
}
Comment on lines +188 to +191
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Gc needs to be set as a root (#122).

}
}

impl<T: Trace + ?Sized> Finalize for Gc<T> {}

unsafe impl<T: Trace + ?Sized> Trace for Gc<T> {
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -489,7 +559,6 @@ unsafe impl<T: Trace + ?Sized> Trace for GcCell<T> {
}
}


/// A wrapper type for an immutably borrowed value from a `GcCell<T>`.
pub struct GcCellRef<'a, T: Trace + ?Sized + 'static> {
flags: &'a Cell<BorrowFlag>,
Expand Down Expand Up @@ -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<T>`.
pub struct GcCellRefMut<'a, T: Trace + ?Sized + 'static> {
flags: &'a Cell<BorrowFlag>,
Expand Down Expand Up @@ -562,7 +630,6 @@ impl<'a, T: Trace + ?Sized + Debug> Debug for GcCellRefMut<'a, T> {
}
}


unsafe impl<T: ?Sized + Send> Send for GcCell<T> {}

impl<T: Trace + Clone> Clone for GcCell<T> {
Expand Down Expand Up @@ -630,16 +697,37 @@ impl<T: Trace + ?Sized + Ord> Ord for GcCell<T> {
impl<T: Trace + ?Sized + Debug> Debug for GcCell<T> {
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", &"<borrowed>")
.finish()
}
BorrowState::Unused | BorrowState::Reading => f
.debug_struct("GcCell")
.field("value", &self.borrow())
.finish(),
BorrowState::Writing => f
.debug_struct("GcCell")
.field("value", &"<borrowed>")
.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<T: ?Sized, U>(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::<GcBox<()>>() & (align - 1);
if misaligned > 0 {
align - misaligned
} else {
0
}
}