Skip to content

Commit

Permalink
Make Gc<T>: Send + Sync if T: Send + Sync
Browse files Browse the repository at this point in the history
This improves the ergonomics of `Gc<T>`, allowing nested `Gc` types
without the need for introducing unsafe newtype workarounds [1].
  • Loading branch information
jacob-hughes committed Apr 8, 2021
1 parent 21ca9b8 commit 70ae093
Showing 1 changed file with 37 additions and 11 deletions.
48 changes: 37 additions & 11 deletions src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,39 @@ pub fn gc_init() {
///
/// `Gc<T>` automatically dereferences to `T` (via the `Deref` trait), so
/// you can call `T`'s methods on a value of type `Gc<T>`.
///
/// `Gc<T>` will implement `Sync` as long as `T` implements `Sync`. `Gc<T>`
/// will always implement `Send` because it requires `T` to implement `Send`.
/// This is because if `T` has a finalizer, it will be run on a seperate thread.
#[derive(PartialEq, Eq)]
pub struct Gc<T: ?Sized + Send> {
ptr: NonNull<GcBox<T>>,
ptr: GcPointer<T>,
_phantom: PhantomData<T>,
}

/// This zero-sized wrapper struct is needed to allow `Gc<T>` to have the same
/// `Send` + `Sync` semantics as `T`. Without it, the inner `NonNull` type would
/// mean that a `Gc` never implements `Send` or `Sync`.
#[derive(PartialEq, Eq)]
struct GcPointer<T: ?Sized>(NonNull<GcBox<T>>);

unsafe impl<T> Send for GcPointer<T> {}
unsafe impl<T> Sync for GcPointer<T> {}

impl<T: ?Sized + Unsize<U> + Send, U: ?Sized + Send> CoerceUnsized<Gc<U>> for Gc<T> {}
impl<T: ?Sized + Unsize<U> + Send, U: ?Sized + Send> DispatchFromDyn<Gc<U>> for Gc<T> {}

impl<T: ?Sized + Unsize<U> + Send, U: ?Sized + Send> CoerceUnsized<GcPointer<U>> for GcPointer<T> {}
impl<T: ?Sized + Unsize<U> + Send, U: ?Sized + Send> DispatchFromDyn<GcPointer<U>>
for GcPointer<T>
{
}

impl<T: Send> Gc<T> {
/// Constructs a new `Gc<T>`.
pub fn new(v: T) -> Self {
Gc {
ptr: unsafe { NonNull::new_unchecked(GcBox::new(v)) },
ptr: unsafe { GcPointer(NonNull::new_unchecked(GcBox::new(v))) },
_phantom: PhantomData,
}
}
Expand Down Expand Up @@ -94,7 +113,7 @@ impl<T: Send> Gc<T> {
}

pub fn unregister_finalizer(&mut self) {
let ptr = self.ptr.as_ptr() as *mut GcBox<T>;
let ptr = self.ptr.0.as_ptr() as *mut GcBox<T>;
unsafe {
GcBox::unregister_finalizer(&mut *ptr);
}
Expand All @@ -104,10 +123,10 @@ impl<T: Send> Gc<T> {
impl Gc<dyn Any + Send> {
pub fn downcast<T: Any + Send>(&self) -> Result<Gc<T>, Gc<dyn Any + Send>> {
if (*self).is::<T>() {
let ptr = self.ptr.cast::<GcBox<T>>();
let ptr = self.ptr.0.cast::<GcBox<T>>();
Ok(Gc::from_inner(ptr))
} else {
Err(Gc::from_inner(self.ptr))
Err(Gc::from_inner(self.ptr.0))
}
}
}
Expand All @@ -125,11 +144,11 @@ pub fn needs_finalizer<T>() -> bool {
impl<T: ?Sized + Send> Gc<T> {
/// Get a raw pointer to the underlying value `T`.
pub fn into_raw(this: Self) -> *const T {
this.ptr.as_ptr() as *const T
this.ptr.0.as_ptr() as *const T
}

pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.ptr.as_ptr() == other.ptr.as_ptr()
this.ptr.0.as_ptr() == other.ptr.0.as_ptr()
}

/// Get a `Gc<T>` from a raw pointer.
Expand All @@ -143,14 +162,14 @@ impl<T: ?Sized + Send> Gc<T> {
/// size and alignment of the originally allocated block.
pub fn from_raw(raw: *const T) -> Gc<T> {
Gc {
ptr: unsafe { NonNull::new_unchecked(raw as *mut GcBox<T>) },
ptr: unsafe { GcPointer(NonNull::new_unchecked(raw as *mut GcBox<T>)) },
_phantom: PhantomData,
}
}

fn from_inner(ptr: NonNull<GcBox<T>>) -> Self {
Self {
ptr,
ptr: GcPointer(ptr),
_phantom: PhantomData,
}
}
Expand All @@ -162,7 +181,7 @@ impl<T: Send> Gc<MaybeUninit<T>> {
/// when the content is not yet fully initialized causes immediate undefined
/// behaviour.
pub unsafe fn assume_init(self) -> Gc<T> {
let ptr = self.ptr.as_ptr() as *mut GcBox<MaybeUninit<T>>;
let ptr = self.ptr.0.as_ptr() as *mut GcBox<MaybeUninit<T>>;
Gc::from_inner((&mut *ptr).assume_init())
}
}
Expand Down Expand Up @@ -255,7 +274,7 @@ impl<T: ?Sized + Send> Deref for Gc<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
unsafe { &*(self.ptr.as_ptr() as *const T) }
unsafe { &*(self.ptr.0.as_ptr() as *const T) }
}
}

Expand All @@ -270,6 +289,13 @@ impl<T: ?Sized + Send> Clone for Gc<T> {
}
}

impl<T: ?Sized> Copy for GcPointer<T> {}

impl<T: ?Sized> Clone for GcPointer<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Hash + Send> Hash for Gc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
Expand Down

0 comments on commit 70ae093

Please sign in to comment.