From 1feaecbb815c150a4295cc3fa58940117008048d Mon Sep 17 00:00:00 2001 From: nekevss Date: Mon, 3 Oct 2022 00:04:39 -0400 Subject: [PATCH 01/10] Initial draft version work --- gc/src/gc.rs | 68 +++++++++++++++-- gc/src/lib.rs | 177 +++++++++++++++++++++++++++++++++++++++++++- gc/tests/weak_gc.rs | 16 ++++ 3 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 gc/tests/weak_gc.rs diff --git a/gc/src/gc.rs b/gc/src/gc.rs index 58696a3..a38b0c1 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -51,14 +51,20 @@ const ROOTS_MAX: usize = ROOTS_MASK; // max allowed value of roots pub(crate) struct GcBoxHeader { roots: Cell, // high bit is used as mark flag + weak_references: Cell, next: Cell>>>, } impl GcBoxHeader { #[inline] - pub fn new(next: Option>>) -> Self { + pub fn new(next: Option>>, weak_flag:bool) -> Self { + let weak_references = match weak_flag { + true=> Cell::new(1_usize), + false=>Cell::new(0_usize), + }; GcBoxHeader { roots: Cell::new(1), // unmarked and roots count = 1 + weak_references, next: Cell::new(next), } } @@ -83,6 +89,7 @@ impl GcBoxHeader { #[inline] pub fn dec_roots(&self) { + println!("Lowering the amount of roots!"); self.roots.set(self.roots.get() - 1) // no underflow check } @@ -100,6 +107,32 @@ impl GcBoxHeader { pub fn unmark(&self) { self.roots.set(self.roots.get() & !MARK_MASK) } + + #[inline] + pub fn inc_weak_refs(&self) { + // Incrementing roots keeps the count and also checks that we don't exceed roots + println!("Incrementing! {}", self.weak_references.get()); + self.weak_references.set(self.weak_references.get() + 1); + } + + #[inline] + pub fn dec_weak_refs(&self) { + self.weak_references.set(self.weak_references.get() + 1); + } + + #[inline] + pub fn has_weak_ref(&self) -> bool { + self.weak_references.get() > 0 + } + + #[inline] + pub fn get_strong_refs(&self) -> usize { + if self.weak_references.get() <= self.roots() { + self.roots() - self.weak_references.get() + } else { + 0_usize + } + } } #[repr(C)] // to justify the layout computation in Gc::from_raw @@ -113,7 +146,7 @@ impl GcBox { /// and appends it to the thread-local `GcBox` chain. /// /// A `GcBox` allocated this way starts its life rooted. - pub(crate) fn new(value: T) -> NonNull { + pub(crate) fn new(value: T, weak_flag: bool) -> NonNull { GC_STATE.with(|st| { let mut st = st.borrow_mut(); @@ -132,8 +165,12 @@ impl GcBox { } } + let header = GcBoxHeader::new(st.boxes_start.take(), weak_flag); + + println!("Newly created header: {} roots and {} weaks", header.roots(), header.weak_references.get()); + let gcbox = Box::into_raw(Box::new(GcBox { - header: GcBoxHeader::new(st.boxes_start.take()), + header, data: value, })); @@ -186,6 +223,22 @@ impl GcBox { pub(crate) fn value(&self) -> &T { &self.data } + + pub(crate) fn root_weakly(&self) { + self.header.inc_roots(); + self.header.inc_weak_refs(); + println!("roots: {} vs. weak_refs: {}", self.header.roots(), self.header.weak_references.get()); + } + + pub(crate) fn unroot_weakly(&self) { + self.header.dec_roots(); + self.header.dec_weak_refs(); + println!("roots: {} vs. weak_refs: {}", self.header.roots(), self.header.weak_references.get()); + } + + pub(crate) fn check_strong_refs(&self) -> bool { + self.header.get_strong_refs() > 0 + } } /// Collects garbage. @@ -200,9 +253,10 @@ fn collect_garbage(st: &mut GcState) { // Walk the tree, tracing and marking the nodes let mut mark_head = head.get(); while let Some(node) = mark_head { - if (*node.as_ptr()).header.roots() > 0 { + //println!("Running mark with roots: {} and strong_refs: {}", (*node.as_ptr()).header.roots(), (*node.as_ptr()).header.roots()); + if (*node.as_ptr()).header.get_strong_refs() > 0 { (*node.as_ptr()).trace_inner(); - } + } mark_head = (*node.as_ptr()).header.next.get(); } @@ -241,8 +295,10 @@ fn collect_garbage(st: &mut GcState) { unsafe { let unmarked = mark(&st.boxes_start); if unmarked.is_empty() { + println!("Unmarked array was empty"); return; } + println!("Unmarked wasn't empty!"); for node in &unmarked { Trace::finalize_glue(&(*node.this.as_ptr()).data); } @@ -257,7 +313,9 @@ fn collect_garbage(st: &mut GcState) { pub fn force_collect() { GC_STATE.with(|st| { let mut st = st.borrow_mut(); + println!("Running garbage collect"); collect_garbage(&mut *st); + println!("Collection Complete") }); } diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 76a9e8c..239eba3 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -76,7 +76,7 @@ impl Gc { unsafe { // Allocate the memory for the object - let ptr = GcBox::new(value); + let ptr = GcBox::new(value, false); // When we create a Gc, all pointers which have been moved to the // heap no longer need to be rooted, so we unroot them. @@ -214,6 +214,20 @@ impl Gc { gc.set_root(); gc } + + #[inline] + pub fn clone_weak_ref(&self) -> WeakGc { + unsafe { + self.inner().root_weakly(); + let weak_gc = WeakGc { + ptr_root: Cell::new(self.ptr_root.get()), + strong_ref_check: Cell::new(true), + marker:PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } } impl Finalize for Gc {} @@ -382,6 +396,167 @@ impl std::convert::AsRef for Gc { } } +//////////// +// WeakGc // +//////////// + +/// A weak Garbage Collected pointer for an immutable value +pub struct WeakGc { + ptr_root: Cell>>, + strong_ref_check: Cell, + marker: PhantomData>, +} + +impl WeakGc { + /// Crate a new Weak type Gc + /// + /// This method can trigger a collection + pub fn new(value: T) -> Self { + assert!(mem::align_of::>() > 1); + + unsafe { + // Allocate the memory for the object + let ptr = GcBox::new(value, true); + // We assume when creating a WeakGc that trace_check is true until + // told otherwise by trace + (*ptr.as_ptr()).value().unroot(); + let weak_gc = WeakGc { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + strong_ref_check: Cell::new(true), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl WeakGc { + fn rooted(&self) -> bool { + self.ptr_root.get().as_ptr() as *mut u8 as usize & 1 != 0 + } + + unsafe fn set_root(&self) { + let ptr = self.ptr_root.get().as_ptr(); + let data = ptr as *mut u8; + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); + self.ptr_root.set(NonNull::new_unchecked(ptr)); + } + + unsafe fn clear_root(&self) { + self.ptr_root.set(clear_root_bit(self.ptr_root.get())); + } + + #[inline] + fn inner_ptr(&self) -> *mut GcBox { + // If we are currently in the dropping phase of garbage collection, + // it would be undefined behavior to dereference this pointer. + // By opting into `Trace` you agree to not dereference this pointer + // within your drop method, meaning that it should be safe. + // + // This assert exists just in case. + assert!(finalizer_safe()); + + unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() } + } + + #[inline] + fn inner(&self) -> &GcBox { + unsafe { &*self.inner_ptr() } + } + + #[inline] + fn set_finalized(&self) { + println!("Finalized was called"); + self.strong_ref_check.set(self.inner().check_strong_refs()) + } + + fn set_traced(&self) { + println!("Trace was called"); + self.strong_ref_check.set(self.inner().check_strong_refs()) + } +} + +impl WeakGc { + pub fn try_deref(&self) -> Option<&T> { + println!("{}", self.inner().check_strong_refs()); + if self.strong_ref_check.get() { + Some(self.inner().value()) + } else { + None + } + } + + pub fn has_strong_refs(&self) -> bool { + self.strong_ref_check.get() + } +} + +impl Finalize for WeakGc { + fn finalize(&self) { + println!("We are finalizing the WeakGc") + } +} + +unsafe impl Trace for WeakGc { + #[inline] + unsafe fn trace(&self) { + // We set trace check here to false in the case that a trace has run and no + // strong refs exist. + self.set_traced(); + self.inner().trace_inner(); + } + + #[inline] + unsafe fn root(&self) { + assert!(!self.rooted(), "Can't double-root a WeakGc"); + self.inner().root_weakly(); + self.set_root(); + } + + #[inline] + unsafe fn unroot(&self) { + assert!(self.rooted(), "Can't double-unroot a WeakGc"); + self.inner().unroot_weakly(); + self.clear_root(); + } + + #[inline] + fn finalize_glue(&self) { + self.set_finalized(); + Finalize::finalize(self) + } +} + +impl Clone for WeakGc { + #[inline] + fn clone(&self) -> Self { + unsafe { + self.inner().root_weakly(); + let weak_gc = WeakGc { + ptr_root: Cell::new(self.ptr_root.get()), + strong_ref_check: Cell::new(true), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl Drop for WeakGc { + #[inline] + fn drop(&mut self) { + if self.rooted() { + unsafe { + self.inner().unroot_weakly() + } + } + } +} + + //////////// // GcCell // //////////// diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs new file mode 100644 index 0000000..db964bf --- /dev/null +++ b/gc/tests/weak_gc.rs @@ -0,0 +1,16 @@ +use gc::{Gc, WeakGc, GcCell}; + +#[test] +fn weak_gc_try_deref_some_value() { + let weak = WeakGc::new(GcCell::new(1)); + let comparable = GcCell::new(1); + assert_eq!(weak.try_deref(), Some(&comparable)); +} + +#[test] +fn weak_gc_from_existing() { + let gc = Gc::new(GcCell::new(1)); + let weak_gc = gc.clone_weak_ref(); + let comparable = GcCell::new(1); + assert_eq!(weak_gc.try_deref(), Some(&comparable)) +} \ No newline at end of file From d44c8859baf8c1376d2a70820733a6708e72916c Mon Sep 17 00:00:00 2001 From: nekevss Date: Mon, 3 Oct 2022 21:07:53 -0400 Subject: [PATCH 02/10] Complete first draft --- gc/src/gc.rs | 39 +++-------- gc/src/lib.rs | 137 ++++++++++++++++++++++++++++++-------- gc/tests/derive_bounds.rs | 2 +- gc/tests/weak_gc.rs | 18 ++++- 4 files changed, 137 insertions(+), 59 deletions(-) diff --git a/gc/src/gc.rs b/gc/src/gc.rs index a38b0c1..3bbb77f 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -57,10 +57,10 @@ pub(crate) struct GcBoxHeader { impl GcBoxHeader { #[inline] - pub fn new(next: Option>>, weak_flag:bool) -> Self { + pub fn new(next: Option>>, weak_flag: bool) -> Self { let weak_references = match weak_flag { - true=> Cell::new(1_usize), - false=>Cell::new(0_usize), + true => Cell::new(1_usize), + false => Cell::new(0_usize), }; GcBoxHeader { roots: Cell::new(1), // unmarked and roots count = 1 @@ -89,7 +89,6 @@ impl GcBoxHeader { #[inline] pub fn dec_roots(&self) { - println!("Lowering the amount of roots!"); self.roots.set(self.roots.get() - 1) // no underflow check } @@ -111,27 +110,17 @@ impl GcBoxHeader { #[inline] pub fn inc_weak_refs(&self) { // Incrementing roots keeps the count and also checks that we don't exceed roots - println!("Incrementing! {}", self.weak_references.get()); self.weak_references.set(self.weak_references.get() + 1); } #[inline] pub fn dec_weak_refs(&self) { - self.weak_references.set(self.weak_references.get() + 1); - } - - #[inline] - pub fn has_weak_ref(&self) -> bool { - self.weak_references.get() > 0 + self.weak_references.set(self.weak_references.get() - 1); } #[inline] pub fn get_strong_refs(&self) -> usize { - if self.weak_references.get() <= self.roots() { - self.roots() - self.weak_references.get() - } else { - 0_usize - } + self.roots() - self.weak_references.get() } } @@ -167,8 +156,6 @@ impl GcBox { let header = GcBoxHeader::new(st.boxes_start.take(), weak_flag); - println!("Newly created header: {} roots and {} weaks", header.roots(), header.weak_references.get()); - let gcbox = Box::into_raw(Box::new(GcBox { header, data: value, @@ -197,7 +184,10 @@ impl GcBox { /// Marks this `GcBox` and marks through its data. pub(crate) unsafe fn trace_inner(&self) { if !self.header.is_marked() { - self.header.mark(); + // Only mark the header if the strong refs(roots) outweigh the weak refs + if self.header.get_strong_refs() > 0 { + self.header.mark() + } self.data.trace(); } } @@ -227,13 +217,11 @@ impl GcBox { pub(crate) fn root_weakly(&self) { self.header.inc_roots(); self.header.inc_weak_refs(); - println!("roots: {} vs. weak_refs: {}", self.header.roots(), self.header.weak_references.get()); } pub(crate) fn unroot_weakly(&self) { self.header.dec_roots(); self.header.dec_weak_refs(); - println!("roots: {} vs. weak_refs: {}", self.header.roots(), self.header.weak_references.get()); } pub(crate) fn check_strong_refs(&self) -> bool { @@ -253,10 +241,9 @@ fn collect_garbage(st: &mut GcState) { // Walk the tree, tracing and marking the nodes let mut mark_head = head.get(); while let Some(node) = mark_head { - //println!("Running mark with roots: {} and strong_refs: {}", (*node.as_ptr()).header.roots(), (*node.as_ptr()).header.roots()); - if (*node.as_ptr()).header.get_strong_refs() > 0 { + if (*node.as_ptr()).header.roots() > 0 { (*node.as_ptr()).trace_inner(); - } + } mark_head = (*node.as_ptr()).header.next.get(); } @@ -295,10 +282,8 @@ fn collect_garbage(st: &mut GcState) { unsafe { let unmarked = mark(&st.boxes_start); if unmarked.is_empty() { - println!("Unmarked array was empty"); return; } - println!("Unmarked wasn't empty!"); for node in &unmarked { Trace::finalize_glue(&(*node.this.as_ptr()).data); } @@ -313,9 +298,7 @@ fn collect_garbage(st: &mut GcState) { pub fn force_collect() { GC_STATE.with(|st| { let mut st = st.borrow_mut(); - println!("Running garbage collect"); collect_garbage(&mut *st); - println!("Collection Complete") }); } diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 239eba3..416a901 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -222,7 +222,7 @@ impl Gc { let weak_gc = WeakGc { ptr_root: Cell::new(self.ptr_root.get()), strong_ref_check: Cell::new(true), - marker:PhantomData, + marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -409,7 +409,7 @@ pub struct WeakGc { impl WeakGc { /// Crate a new Weak type Gc - /// + /// /// This method can trigger a collection pub fn new(value: T) -> Self { assert!(mem::align_of::>() > 1); @@ -417,7 +417,7 @@ impl WeakGc { unsafe { // Allocate the memory for the object let ptr = GcBox::new(value, true); - // We assume when creating a WeakGc that trace_check is true until + // We assume when creating a WeakGc that strong_ref_check is true until // told otherwise by trace (*ptr.as_ptr()).value().unroot(); let weak_gc = WeakGc { @@ -465,17 +465,6 @@ impl WeakGc { fn inner(&self) -> &GcBox { unsafe { &*self.inner_ptr() } } - - #[inline] - fn set_finalized(&self) { - println!("Finalized was called"); - self.strong_ref_check.set(self.inner().check_strong_refs()) - } - - fn set_traced(&self) { - println!("Trace was called"); - self.strong_ref_check.set(self.inner().check_strong_refs()) - } } impl WeakGc { @@ -493,18 +482,14 @@ impl WeakGc { } } -impl Finalize for WeakGc { - fn finalize(&self) { - println!("We are finalizing the WeakGc") - } -} +impl Finalize for WeakGc {} unsafe impl Trace for WeakGc { #[inline] unsafe fn trace(&self) { - // We set trace check here to false in the case that a trace has run and no - // strong refs exist. - self.set_traced(); + // Set the strong reference here to false in the case that a trace has run and no + // strong refs exist. + self.strong_ref_check.set(self.inner().check_strong_refs()); self.inner().trace_inner(); } @@ -524,7 +509,6 @@ unsafe impl Trace for WeakGc { #[inline] fn finalize_glue(&self) { - self.set_finalized(); Finalize::finalize(self) } } @@ -536,7 +520,7 @@ impl Clone for WeakGc { self.inner().root_weakly(); let weak_gc = WeakGc { ptr_root: Cell::new(self.ptr_root.get()), - strong_ref_check: Cell::new(true), + strong_ref_check: Cell::new(self.strong_ref_check.get()), marker: PhantomData, }; weak_gc.set_root(); @@ -549,13 +533,111 @@ impl Drop for WeakGc { #[inline] fn drop(&mut self) { if self.rooted() { - unsafe { - self.inner().unroot_weakly() - } + self.inner().unroot_weakly() } } } +impl Deref for WeakGc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().value() + } +} + +impl Default for WeakGc { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl PartialEq for WeakGc { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for WeakGc {} + +impl PartialOrd for WeakGc { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + **self < **other + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + **self <= **other + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + **self > **other + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + **self >= **other + } +} + +impl Ord for WeakGc { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl Hash for WeakGc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl Display for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +impl Debug for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl fmt::Pointer for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.inner(), f) + } +} + +impl From for WeakGc { + fn from(t: T) -> Self { + Self::new(t) + } +} + +impl std::borrow::Borrow for WeakGc { + fn borrow(&self) -> &T { + &**self + } +} + +impl std::convert::AsRef for WeakGc { + fn as_ref(&self) -> &T { + &**self + } +} //////////// // GcCell // @@ -813,7 +895,6 @@ unsafe impl Trace for GcCell { } } - #[inline] unsafe fn root(&self) { assert!(!self.flags.get().rooted(), "Can't root a GcCell twice!"); self.flags.set(self.flags.get().set_rooted(true)); diff --git a/gc/tests/derive_bounds.rs b/gc/tests/derive_bounds.rs index 9552545..0ff214c 100644 --- a/gc/tests/derive_bounds.rs +++ b/gc/tests/derive_bounds.rs @@ -1,5 +1,5 @@ -use gc_derive::{Finalize, Trace}; use gc::Gc; +use gc_derive::{Finalize, Trace}; // This impl should *not* require T: Trace. #[derive(Finalize, Trace)] diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs index db964bf..43bb2f5 100644 --- a/gc/tests/weak_gc.rs +++ b/gc/tests/weak_gc.rs @@ -1,4 +1,4 @@ -use gc::{Gc, WeakGc, GcCell}; +use gc::{Gc, GcCell, WeakGc}; #[test] fn weak_gc_try_deref_some_value() { @@ -13,4 +13,18 @@ fn weak_gc_from_existing() { let weak_gc = gc.clone_weak_ref(); let comparable = GcCell::new(1); assert_eq!(weak_gc.try_deref(), Some(&comparable)) -} \ No newline at end of file +} + +#[test] +fn weak_gc_different_copies() { + let gc = Gc::new(GcCell::new(1)); + let weak_gc1 = gc.clone_weak_ref(); + let weak_gc2 = weak_gc1.clone(); + + { + let _weak_gc3 = WeakGc::new(GcCell::new(2)); + gc::force_collect(); + } + + assert_eq!(weak_gc2.has_strong_refs(), true); +} From 8e1575f07a90a066b5d80c7272a9f883b830c35c Mon Sep 17 00:00:00 2001 From: nekevss Date: Mon, 3 Oct 2022 21:19:28 -0400 Subject: [PATCH 03/10] Complete random touchups --- gc/src/lib.rs | 3 +-- gc/tests/weak_gc.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 416a901..669abbc 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -468,8 +468,7 @@ impl WeakGc { } impl WeakGc { - pub fn try_deref(&self) -> Option<&T> { - println!("{}", self.inner().check_strong_refs()); + pub fn value(&self) -> Option<&T> { if self.strong_ref_check.get() { Some(self.inner().value()) } else { diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs index 43bb2f5..a25adb2 100644 --- a/gc/tests/weak_gc.rs +++ b/gc/tests/weak_gc.rs @@ -3,16 +3,14 @@ use gc::{Gc, GcCell, WeakGc}; #[test] fn weak_gc_try_deref_some_value() { let weak = WeakGc::new(GcCell::new(1)); - let comparable = GcCell::new(1); - assert_eq!(weak.try_deref(), Some(&comparable)); + assert_eq!(weak.value(), Some(&(GcCell::new(1)))); } #[test] fn weak_gc_from_existing() { let gc = Gc::new(GcCell::new(1)); let weak_gc = gc.clone_weak_ref(); - let comparable = GcCell::new(1); - assert_eq!(weak_gc.try_deref(), Some(&comparable)) + assert_eq!(weak_gc.value(), Some(&(GcCell::new(1)))); } #[test] From 5e5ff59a783de3e844c5634f16ae6b5bc62587dc Mon Sep 17 00:00:00 2001 From: nekevss Date: Thu, 6 Oct 2022 18:27:59 -0400 Subject: [PATCH 04/10] Move weak-type to queue and implement trait method --- gc/src/gc.rs | 62 ++++----- gc/src/lib.rs | 45 ++++--- gc/src/trace.rs | 263 +++++++++++++++++++++++++++------------ gc/tests/gc_semantics.rs | 96 ++++++++------ gc/tests/trace_impl.rs | 3 + gc_derive/src/lib.rs | 9 ++ 6 files changed, 318 insertions(+), 160 deletions(-) diff --git a/gc/src/gc.rs b/gc/src/gc.rs index 3bbb77f..dd061a5 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -51,20 +51,16 @@ const ROOTS_MAX: usize = ROOTS_MASK; // max allowed value of roots pub(crate) struct GcBoxHeader { roots: Cell, // high bit is used as mark flag - weak_references: Cell, + weak_flag: Cell, next: Cell>>>, } impl GcBoxHeader { #[inline] pub fn new(next: Option>>, weak_flag: bool) -> Self { - let weak_references = match weak_flag { - true => Cell::new(1_usize), - false => Cell::new(0_usize), - }; GcBoxHeader { roots: Cell::new(1), // unmarked and roots count = 1 - weak_references, + weak_flag: Cell::new(weak_flag), next: Cell::new(next), } } @@ -108,19 +104,13 @@ impl GcBoxHeader { } #[inline] - pub fn inc_weak_refs(&self) { - // Incrementing roots keeps the count and also checks that we don't exceed roots - self.weak_references.set(self.weak_references.get() + 1); + pub fn set_weak(&self, new_flag: bool) { + self.weak_flag.set(new_flag); } #[inline] - pub fn dec_weak_refs(&self) { - self.weak_references.set(self.weak_references.get() - 1); - } - - #[inline] - pub fn get_strong_refs(&self) -> usize { - self.roots() - self.weak_references.get() + pub fn is_weak(&self) -> bool { + self.weak_flag.get() } } @@ -184,14 +174,20 @@ impl GcBox { /// Marks this `GcBox` and marks through its data. pub(crate) unsafe fn trace_inner(&self) { if !self.header.is_marked() { - // Only mark the header if the strong refs(roots) outweigh the weak refs - if self.header.get_strong_refs() > 0 { - self.header.mark() - } + self.header.mark(); self.data.trace(); } } + /// Trace inner data + pub(crate) unsafe fn weak_trace(&self) -> bool { + let marked = self.data.weak_trace(); + if marked { + self.header.mark(); + } + marked + } + /// Increases the root count on this `GcBox`. /// Roots prevent the `GcBox` from being destroyed by the garbage collector. pub(crate) unsafe fn root_inner(&self) { @@ -214,18 +210,16 @@ impl GcBox { &self.data } - pub(crate) fn root_weakly(&self) { - self.header.inc_roots(); - self.header.inc_weak_refs(); + pub(crate) fn is_marked(&self) -> bool { + self.header.is_marked() } - pub(crate) fn unroot_weakly(&self) { - self.header.dec_roots(); - self.header.dec_weak_refs(); + pub(crate) fn root_weakly(&self) { + self.header.set_weak(true) } - pub(crate) fn check_strong_refs(&self) -> bool { - self.header.get_strong_refs() > 0 + pub(crate) fn unroot_weakly(&self) { + self.header.set_weak(false); } } @@ -239,15 +233,27 @@ fn collect_garbage(st: &mut GcState) { } unsafe fn mark(head: &Cell>>>) -> Vec> { // Walk the tree, tracing and marking the nodes + let mut weak_nodes = Vec::new(); let mut mark_head = head.get(); while let Some(node) = mark_head { if (*node.as_ptr()).header.roots() > 0 { (*node.as_ptr()).trace_inner(); } + if (*node.as_ptr()).header.is_weak() { + weak_nodes.push(node); + } + mark_head = (*node.as_ptr()).header.next.get(); } + // Evaluate any Weak `GcBox` to determine if it holds a reachable property + if weak_nodes.len() > 0 { + for node in weak_nodes { + let _weak_check = (*node.as_ptr()).weak_trace(); + } + } + // Collect a vector of all of the nodes which were not marked, // and unmark the ones which were. let mut unmarked = Vec::new(); diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 669abbc..0564d2c 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -221,7 +221,7 @@ impl Gc { self.inner().root_weakly(); let weak_gc = WeakGc { ptr_root: Cell::new(self.ptr_root.get()), - strong_ref_check: Cell::new(true), + is_live: Cell::new(true), marker: PhantomData, }; weak_gc.set_root(); @@ -238,6 +238,15 @@ unsafe impl Trace for Gc { self.inner().trace_inner(); } + #[inline] + unsafe fn weak_trace(&self) -> bool { + let marked = match self.inner().is_marked() { + true => self.inner().is_marked(), + false => self.inner().weak_trace(), + }; + marked + } + #[inline] unsafe fn root(&self) { assert!(!self.rooted(), "Can't double-root a Gc"); @@ -403,7 +412,7 @@ impl std::convert::AsRef for Gc { /// A weak Garbage Collected pointer for an immutable value pub struct WeakGc { ptr_root: Cell>>, - strong_ref_check: Cell, + is_live: Cell, marker: PhantomData>, } @@ -422,7 +431,7 @@ impl WeakGc { (*ptr.as_ptr()).value().unroot(); let weak_gc = WeakGc { ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - strong_ref_check: Cell::new(true), + is_live: Cell::new(true), marker: PhantomData, }; weak_gc.set_root(); @@ -469,7 +478,7 @@ impl WeakGc { impl WeakGc { pub fn value(&self) -> Option<&T> { - if self.strong_ref_check.get() { + if self.is_live.get() { Some(self.inner().value()) } else { None @@ -477,7 +486,7 @@ impl WeakGc { } pub fn has_strong_refs(&self) -> bool { - self.strong_ref_check.get() + self.is_live.get() } } @@ -488,10 +497,15 @@ unsafe impl Trace for WeakGc { unsafe fn trace(&self) { // Set the strong reference here to false in the case that a trace has run and no // strong refs exist. - self.strong_ref_check.set(self.inner().check_strong_refs()); self.inner().trace_inner(); } + unsafe fn weak_trace(&self) -> bool { + let marked = self.inner().weak_trace(); + self.is_live.set(marked); + marked + } + #[inline] unsafe fn root(&self) { assert!(!self.rooted(), "Can't double-root a WeakGc"); @@ -519,7 +533,7 @@ impl Clone for WeakGc { self.inner().root_weakly(); let weak_gc = WeakGc { ptr_root: Cell::new(self.ptr_root.get()), - strong_ref_check: Cell::new(self.strong_ref_check.get()), + is_live: Cell::new(self.is_live.get()), marker: PhantomData, }; weak_gc.set_root(); @@ -528,15 +542,6 @@ impl Clone for WeakGc { } } -impl Drop for WeakGc { - #[inline] - fn drop(&mut self) { - if self.rooted() { - self.inner().unroot_weakly() - } - } -} - impl Deref for WeakGc { type Target = T; @@ -894,6 +899,14 @@ unsafe impl Trace for GcCell { } } + #[inline] + unsafe fn weak_trace(&self) -> bool { + match self.flags.get().borrowed() { + BorrowState::Writing => false, + _ => (*self.cell.get()).weak_trace(), + } + } + unsafe fn root(&self) { assert!(!self.flags.get().rooted(), "Can't root a GcCell twice!"); self.flags.set(self.flags.get().set_rooted(true)); diff --git a/gc/src/trace.rs b/gc/src/trace.rs index c881bfc..cd477ec 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -24,6 +24,9 @@ pub unsafe trait Trace: Finalize { /// Marks all contained `Gc`s. unsafe fn trace(&self); + /// Returns true if a marked `Gc` is found + unsafe fn weak_trace(&self) -> bool; + /// Increments the root-count of all contained `Gc`s. unsafe fn root(&self); @@ -44,6 +47,10 @@ macro_rules! unsafe_empty_trace { #[inline] unsafe fn trace(&self) {} #[inline] + unsafe fn weak_trace(&self) -> bool { + false + } + #[inline] unsafe fn root(&self) {} #[inline] unsafe fn unroot(&self) {} @@ -61,7 +68,7 @@ macro_rules! unsafe_empty_trace { /// correct method on the argument. #[macro_export] macro_rules! custom_trace { - ($this:ident, $body:expr) => { + ($this:ident, $body:expr, $weak_body:expr) => { #[inline] unsafe fn trace(&self) { #[inline] @@ -72,6 +79,15 @@ macro_rules! custom_trace { $body } #[inline] + unsafe fn weak_trace(&self) -> bool { + #[inline] + unsafe fn mark(it: &T) -> bool { + $crate::Trace::weak_trace(it) + } + let $this = self; + $weak_body + } + #[inline] unsafe fn root(&self) { #[inline] unsafe fn mark(it: &T) { @@ -166,11 +182,15 @@ simple_empty_finalize_trace![ impl Finalize for [T; N] {} unsafe impl Trace for [T; N] { - custom_trace!(this, { - for v in this { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this { + mark(v); + } + }, + this.into_iter().any(|v| mark(v)) + ); } macro_rules! fn_finalize_trace_one { @@ -207,6 +227,15 @@ macro_rules! tuple_finalize_trace { unsafe { $(mark($args);)* } } avoid_lints(this) + }, { + #[allow(non_snake_case, unused_unsafe)] + fn avoid_lints<$($args: $crate::Trace),*>(&($(ref $args,)*): &($($args,)*)) -> bool { + unsafe { + let marked = $(mark($args);)* + marked + } + } + avoid_lints(this) }); } } @@ -239,118 +268,183 @@ type_arg_tuple_based_finalize_trace_impls![ impl Finalize for Rc {} unsafe impl Trace for Rc { - custom_trace!(this, { - mark(&**this); - }); + custom_trace!( + this, + { + mark(&**this); + }, + mark(&**this) + ); } impl Finalize for Rc<[T]> {} unsafe impl Trace for Rc<[T]> { - custom_trace!(this, { - for e in this.iter() { - mark(e); - } - }); + custom_trace!( + this, + { + for e in this.iter() { + mark(e); + } + }, + this.iter().any(|e| mark(e)) + ); } impl Finalize for Box {} unsafe impl Trace for Box { - custom_trace!(this, { - mark(&**this); - }); + custom_trace!( + this, + { + mark(&**this); + }, + mark(&**this) + ); } impl Finalize for Box<[T]> {} unsafe impl Trace for Box<[T]> { - custom_trace!(this, { - for e in this.iter() { - mark(e); - } - }); + custom_trace!( + this, + { + for e in this.iter() { + mark(e); + } + }, + this.iter().any(|e| mark(e)) + ); } impl Finalize for Vec {} unsafe impl Trace for Vec { - custom_trace!(this, { - for e in this { - mark(e); - } - }); + custom_trace!( + this, + { + for e in this { + mark(e); + } + }, + this.into_iter().any(|e| mark(e)) + ); } impl Finalize for Option {} unsafe impl Trace for Option { - custom_trace!(this, { - if let Some(ref v) = *this { - mark(v); + custom_trace!( + this, + { + if let Some(ref v) = *this { + mark(v); + } + }, + { + let marked = if let Some(ref v) = *this { + mark(v) + } else { + false + }; + marked } - }); + ); } impl Finalize for Result {} unsafe impl Trace for Result { - custom_trace!(this, { - match *this { - Ok(ref v) => mark(v), - Err(ref v) => mark(v), + custom_trace!( + this, + { + match *this { + Ok(ref v) => mark(v), + Err(ref v) => mark(v), + } + }, + { + let marked = match *this { + Ok(ref v) => mark(v), + Err(ref v) => mark(v), + }; + marked } - }); + ); } impl Finalize for BinaryHeap {} unsafe impl Trace for BinaryHeap { - custom_trace!(this, { - for v in this.iter() { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this.iter() { + mark(v); + } + }, + this.iter().any(|v| mark(v)) + ); } impl Finalize for BTreeMap {} unsafe impl Trace for BTreeMap { - custom_trace!(this, { - for (k, v) in this { - mark(k); - mark(v); - } - }); + custom_trace!( + this, + { + for (k, v) in this { + mark(k); + mark(v); + } + }, + this.iter().any(|(k, v)| mark(k) | mark(v)) + ); } impl Finalize for BTreeSet {} unsafe impl Trace for BTreeSet { - custom_trace!(this, { - for v in this { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this { + mark(v); + } + }, + this.iter().any(|v| mark(v)) + ); } impl Finalize for HashMap {} unsafe impl Trace for HashMap { - custom_trace!(this, { - for (k, v) in this.iter() { - mark(k); - mark(v); - } - }); + custom_trace!( + this, + { + for (k, v) in this.iter() { + mark(k); + mark(v); + } + }, + this.into_iter().any(|(k, v)| mark(k) | mark(v)) + ); } impl Finalize for HashSet {} unsafe impl Trace for HashSet { - custom_trace!(this, { - for v in this.iter() { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this.iter() { + mark(v); + } + }, + this.iter().any(|v| mark(v)) + ); } impl Finalize for LinkedList {} unsafe impl Trace for LinkedList { - custom_trace!(this, { - for v in this.iter() { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this.iter() { + mark(v); + } + }, + this.iter().any(|v| mark(v)) + ); } impl Finalize for PhantomData {} @@ -360,11 +454,15 @@ unsafe impl Trace for PhantomData { impl Finalize for VecDeque {} unsafe impl Trace for VecDeque { - custom_trace!(this, { - for v in this.iter() { - mark(v); - } - }); + custom_trace!( + this, + { + for v in this.iter() { + mark(v); + } + }, + this.iter().any(|v| mark(v)) + ); } impl<'a, T: ToOwned + Trace + ?Sized> Finalize for Cow<'a, T> {} @@ -372,9 +470,20 @@ unsafe impl<'a, T: ToOwned + Trace + ?Sized> Trace for Cow<'a, T> where T::Owned: Trace, { - custom_trace!(this, { - if let Cow::Owned(ref v) = this { - mark(v); + custom_trace!( + this, + { + if let Cow::Owned(ref v) = this { + mark(v); + } + }, + { + let marked = if let Cow::Owned(ref v) = this { + mark(v) + } else { + false + }; + marked } - }); + ); } diff --git a/gc/tests/gc_semantics.rs b/gc/tests/gc_semantics.rs index 83fcb90..d4c65b4 100644 --- a/gc/tests/gc_semantics.rs +++ b/gc/tests/gc_semantics.rs @@ -7,6 +7,7 @@ use std::thread::LocalKey; #[derive(PartialEq, Eq, Debug, Clone, Copy)] struct GcWatchFlags { trace: i32, + weak_trace: i32, root: i32, unroot: i32, drop: i32, @@ -14,9 +15,17 @@ struct GcWatchFlags { } impl GcWatchFlags { - fn new(trace: i32, root: i32, unroot: i32, drop: i32, finalize: i32) -> GcWatchFlags { + fn new( + trace: i32, + weak_trace: i32, + root: i32, + unroot: i32, + drop: i32, + finalize: i32, + ) -> GcWatchFlags { GcWatchFlags { trace, + weak_trace, root, unroot, drop, @@ -27,6 +36,7 @@ impl GcWatchFlags { fn zero() -> Cell { Cell::new(GcWatchFlags { trace: 0, + weak_trace: 0, root: 0, unroot: 0, drop: 0, @@ -65,6 +75,14 @@ unsafe impl Trace for GcWatch { f.set(of); }); } + unsafe fn weak_trace(&self) -> bool { + self.0.with(|f| { + let mut of = f.get(); + of.weak_trace += 1; + f.set(of) + }); + false + } unsafe fn root(&self) { self.0.with(|f| { let mut of = f.get(); @@ -98,14 +116,14 @@ fn basic_allocate() { { let _gced_val = Gc::new(GcWatch(&FLAGS)); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 1, 0, 0))); force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); } - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 1, 1))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 1, 1))); } #[test] @@ -119,49 +137,49 @@ fn basic_cycle_allocate() { watch: GcWatch(&FLAGS1), cycle: GcCell::new(None), }); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 1, 0, 0))); let node2 = Gc::new(GcWatchCycle { watch: GcWatch(&FLAGS2), cycle: GcCell::new(Some(node1.clone())), }); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 1, 0, 0))); force_collect(); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); // Move node2 into the cycleref { *node1.cycle.borrow_mut() = Some(node2); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); force_collect(); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); } - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); force_collect(); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 0, 0))); } - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 0, 0))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 0, 0))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 0, 0))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 0, 0))); force_collect(); - FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 1, 1))); - FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 1, 1))); + FLAGS1.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 1, 1))); + FLAGS2.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 0, 1, 1, 1))); } #[test] @@ -171,74 +189,74 @@ fn gccell_rooting() { { let cell = GcCell::new(GcWatch(&FLAGS)); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); { // Borrow it let _borrowed = cell.borrow(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); // Shared borrows can happen multiple times in one scope let _borrowed2 = cell.borrow(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); } - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); { // Borrow it mutably now let _borrowed = cell.borrow_mut(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); } - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 0, 0, 0))); // Put it in a gc (should unroot the GcWatch) let gc_wrapper = Gc::new(cell); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(0, 0, 0, 1, 0, 0))); // It should be traced by the GC force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); { // Borrow it let _borrowed = gc_wrapper.borrow(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); // Shared borrows can happen multiple times in one scope let _borrowed2 = gc_wrapper.borrow(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(1, 0, 0, 1, 0, 0))); // It should be traced by the GC force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); } - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 0, 1, 0, 0))); { // Borrow it mutably now - this should root the GcWatch let _borrowed = gc_wrapper.borrow_mut(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 1, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 1, 0, 0))); // It shouldn't be traced by the GC (as it's owned by the GcCell) // If it had rootable members, they would be traced by the GC force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 1, 1, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 1, 0, 0))); } // Dropping the borrow should unroot it again - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 1, 2, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(2, 0, 1, 2, 0, 0))); // It should be traced by the GC force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 1, 2, 0, 0))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 2, 0, 0))); } // It should be collected by the GC force_collect(); - FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 1, 2, 1, 1))); + FLAGS.with(|f| assert_eq!(f.get(), GcWatchFlags::new(3, 0, 1, 2, 1, 1))); } #[cfg(feature = "nightly")] diff --git a/gc/tests/trace_impl.rs b/gc/tests/trace_impl.rs index c59783f..77d7e08 100644 --- a/gc/tests/trace_impl.rs +++ b/gc/tests/trace_impl.rs @@ -16,6 +16,9 @@ unsafe impl Trace for Foo { *m += 1; }) } + unsafe fn weak_trace(&self) -> bool { + false + } unsafe fn root(&self) {} unsafe fn unroot(&self) {} fn finalize_glue(&self) {} diff --git a/gc_derive/src/lib.rs b/gc_derive/src/lib.rs index bedc924..9539835 100644 --- a/gc_derive/src/lib.rs +++ b/gc_derive/src/lib.rs @@ -11,6 +11,7 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { .any(|attr| attr.path.is_ident("unsafe_ignore_trace")) }); let trace_body = s.each(|bi| quote!(mark(#bi))); + let weak_trace_body = s.fold(false, |acc, bi| quote! { #acc || return mark(#bi)}); s.add_bounds(AddBounds::Fields); let trace_impl = s.unsafe_bound_impl( @@ -24,6 +25,14 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { } match *self { #trace_body } } + #[inline] unsafe fn weak_trace(&self) -> bool { + #[allow(dead_code, unreachable_code)] + #[inline] + unsafe fn mark(it: &T) -> bool { + ::gc::Trace::weak_trace(it) + } + match *self { #weak_trace_body } + } #[inline] unsafe fn root(&self) { #[allow(dead_code)] #[inline] From c8838e95eb770a626ca05a90b9bcede36107f4b2 Mon Sep 17 00:00:00 2001 From: nekevss Date: Fri, 14 Oct 2022 16:29:28 -0400 Subject: [PATCH 05/10] Rename gcbox weak_trace to weak_trace_inner --- gc/src/gc.rs | 4 ++-- gc/src/lib.rs | 4 ++-- gc/src/trace.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gc/src/gc.rs b/gc/src/gc.rs index dd061a5..2d342d0 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -180,7 +180,7 @@ impl GcBox { } /// Trace inner data - pub(crate) unsafe fn weak_trace(&self) -> bool { + pub(crate) unsafe fn weak_trace_inner(&self) -> bool { let marked = self.data.weak_trace(); if marked { self.header.mark(); @@ -250,7 +250,7 @@ fn collect_garbage(st: &mut GcState) { // Evaluate any Weak `GcBox` to determine if it holds a reachable property if weak_nodes.len() > 0 { for node in weak_nodes { - let _weak_check = (*node.as_ptr()).weak_trace(); + let _weak_check = (*node.as_ptr()).weak_trace_inner(); } } diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 0564d2c..f2facbe 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -242,7 +242,7 @@ unsafe impl Trace for Gc { unsafe fn weak_trace(&self) -> bool { let marked = match self.inner().is_marked() { true => self.inner().is_marked(), - false => self.inner().weak_trace(), + false => self.inner().weak_trace_inner(), }; marked } @@ -501,7 +501,7 @@ unsafe impl Trace for WeakGc { } unsafe fn weak_trace(&self) -> bool { - let marked = self.inner().weak_trace(); + let marked = self.inner().weak_trace_inner(); self.is_live.set(marked); marked } diff --git a/gc/src/trace.rs b/gc/src/trace.rs index cd477ec..7724af0 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -189,7 +189,7 @@ unsafe impl Trace for [T; N] { mark(v); } }, - this.into_iter().any(|v| mark(v)) + this.into_iter().any(|v| mark(v)) ); } From f3c96074ad38062d70c699f3987f67082e725956 Mon Sep 17 00:00:00 2001 From: nekevss Date: Mon, 17 Oct 2022 18:22:08 -0400 Subject: [PATCH 06/10] Add Ephemeron, implement ephemeron aware mark, and wrapping types --- gc/src/gc.rs | 279 +++++++++++++++++++-------------- gc/src/lib.rs | 295 +++++------------------------------ gc/src/trace.rs | 148 +++++++++++++----- gc/src/weak/ephemeron.rs | 234 +++++++++++++++++++++++++++ gc/src/weak/mod.rs | 23 +++ gc/src/weak/pair.rs | 273 ++++++++++++++++++++++++++++++++ gc/src/weak/weak_gc.rs | 264 +++++++++++++++++++++++++++++++ gc/tests/derive_bounds.rs | 2 +- gc/tests/finalize.rs | 2 +- gc/tests/gc_semantics.rs | 8 +- gc/tests/gymnastics_cycle.rs | 2 +- gc/tests/trace_impl.rs | 5 +- gc/tests/unsized.rs | 2 +- gc/tests/weak_gc.rs | 8 +- gc_derive/Cargo.toml | 2 +- gc_derive/src/lib.rs | 11 +- 16 files changed, 1122 insertions(+), 436 deletions(-) create mode 100644 gc/src/weak/ephemeron.rs create mode 100644 gc/src/weak/mod.rs create mode 100644 gc/src/weak/pair.rs create mode 100644 gc/src/weak/weak_gc.rs diff --git a/gc/src/gc.rs b/gc/src/gc.rs index 2d342d0..44fe6c4 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -1,9 +1,11 @@ use crate::trace::Trace; +use crate::weak::Ephemeron; +use crate::Finalize; use std::cell::{Cell, RefCell}; use std::mem; use std::ptr::{self, NonNull}; -struct GcState { +pub(crate) struct GcState { stats: GcStats, config: GcConfig, boxes_start: Cell>>>, @@ -45,22 +47,46 @@ thread_local!(static GC_STATE: RefCell = RefCell::new(GcState { boxes_start: Cell::new(None), })); +pub enum GcBoxType { + Standard, + Weak, + Ephemeron, +} + const MARK_MASK: usize = 1 << (usize::BITS - 1); const ROOTS_MASK: usize = !MARK_MASK; const ROOTS_MAX: usize = ROOTS_MASK; // max allowed value of roots pub(crate) struct GcBoxHeader { roots: Cell, // high bit is used as mark flag - weak_flag: Cell, + ephemeron_flag: Cell, next: Cell>>>, } impl GcBoxHeader { #[inline] - pub fn new(next: Option>>, weak_flag: bool) -> Self { + pub fn new(next: Option>>) -> Self { GcBoxHeader { roots: Cell::new(1), // unmarked and roots count = 1 - weak_flag: Cell::new(weak_flag), + ephemeron_flag: Cell::new(false), + next: Cell::new(next), + } + } + + #[inline] + pub fn new_ephemeron(next: Option>>) -> Self { + GcBoxHeader { + roots: Cell::new(0), + ephemeron_flag: Cell::new(true), + next: Cell::new(next), + } + } + + #[inline] + pub fn new_weak(next: Option>>) -> Self { + GcBoxHeader { + roots: Cell::new(0), + ephemeron_flag: Cell::new(false), next: Cell::new(next), } } @@ -104,65 +130,17 @@ impl GcBoxHeader { } #[inline] - pub fn set_weak(&self, new_flag: bool) { - self.weak_flag.set(new_flag); - } - - #[inline] - pub fn is_weak(&self) -> bool { - self.weak_flag.get() + pub fn is_ephemeron(&self) -> bool { + self.ephemeron_flag.get() } } #[repr(C)] // to justify the layout computation in Gc::from_raw -pub(crate) struct GcBox { +pub struct GcBox { header: GcBoxHeader, data: T, } -impl GcBox { - /// Allocates a garbage collected `GcBox` on the heap, - /// and appends it to the thread-local `GcBox` chain. - /// - /// A `GcBox` allocated this way starts its life rooted. - pub(crate) fn new(value: T, weak_flag: bool) -> NonNull { - GC_STATE.with(|st| { - let mut st = st.borrow_mut(); - - // XXX We should probably be more clever about collecting - if st.stats.bytes_allocated > st.config.threshold { - collect_garbage(&mut *st); - - if st.stats.bytes_allocated as f64 - > st.config.threshold as f64 * st.config.used_space_ratio - { - // we didn't collect enough, so increase the - // threshold for next time, to avoid thrashing the - // collector too much/behaving quadratically. - st.config.threshold = - (st.stats.bytes_allocated as f64 / st.config.used_space_ratio) as usize - } - } - - let header = GcBoxHeader::new(st.boxes_start.take(), weak_flag); - - let gcbox = Box::into_raw(Box::new(GcBox { - header, - data: value, - })); - - st.boxes_start - .set(Some(unsafe { NonNull::new_unchecked(gcbox) })); - - // We allocated some bytes! Let's record it - st.stats.bytes_allocated += mem::size_of::>(); - - // Return the pointer to the newly allocated data - unsafe { NonNull::new_unchecked(gcbox) } - }) - } -} - impl GcBox { /// Returns `true` if the two references refer to the same `GcBox`. pub(crate) fn ptr_eq(this: &GcBox, other: &GcBox) -> bool { @@ -173,19 +151,15 @@ impl GcBox { /// Marks this `GcBox` and marks through its data. pub(crate) unsafe fn trace_inner(&self) { - if !self.header.is_marked() { + if !self.header.is_marked() && !self.header.is_ephemeron() { self.header.mark(); self.data.trace(); } } /// Trace inner data - pub(crate) unsafe fn weak_trace_inner(&self) -> bool { - let marked = self.data.weak_trace(); - if marked { - self.header.mark(); - } - marked + pub(crate) unsafe fn weak_trace_inner(&self, queue: &mut Vec>>) { + self.data.weak_trace(queue); } /// Increases the root count on this `GcBox`. @@ -213,13 +187,52 @@ impl GcBox { pub(crate) fn is_marked(&self) -> bool { self.header.is_marked() } +} - pub(crate) fn root_weakly(&self) { - self.header.set_weak(true) - } +impl GcBox { + /// Allocates a garbage collected `GcBox` on the heap, + /// and appends it to the thread-local `GcBox` chain. + /// + /// A `GcBox` allocated this way starts its life rooted. + pub(crate) fn new(value: T, box_type: GcBoxType) -> NonNull { + GC_STATE.with(|st| { + let mut st = st.borrow_mut(); + + // XXX We should probably be more clever about collecting + if st.stats.bytes_allocated > st.config.threshold { + collect_garbage(&mut *st); + + if st.stats.bytes_allocated as f64 + > st.config.threshold as f64 * st.config.used_space_ratio + { + // we didn't collect enough, so increase the + // threshold for next time, to avoid thrashing the + // collector too much/behaving quadratically. + st.config.threshold = + (st.stats.bytes_allocated as f64 / st.config.used_space_ratio) as usize + } + } + + let header = match box_type { + GcBoxType::Standard => GcBoxHeader::new(st.boxes_start.take()), + GcBoxType::Weak => GcBoxHeader::new_weak(st.boxes_start.take()), + GcBoxType::Ephemeron => GcBoxHeader::new_ephemeron(st.boxes_start.take()), + }; + + let gcbox = Box::into_raw(Box::new(GcBox { + header, + data: value, + })); - pub(crate) fn unroot_weakly(&self) { - self.header.set_weak(false); + st.boxes_start + .set(Some(unsafe { NonNull::new_unchecked(gcbox) })); + + // We allocated some bytes! Let's record it + st.stats.bytes_allocated += mem::size_of::>(); + + // Return the pointer to the newly allocated data + unsafe { NonNull::new_unchecked(gcbox) } + }) } } @@ -227,74 +240,104 @@ impl GcBox { fn collect_garbage(st: &mut GcState) { st.stats.collections_performed += 1; - struct Unmarked<'a> { - incoming: &'a Cell>>>, - this: NonNull>, - } - unsafe fn mark(head: &Cell>>>) -> Vec> { + unsafe fn mark( + head: &Cell>>>, + ) -> Vec>> { // Walk the tree, tracing and marking the nodes - let mut weak_nodes = Vec::new(); - let mut mark_head = head.get(); - while let Some(node) = mark_head { - if (*node.as_ptr()).header.roots() > 0 { - (*node.as_ptr()).trace_inner(); - } - - if (*node.as_ptr()).header.is_weak() { - weak_nodes.push(node); + let mut finalize = Vec::new(); + let mut ephemeron_queue = Vec::new(); + let mut mark_head = head; + while let Some(node) = mark_head.get() { + if (*node.as_ptr()).header.is_ephemeron() { + ephemeron_queue.push(node); + } else { + if (*node.as_ptr()).header.roots() > 0 { + (*node.as_ptr()).trace_inner(); + } else { + finalize.push(node) + } } - - mark_head = (*node.as_ptr()).header.next.get(); + mark_head = &(*node.as_ptr()).header.next; } - // Evaluate any Weak `GcBox` to determine if it holds a reachable property - if weak_nodes.len() > 0 { - for node in weak_nodes { - let _weak_check = (*node.as_ptr()).weak_trace_inner(); + // Ephemeron Evaluation + if !ephemeron_queue.is_empty() { + loop { + let mut reachable_nodes = Vec::new(); + let mut other_nodes = Vec::new(); + // iterate through ephemeron queue, sorting nodes by whether they + // are reachable or unreachable + for node in ephemeron_queue { + if (*node.as_ptr()).data.is_marked_ephemeron() { + (*node.as_ptr()).header.mark(); + reachable_nodes.push(node); + } else { + other_nodes.push(node); + } + } + // Replace the old queue with the unreachable + ephemeron_queue = other_nodes; + + // If reachable nodes is not empty, trace values. If it is empty, + // break from the loop + if !reachable_nodes.is_empty() { + // iterate through reachable nodes and trace their values, + // enqueuing any ephemeron that is found during the trace + for node in reachable_nodes { + (*node.as_ptr()).weak_trace_inner(&mut ephemeron_queue) + } + } else { + break; + } } } - // Collect a vector of all of the nodes which were not marked, - // and unmark the ones which were. - let mut unmarked = Vec::new(); - let mut unmark_head = head; - while let Some(node) = unmark_head.get() { - if (*node.as_ptr()).header.is_marked() { - (*node.as_ptr()).header.unmark(); - } else { - unmarked.push(Unmarked { - incoming: unmark_head, - this: node, - }); + // Any left over nodes in the ephemeron queue at this point are + // unreachable and need to be notified/finalized. + finalize.extend(ephemeron_queue); + + finalize + } + + unsafe fn finalize(finalize_vec: Vec>>) { + for node in finalize_vec { + // We double check that the unreachable nodes are actually unreachable + // prior to finalization as they could have been marked by a different + // trace after initially being added to the queue + if !(*node.as_ptr()).header.is_marked() { + Finalize::finalize(&(*node.as_ptr()).data) } - unmark_head = &(*node.as_ptr()).header.next; } - unmarked } - unsafe fn sweep(finalized: Vec>, bytes_allocated: &mut usize) { + unsafe fn sweep(head: &Cell>>>, bytes_allocated: &mut usize) { let _guard = DropGuard::new(); - for node in finalized.into_iter().rev() { - if (*node.this.as_ptr()).header.is_marked() { - continue; + + let mut sweep_head = head; + while let Some(node) = sweep_head.get() { + if (*node.as_ptr()).header.is_marked() { + (*node.as_ptr()).header.unmark(); + sweep_head = &(*node.as_ptr()).header.next; + } else { + let unmarked_node = Box::from_raw(node.as_ptr()); + *bytes_allocated -= mem::size_of_val::>(&*unmarked_node); + sweep_head.set(unmarked_node.header.next.take()); } - let incoming = node.incoming; - let node = Box::from_raw(node.this.as_ptr()); - *bytes_allocated -= mem::size_of_val::>(&*node); - incoming.set(node.header.next.take()); } } unsafe { - let unmarked = mark(&st.boxes_start); - if unmarked.is_empty() { - return; - } - for node in &unmarked { - Trace::finalize_glue(&(*node.this.as_ptr()).data); - } - mark(&st.boxes_start); - sweep(unmarked, &mut st.stats.bytes_allocated); + // Run mark and return vector of nonreachable porperties + let unreachable_nodes = mark(&st.boxes_start); + // Finalize the unreachable properties + finalize(unreachable_nodes); + // Run mark again to mark any nodes that are resurrected by their finalizer + // + // At this point, _f should be filled with all nodes that are unreachable and + // have already been finalized, so they can be ignored. + let _f = mark(&st.boxes_start); + // Run sweep: unmarking all marked nodes and freeing any unmarked nodes + sweep(&st.boxes_start, &mut st.stats.bytes_allocated); } } diff --git a/gc/src/lib.rs b/gc/src/lib.rs index f2facbe..aecf1cf 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -6,7 +6,7 @@ #![cfg_attr(feature = "nightly", feature(coerce_unsized, unsize))] -use crate::gc::{GcBox, GcBoxHeader}; +use crate::gc::{GcBox, GcBoxHeader, GcBoxType}; use std::alloc::Layout; use std::cell::{Cell, UnsafeCell}; use std::cmp::Ordering; @@ -27,10 +27,22 @@ mod gc; #[cfg(feature = "serde")] mod serde; mod trace; +pub mod weak; + +pub use weak::{WeakGc, WeakPair}; #[cfg(feature = "derive")] pub use gc_derive::{Finalize, Trace}; +/// `derive_prelude` is a quick prelude that imports +/// `Finalize`, `Trace`, and `GcPointer` for implementing +/// the derive +#[cfg(feature = "derive")] +pub mod derive_prelude { + pub use crate::GcPointer; + pub use gc_derive::{Finalize, Trace}; +} + // We re-export the Trace method, as well as some useful internal methods for // managing collections or configuring the garbage collector. pub use crate::gc::{finalizer_safe, force_collect}; @@ -41,6 +53,8 @@ pub use crate::gc::{configure, GcConfig}; #[cfg(feature = "unstable-stats")] pub use crate::gc::{stats, GcStats}; +pub type GcPointer = NonNull>; + //////// // Gc // //////// @@ -76,7 +90,7 @@ impl Gc { unsafe { // Allocate the memory for the object - let ptr = GcBox::new(value, false); + let ptr = GcBox::new(value, GcBoxType::Standard); // When we create a Gc, all pointers which have been moved to the // heap no longer need to be rooted, so we unroot them. @@ -99,7 +113,9 @@ impl Gc { } /// Returns the given pointer with its root bit cleared. -unsafe fn clear_root_bit(ptr: NonNull>) -> NonNull> { +pub(crate) unsafe fn clear_root_bit( + ptr: NonNull>, +) -> NonNull> { let ptr = ptr.as_ptr(); let data = ptr as *mut u8; let addr = data as isize; @@ -218,13 +234,8 @@ impl Gc { #[inline] pub fn clone_weak_ref(&self) -> WeakGc { unsafe { - self.inner().root_weakly(); - let weak_gc = WeakGc { - ptr_root: Cell::new(self.ptr_root.get()), - is_live: Cell::new(true), - marker: PhantomData, - }; - weak_gc.set_root(); + // An Ephemeron is not rooted. + let weak_gc = WeakGc::from_gc_box(self.ptr_root.get()); weak_gc } } @@ -239,12 +250,13 @@ unsafe impl Trace for Gc { } #[inline] - unsafe fn weak_trace(&self) -> bool { - let marked = match self.inner().is_marked() { - true => self.inner().is_marked(), - false => self.inner().weak_trace_inner(), - }; - marked + unsafe fn is_marked_ephemeron(&self) -> bool { + false + } + + #[inline] + unsafe fn weak_trace(&self, queue: &mut Vec) { + self.inner().weak_trace_inner(queue); } #[inline] @@ -405,244 +417,6 @@ impl std::convert::AsRef for Gc { } } -//////////// -// WeakGc // -//////////// - -/// A weak Garbage Collected pointer for an immutable value -pub struct WeakGc { - ptr_root: Cell>>, - is_live: Cell, - marker: PhantomData>, -} - -impl WeakGc { - /// Crate a new Weak type Gc - /// - /// This method can trigger a collection - pub fn new(value: T) -> Self { - assert!(mem::align_of::>() > 1); - - unsafe { - // Allocate the memory for the object - let ptr = GcBox::new(value, true); - // We assume when creating a WeakGc that strong_ref_check is true until - // told otherwise by trace - (*ptr.as_ptr()).value().unroot(); - let weak_gc = WeakGc { - ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - is_live: Cell::new(true), - marker: PhantomData, - }; - weak_gc.set_root(); - weak_gc - } - } -} - -impl WeakGc { - fn rooted(&self) -> bool { - self.ptr_root.get().as_ptr() as *mut u8 as usize & 1 != 0 - } - - unsafe fn set_root(&self) { - let ptr = self.ptr_root.get().as_ptr(); - let data = ptr as *mut u8; - let addr = data as isize; - let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); - self.ptr_root.set(NonNull::new_unchecked(ptr)); - } - - unsafe fn clear_root(&self) { - self.ptr_root.set(clear_root_bit(self.ptr_root.get())); - } - - #[inline] - fn inner_ptr(&self) -> *mut GcBox { - // If we are currently in the dropping phase of garbage collection, - // it would be undefined behavior to dereference this pointer. - // By opting into `Trace` you agree to not dereference this pointer - // within your drop method, meaning that it should be safe. - // - // This assert exists just in case. - assert!(finalizer_safe()); - - unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() } - } - - #[inline] - fn inner(&self) -> &GcBox { - unsafe { &*self.inner_ptr() } - } -} - -impl WeakGc { - pub fn value(&self) -> Option<&T> { - if self.is_live.get() { - Some(self.inner().value()) - } else { - None - } - } - - pub fn has_strong_refs(&self) -> bool { - self.is_live.get() - } -} - -impl Finalize for WeakGc {} - -unsafe impl Trace for WeakGc { - #[inline] - unsafe fn trace(&self) { - // Set the strong reference here to false in the case that a trace has run and no - // strong refs exist. - self.inner().trace_inner(); - } - - unsafe fn weak_trace(&self) -> bool { - let marked = self.inner().weak_trace_inner(); - self.is_live.set(marked); - marked - } - - #[inline] - unsafe fn root(&self) { - assert!(!self.rooted(), "Can't double-root a WeakGc"); - self.inner().root_weakly(); - self.set_root(); - } - - #[inline] - unsafe fn unroot(&self) { - assert!(self.rooted(), "Can't double-unroot a WeakGc"); - self.inner().unroot_weakly(); - self.clear_root(); - } - - #[inline] - fn finalize_glue(&self) { - Finalize::finalize(self) - } -} - -impl Clone for WeakGc { - #[inline] - fn clone(&self) -> Self { - unsafe { - self.inner().root_weakly(); - let weak_gc = WeakGc { - ptr_root: Cell::new(self.ptr_root.get()), - is_live: Cell::new(self.is_live.get()), - marker: PhantomData, - }; - weak_gc.set_root(); - weak_gc - } - } -} - -impl Deref for WeakGc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().value() - } -} - -impl Default for WeakGc { - #[inline] - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl PartialEq for WeakGc { - #[inline(always)] - fn eq(&self, other: &Self) -> bool { - **self == **other - } -} - -impl Eq for WeakGc {} - -impl PartialOrd for WeakGc { - #[inline(always)] - fn partial_cmp(&self, other: &Self) -> Option { - (**self).partial_cmp(&**other) - } - - #[inline(always)] - fn lt(&self, other: &Self) -> bool { - **self < **other - } - - #[inline(always)] - fn le(&self, other: &Self) -> bool { - **self <= **other - } - - #[inline(always)] - fn gt(&self, other: &Self) -> bool { - **self > **other - } - - #[inline(always)] - fn ge(&self, other: &Self) -> bool { - **self >= **other - } -} - -impl Ord for WeakGc { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - (**self).cmp(&**other) - } -} - -impl Hash for WeakGc { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } -} - -impl Display for WeakGc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&**self, f) - } -} - -impl Debug for WeakGc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt(&**self, f) - } -} - -impl fmt::Pointer for WeakGc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.inner(), f) - } -} - -impl From for WeakGc { - fn from(t: T) -> Self { - Self::new(t) - } -} - -impl std::borrow::Borrow for WeakGc { - fn borrow(&self) -> &T { - &**self - } -} - -impl std::convert::AsRef for WeakGc { - fn as_ref(&self) -> &T { - &**self - } -} - //////////// // GcCell // //////////// @@ -900,10 +674,15 @@ unsafe impl Trace for GcCell { } #[inline] - unsafe fn weak_trace(&self) -> bool { + unsafe fn is_marked_ephemeron(&self) -> bool { + false + } + + #[inline] + unsafe fn weak_trace(&self, queue: &mut Vec) { match self.flags.get().borrowed() { - BorrowState::Writing => false, - _ => (*self.cell.get()).weak_trace(), + BorrowState::Writing => (), + _ => (*self.cell.get()).weak_trace(queue), } } @@ -1248,7 +1027,7 @@ impl Debug for GcCell { // // 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 { +pub(crate) 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 } diff --git a/gc/src/trace.rs b/gc/src/trace.rs index 7724af0..0ea97ba 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -7,12 +7,13 @@ use std::num::{ NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }; use std::path::{Path, PathBuf}; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, }; +pub use crate::GcPointer; /// The Finalize trait, which needs to be implemented on /// garbage-collected objects to define finalization logic. pub trait Finalize { @@ -24,8 +25,13 @@ pub unsafe trait Trace: Finalize { /// Marks all contained `Gc`s. unsafe fn trace(&self); + /// Checks if an ephemeron's key is marked. + /// + /// Note: value should always be implemented to return false + unsafe fn is_marked_ephemeron(&self) -> bool; + /// Returns true if a marked `Gc` is found - unsafe fn weak_trace(&self) -> bool; + unsafe fn weak_trace(&self, ephemeron_queue: &mut Vec); /// Increments the root-count of all contained `Gc`s. unsafe fn root(&self); @@ -47,10 +53,12 @@ macro_rules! unsafe_empty_trace { #[inline] unsafe fn trace(&self) {} #[inline] - unsafe fn weak_trace(&self) -> bool { + unsafe fn is_marked_ephemeron(&self) -> bool { false } #[inline] + unsafe fn weak_trace(&self, _ephemeron_queue: &mut Vec) {} + #[inline] unsafe fn root(&self) {} #[inline] unsafe fn unroot(&self) {} @@ -68,7 +76,7 @@ macro_rules! unsafe_empty_trace { /// correct method on the argument. #[macro_export] macro_rules! custom_trace { - ($this:ident, $body:expr, $weak_body:expr) => { + ($this:ident, $op:ident, $body:expr, $weak_body:expr) => { #[inline] unsafe fn trace(&self) { #[inline] @@ -79,12 +87,17 @@ macro_rules! custom_trace { $body } #[inline] - unsafe fn weak_trace(&self) -> bool { + unsafe fn is_marked_ephemeron(&self) -> bool { + false + } + #[inline] + unsafe fn weak_trace(&self, queue: &mut Vec) { #[inline] - unsafe fn mark(it: &T) -> bool { - $crate::Trace::weak_trace(it) + unsafe fn mark(it: &T, queue: &mut Vec) { + $crate::Trace::weak_trace(it, queue) } let $this = self; + let $op = queue; $weak_body } #[inline] @@ -184,12 +197,17 @@ impl Finalize for [T; N] {} unsafe impl Trace for [T; N] { custom_trace!( this, + queue, { for v in this { mark(v); } }, - this.into_iter().any(|v| mark(v)) + { + for v in this { + mark(v, queue); + } + } ); } @@ -221,7 +239,7 @@ macro_rules! tuple_finalize_trace { ($($args:ident),*) => { impl<$($args),*> Finalize for ($($args,)*) {} unsafe impl<$($args: $crate::Trace),*> Trace for ($($args,)*) { - custom_trace!(this, { + custom_trace!(this, queue, { #[allow(non_snake_case, unused_unsafe)] fn avoid_lints<$($args: $crate::Trace),*>(&($(ref $args,)*): &($($args,)*)) { unsafe { $(mark($args);)* } @@ -229,13 +247,10 @@ macro_rules! tuple_finalize_trace { avoid_lints(this) }, { #[allow(non_snake_case, unused_unsafe)] - fn avoid_lints<$($args: $crate::Trace),*>(&($(ref $args,)*): &($($args,)*)) -> bool { - unsafe { - let marked = $(mark($args);)* - marked - } + fn avoid_lints<$($args: $crate::Trace),*>(&($(ref $args,)*): &($($args,)*), queue: &mut Vec) { + unsafe { $(mark($args, queue);)* } } - avoid_lints(this) + avoid_lints(this, queue) }); } } @@ -270,10 +285,11 @@ impl Finalize for Rc {} unsafe impl Trace for Rc { custom_trace!( this, + queue, { mark(&**this); }, - mark(&**this) + mark(&**this, queue) ); } @@ -281,12 +297,17 @@ impl Finalize for Rc<[T]> {} unsafe impl Trace for Rc<[T]> { custom_trace!( this, + queue, { for e in this.iter() { mark(e); } }, - this.iter().any(|e| mark(e)) + { + for e in this.iter() { + mark(e, queue); + } + } ); } @@ -294,10 +315,11 @@ impl Finalize for Box {} unsafe impl Trace for Box { custom_trace!( this, + queue, { mark(&**this); }, - mark(&**this) + mark(&**this, queue) ); } @@ -305,12 +327,17 @@ impl Finalize for Box<[T]> {} unsafe impl Trace for Box<[T]> { custom_trace!( this, + queue, { for e in this.iter() { mark(e); } }, - this.iter().any(|e| mark(e)) + { + for e in this.iter() { + mark(e, queue); + } + } ); } @@ -318,12 +345,17 @@ impl Finalize for Vec {} unsafe impl Trace for Vec { custom_trace!( this, + queue, { for e in this { mark(e); } }, - this.into_iter().any(|e| mark(e)) + { + for e in this { + mark(e, queue); + } + } ); } @@ -331,18 +363,16 @@ impl Finalize for Option {} unsafe impl Trace for Option { custom_trace!( this, + queue, { if let Some(ref v) = *this { mark(v); } }, { - let marked = if let Some(ref v) = *this { - mark(v) - } else { - false - }; - marked + if let Some(ref v) = *this { + mark(v, queue) + } } ); } @@ -351,6 +381,7 @@ impl Finalize for Result {} unsafe impl Trace for Result { custom_trace!( this, + queue, { match *this { Ok(ref v) => mark(v), @@ -359,8 +390,8 @@ unsafe impl Trace for Result { }, { let marked = match *this { - Ok(ref v) => mark(v), - Err(ref v) => mark(v), + Ok(ref v) => mark(v, queue), + Err(ref v) => mark(v, queue), }; marked } @@ -371,12 +402,17 @@ impl Finalize for BinaryHeap {} unsafe impl Trace for BinaryHeap { custom_trace!( this, + queue, { for v in this.iter() { mark(v); } }, - this.iter().any(|v| mark(v)) + { + for e in this.iter() { + mark(e, queue); + } + } ); } @@ -384,13 +420,19 @@ impl Finalize for BTreeMap {} unsafe impl Trace for BTreeMap { custom_trace!( this, + queue, { for (k, v) in this { mark(k); mark(v); } }, - this.iter().any(|(k, v)| mark(k) | mark(v)) + { + for (k, v) in this { + mark(k, queue); + mark(v, queue); + } + } ); } @@ -398,12 +440,17 @@ impl Finalize for BTreeSet {} unsafe impl Trace for BTreeSet { custom_trace!( this, + queue, { for v in this { mark(v); } }, - this.iter().any(|v| mark(v)) + { + for v in this { + mark(v, queue); + } + } ); } @@ -411,13 +458,19 @@ impl Finalize for HashMap Trace for HashMap { custom_trace!( this, + queue, { for (k, v) in this.iter() { mark(k); mark(v); } }, - this.into_iter().any(|(k, v)| mark(k) | mark(v)) + { + for (k, v) in this.iter() { + mark(k, queue); + mark(v, queue); + } + } ); } @@ -425,12 +478,17 @@ impl Finalize for HashSet {} unsafe impl Trace for HashSet { custom_trace!( this, + queue, { for v in this.iter() { mark(v); } }, - this.iter().any(|v| mark(v)) + { + for v in this.iter() { + mark(v, queue); + } + } ); } @@ -438,12 +496,17 @@ impl Finalize for LinkedList {} unsafe impl Trace for LinkedList { custom_trace!( this, + queue, { for v in this.iter() { mark(v); } }, - this.iter().any(|v| mark(v)) + { + for v in this.iter() { + mark(v, queue); + } + } ); } @@ -456,12 +519,17 @@ impl Finalize for VecDeque {} unsafe impl Trace for VecDeque { custom_trace!( this, + queue, { for v in this.iter() { mark(v); } }, - this.iter().any(|v| mark(v)) + { + for v in this.iter() { + mark(v, queue); + } + } ); } @@ -472,18 +540,16 @@ where { custom_trace!( this, + queue, { if let Cow::Owned(ref v) = this { mark(v); } }, { - let marked = if let Cow::Owned(ref v) = this { - mark(v) - } else { - false - }; - marked + if let Cow::Owned(ref v) = this { + mark(v, queue) + } } ); } diff --git a/gc/src/weak/ephemeron.rs b/gc/src/weak/ephemeron.rs new file mode 100644 index 0000000..6c95f7d --- /dev/null +++ b/gc/src/weak/ephemeron.rs @@ -0,0 +1,234 @@ +//! This module will implement the internal types GcBox and Ephemeron +use crate::gc::{finalizer_safe, GcBox, GcBoxType}; +use crate::trace::Trace; +use crate::{clear_root_bit, set_data_ptr, Finalize, GcPointer}; +use std::cell::Cell; +use std::mem; +use std::ptr::NonNull; + +/// Implementation of an Ephemeron structure +/// +/// An Ephemeron can be either a WeakPair (Ephemeron) or a WeakBox (Ephemeron) +/// +/// +/// # Tracing with Ephemerons +/// +/// Tracing with ephemerons requires a 3 phase approach: +/// - Phase One: Trace everything up to an ephemeron (queue found ephemerons) +/// - Phase Two: Trace keys of queued ephemerons. If reachable, +/// +/// [Reference]: https://docs.racket-lang.org/reference/ephemerons.html#%28tech._ephemeron%29 +pub struct Ephemeron { + key: Cell>>, + value: Cell>>>, +} + +impl Ephemeron { + pub(crate) fn new_weak(value: T) -> Self { + assert!(mem::align_of::>() > 1); + + unsafe { + let ptr = GcBox::new(value, GcBoxType::Weak); + + let ephem = Ephemeron { + key: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + value: Cell::new(None), + }; + ephem.set_root(); + ephem + } + } + + pub(crate) fn new_weak_pair(key: T, value: Option) -> Self { + assert!(mem::align_of::>() > 1); + + unsafe { + let key_ptr = GcBox::new(key, GcBoxType::Weak); + let value = if let Some(v) = value { + let val_ptr = GcBox::new(v, GcBoxType::Weak); + Cell::new(Some(NonNull::new_unchecked(val_ptr.as_ptr()))) + } else { + Cell::new(None) + }; + + let ephem = Ephemeron { + key: Cell::new(NonNull::new_unchecked(key_ptr.as_ptr())), + value, + }; + ephem.set_root(); + ephem + } + } + + #[inline] + pub(crate) fn set_value(&self, value: Option) { + unsafe { + if let Some(v) = value { + let val_ptr = GcBox::new(v, GcBoxType::Weak); + self.value + .set(Some(NonNull::new_unchecked(val_ptr.as_ptr()))); + } else { + self.value.set(None); + } + } + } +} + +impl Ephemeron { + #[inline] + pub(crate) fn weak_from_gc_box(value: NonNull>) -> Self { + unsafe { + let ephem = Ephemeron { + key: Cell::new(NonNull::new_unchecked(value.as_ptr())), + value: Cell::new(None), + }; + ephem.set_root(); + ephem + } + } + + #[inline] + pub(crate) fn weak_pair_from_gc_boxes( + key: NonNull>, + value: Option>>, + ) -> Self { + unsafe { + let value = if let Some(val) = value { + Cell::new(Some(NonNull::new_unchecked(val.as_ptr()))) + } else { + Cell::new(None) + }; + + let ephem = Ephemeron { + key: Cell::new(NonNull::new_unchecked(key.as_ptr())), + value, + }; + ephem.set_root(); + ephem + } + } + + fn rooted(&self) -> bool { + self.key.get().as_ptr() as *mut u8 as usize & 1 != 0 + } + + unsafe fn set_root(&self) { + let ptr = self.key.get().as_ptr(); + let data = ptr as *mut u8; + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); + self.key.set(NonNull::new_unchecked(ptr)); + } + + unsafe fn clear_root(&self) { + self.key.set(clear_root_bit(self.key.get())); + } + + #[inline] + pub(crate) fn is_marked(&self) -> bool { + self.inner_key().is_marked() + } + + #[inline] + fn inner_key_ptr(&self) -> *mut GcBox { + assert!(finalizer_safe()); + unsafe { clear_root_bit(self.key.get()).as_ptr() } + } + + #[inline] + fn inner_value_ptr(&self) -> Option<*mut GcBox> { + assert!(finalizer_safe()); + + if let Some(gc_box) = self.value.get() { + let val = unsafe { gc_box.as_ptr() }; + Some(val) + } else { + None + } + } + + #[inline] + fn inner_key(&self) -> &GcBox { + unsafe { &*self.inner_key_ptr() } + } + + #[inline] + fn inner_value(&self) -> Option<&GcBox> { + unsafe { + if let Some(inner_value) = self.inner_value_ptr() { + Some(&*inner_value) + } else { + None + } + } + } + + #[inline] + pub fn key_value(&self) -> &T { + self.inner_key().value() + } + + #[inline] + pub fn value(&self) -> Option<&T> { + if let Some(gcbox) = self.inner_value() { + Some(gcbox.value()) + } else { + None + } + } + + #[inline] + unsafe fn weak_trace_key(&self, queue: &mut Vec) { + self.inner_key().weak_trace_inner(queue) + } + + #[inline] + unsafe fn weak_trace_value(&self, queue: &mut Vec) { + if let Some(gcbox) = self.inner_value() { + gcbox.weak_trace_inner(queue) + } + } +} + +impl Finalize for Ephemeron {} + +unsafe impl Trace for Ephemeron { + #[inline] + unsafe fn trace(&self) { + /* An ephemeron is never traced with Phase One Trace */ + /* May be traced in phase 3, so this still may need to be implemented */ + } + + #[inline] + unsafe fn is_marked_ephemeron(&self) -> bool { + self.is_marked() + } + + #[inline] + unsafe fn weak_trace(&self, queue: &mut Vec) { + if self.is_marked() { + self.weak_trace_key(queue); + self.weak_trace_value(queue); + } + } + + #[inline] + unsafe fn root(&self) { + // An ephemeron is never rooted in the GcBoxHeader + assert!(!self.rooted(), "Can't double-root an Ephemeron"); + + self.set_root() + } + + #[inline] + unsafe fn unroot(&self) { + // An ephemeron is never rotted in the GcBoxHeader + assert!(self.rooted(), "Can't double-unroot an Ephemeron"); + self.clear_root(); + } + + #[inline] + fn finalize_glue(&self) { + Finalize::finalize(self) + } +} diff --git a/gc/src/weak/mod.rs b/gc/src/weak/mod.rs new file mode 100644 index 0000000..1f65e0e --- /dev/null +++ b/gc/src/weak/mod.rs @@ -0,0 +1,23 @@ +use crate::gc::GcBox; +pub use crate::gc::{finalizer_safe, force_collect}; +use crate::set_data_ptr; +pub use crate::trace::{Finalize, Trace}; +use std::ptr::NonNull; + +pub(crate) mod ephemeron; +pub mod pair; +pub mod weak_gc; + +pub(crate) use ephemeron::Ephemeron; +pub use pair::WeakPair; +pub use weak_gc::WeakGc; + +pub(crate) unsafe fn clear_root_bit( + ptr: NonNull>>, +) -> NonNull>> { + let ptr = ptr.as_ptr(); + let data = ptr as *mut u8; + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr & !1) - addr)); + NonNull::new_unchecked(ptr) +} diff --git a/gc/src/weak/pair.rs b/gc/src/weak/pair.rs new file mode 100644 index 0000000..a37cb3f --- /dev/null +++ b/gc/src/weak/pair.rs @@ -0,0 +1,273 @@ +pub use crate::gc::{finalizer_safe, force_collect}; +use crate::gc::{GcBox, GcBoxType}; +pub use crate::trace::{Finalize, Trace}; +use crate::weak::{clear_root_bit, Ephemeron}; +use crate::{set_data_ptr, GcPointer}; +use std::cell::Cell; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::mem; +use std::ops::Deref; +use std::ptr::NonNull; +use std::rc::Rc; + +////////////// +// WeakPair // +////////////// + + +// The WeakPair struct is a garbage collected pointer to an Ephemeron +pub struct WeakPair { + ptr_root: Cell>>>, + marker: PhantomData>, +} + +impl WeakPair { + /// Crate a new Weak type Gc + /// + /// This method can trigger a collection + pub fn new(key: T, value: Option) -> Self { + assert!(mem::align_of::>() > 1); + + unsafe { + // Allocate the memory for the object + let eph_value = Ephemeron::new_weak_pair(key, value); + let ptr = GcBox::new(eph_value, GcBoxType::Ephemeron); + + (*ptr.as_ptr()).value().unroot(); + let weak_gc = WeakPair { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } + + #[inline] + pub fn set_value(&self, value: Option) { + self.inner().value().set_value(value) + } +} + +impl WeakPair { + fn rooted(&self) -> bool { + self.ptr_root.get().as_ptr() as *mut u8 as usize & 1 != 0 + } + + unsafe fn set_root(&self) { + let ptr = self.ptr_root.get().as_ptr(); + let data = ptr as *mut u8; + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); + self.ptr_root.set(NonNull::new_unchecked(ptr)); + } + + unsafe fn clear_root(&self) { + self.ptr_root.set(clear_root_bit(self.ptr_root.get())); + } + + #[inline] + fn inner_ptr(&self) -> *mut GcBox> { + // If we are currently in the dropping phase of garbage collection, + // it would be undefined behavior to dereference this pointer. + // By opting into `Trace` you agree to not dereference this pointer + // within your drop method, meaning that it should be safe. + // + // This assert exists just in case. + assert!(finalizer_safe()); + + unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() } + } + + #[inline] + fn inner(&self) -> &GcBox> { + unsafe { &*self.inner_ptr() } + } +} + +impl WeakPair { + #[inline] + pub fn key_value(&self) -> &T { + self.inner().value().key_value() + } + + #[inline] + pub fn value(&self) -> Option<&T> { + self.inner().value().value() + } + + #[inline] + pub fn value_tuple(&self) -> (&T, Option<&T>) { + (self.key_value(), self.value()) + } + + #[inline] + pub(crate) fn from_gc_boxes(key: NonNull>, value: Option>>) -> Self { + unsafe { + let eph = Ephemeron::weak_pair_from_gc_boxes(key, value); + let ptr = GcBox::new(eph, GcBoxType::Ephemeron); + + let weak_gc = WeakPair { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl Finalize for WeakPair {} + +unsafe impl Trace for WeakPair { + #[inline] + unsafe fn trace(&self) { + // Set the strong reference here to false in the case that a trace has run and no + // strong refs exist. + self.inner().trace_inner(); + } + + unsafe fn is_marked_ephemeron(&self) -> bool { + // This is technically an Ephemeron wrapper. + // Returning false to ensure that only an Ephemeron returns true + false + } + + unsafe fn weak_trace(&self, queue: &mut Vec) { + // WeakPair is an Ephemeron wrapper, so we know the inner GcBox must contain an + // an Ephemeron. So we push the Ephemeron onto the Ephemeron queue to be checked + // by the collector + queue.push(self.ptr_root.get()) + } + + #[inline] + unsafe fn root(&self) { + assert!(!self.rooted(), "Can't double-root a WeakPair"); + self.set_root(); + } + + #[inline] + unsafe fn unroot(&self) { + assert!(self.rooted(), "Can't double-unroot a WeakPair"); + self.clear_root(); + } + + #[inline] + fn finalize_glue(&self) { + Finalize::finalize(self) + } +} + +impl Clone for WeakPair { + #[inline] + fn clone(&self) -> Self { + unsafe { + let weak_gc = WeakPair { + ptr_root: Cell::new(self.ptr_root.get()), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl Deref for WeakPair { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().value().key_value() + } +} + +impl Default for WeakPair { + #[inline] + fn default() -> Self { + Self::new(Default::default(), Default::default()) + } +} + +impl PartialEq for WeakPair { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for WeakPair {} + +impl PartialOrd for WeakPair { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + **self < **other + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + **self <= **other + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + **self > **other + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + **self >= **other + } +} + +impl Ord for WeakPair { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl Hash for WeakPair { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl Display for WeakPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +impl Debug for WeakPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl fmt::Pointer for WeakPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.inner(), f) + } +} + +// TODO: implement FROM trait for WeakPair + +impl std::borrow::Borrow for WeakPair { + fn borrow(&self) -> &T { + &**self + } +} + +impl std::convert::AsRef for WeakPair { + fn as_ref(&self) -> &T { + &**self + } +} diff --git a/gc/src/weak/weak_gc.rs b/gc/src/weak/weak_gc.rs new file mode 100644 index 0000000..4cc61e9 --- /dev/null +++ b/gc/src/weak/weak_gc.rs @@ -0,0 +1,264 @@ +pub use crate::gc::{finalizer_safe, force_collect}; +use crate::gc::{GcBox, GcBoxType}; +pub use crate::trace::{Finalize, Trace}; +use crate::weak::{clear_root_bit, Ephemeron}; +use crate::{set_data_ptr, GcPointer}; +use std::cell::Cell; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::mem; +use std::ops::Deref; +use std::ptr::NonNull; +use std::rc::Rc; + +//////////// +// WeakGc // +//////////// + +/// A weak Garbage Collected pointer for an immutable value +/// +/// This implementation uses an Ephemeron as a generalized weak +/// box to trace and sweep the inner values. +pub struct WeakGc { + ptr_root: Cell>>>, + marker: PhantomData>, +} + +impl WeakGc { + /// Crate a new Weak type Gc + /// + /// This method can trigger a collection + pub fn new(value: T) -> Self { + assert!(mem::align_of::>() > 1); + + unsafe { + // Allocate the memory for the object + let eph_value = Ephemeron::new_weak(value); + let ptr = GcBox::new(eph_value, GcBoxType::Ephemeron); + + (*ptr.as_ptr()).value().unroot(); + let weak_gc = WeakGc { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl WeakGc { + fn rooted(&self) -> bool { + self.ptr_root.get().as_ptr() as *mut u8 as usize & 1 != 0 + } + + unsafe fn set_root(&self) { + let ptr = self.ptr_root.get().as_ptr(); + let data = ptr as *mut u8; + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); + self.ptr_root.set(NonNull::new_unchecked(ptr)); + } + + unsafe fn clear_root(&self) { + self.ptr_root.set(clear_root_bit(self.ptr_root.get())); + } + + #[inline] + fn inner_ptr(&self) -> *mut GcBox> { + // If we are currently in the dropping phase of garbage collection, + // it would be undefined behavior to dereference this pointer. + // By opting into `Trace` you agree to not dereference this pointer + // within your drop method, meaning that it should be safe. + // + // This assert exists just in case. + assert!(finalizer_safe()); + + unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() } + } + + #[inline] + fn inner(&self) -> &GcBox> { + unsafe { &*self.inner_ptr() } + } +} + +impl WeakGc { + #[inline] + pub fn value(&self) -> &T { + self.inner().value().key_value() + } + + #[inline] + pub(crate) fn from_gc_box(gc_box: NonNull>) -> Self { + unsafe { + let eph = Ephemeron::weak_from_gc_box(gc_box); + let ptr = GcBox::new(eph, GcBoxType::Ephemeron); + + let weak_gc = WeakGc { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl Finalize for WeakGc {} + +unsafe impl Trace for WeakGc { + #[inline] + unsafe fn trace(&self) { + // Set the strong reference here to false in the case that a trace has run and no + // strong refs exist. + self.inner().trace_inner(); + } + + unsafe fn is_marked_ephemeron(&self) -> bool { + // This is technically an Ephemeron wrapper. + // Returning false to ensure that only an Ephemeron returns true + false + } + + unsafe fn weak_trace(&self, queue: &mut Vec) { + // WeakGc is an Ephemeron wrapper, so we know the inner GcBox must contain an + // an Ephemeron. So we push the Ephemeron onto the Ephemeron queue to be checked + // by the collector + queue.push(self.ptr_root.get()) + } + + #[inline] + unsafe fn root(&self) { + assert!(!self.rooted(), "Can't double-root a WeakGc"); + self.set_root(); + } + + #[inline] + unsafe fn unroot(&self) { + assert!(self.rooted(), "Can't double-unroot a WeakGc"); + self.clear_root(); + } + + #[inline] + fn finalize_glue(&self) { + Finalize::finalize(self) + } +} + +impl Clone for WeakGc { + #[inline] + fn clone(&self) -> Self { + unsafe { + let weak_gc = WeakGc { + ptr_root: Cell::new(self.ptr_root.get()), + marker: PhantomData, + }; + weak_gc.set_root(); + weak_gc + } + } +} + +impl Deref for WeakGc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().value().key_value() + } +} + +impl Default for WeakGc { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl PartialEq for WeakGc { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for WeakGc {} + +impl PartialOrd for WeakGc { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + **self < **other + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + **self <= **other + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + **self > **other + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + **self >= **other + } +} + +impl Ord for WeakGc { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl Hash for WeakGc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl Display for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +impl Debug for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl fmt::Pointer for WeakGc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.inner(), f) + } +} + +impl From for WeakGc { + fn from(t: T) -> Self { + Self::new(t) + } +} + +impl std::borrow::Borrow for WeakGc { + fn borrow(&self) -> &T { + &**self + } +} + +impl std::convert::AsRef for WeakGc { + fn as_ref(&self) -> &T { + &**self + } +} diff --git a/gc/tests/derive_bounds.rs b/gc/tests/derive_bounds.rs index 0ff214c..c35042b 100644 --- a/gc/tests/derive_bounds.rs +++ b/gc/tests/derive_bounds.rs @@ -1,4 +1,4 @@ -use gc::Gc; +use gc::{Gc, GcPointer}; use gc_derive::{Finalize, Trace}; // This impl should *not* require T: Trace. diff --git a/gc/tests/finalize.rs b/gc/tests/finalize.rs index 5b668a6..f90d6af 100644 --- a/gc/tests/finalize.rs +++ b/gc/tests/finalize.rs @@ -1,4 +1,4 @@ -use gc::{Finalize, Trace}; +use gc::{Finalize, GcPointer, Trace}; use gc_derive::{Finalize, Trace}; use std::cell::Cell; diff --git a/gc/tests/gc_semantics.rs b/gc/tests/gc_semantics.rs index d4c65b4..db05650 100644 --- a/gc/tests/gc_semantics.rs +++ b/gc/tests/gc_semantics.rs @@ -1,4 +1,4 @@ -use gc::{force_collect, Finalize, Gc, GcCell, Trace}; +use gc::{force_collect, Finalize, Gc, GcCell, GcPointer, Trace}; use gc_derive::{Finalize, Trace}; use std::cell::Cell; use std::thread::LocalKey; @@ -75,13 +75,15 @@ unsafe impl Trace for GcWatch { f.set(of); }); } - unsafe fn weak_trace(&self) -> bool { + unsafe fn is_marked_ephemeron(&self) -> bool { + false + } + unsafe fn weak_trace(&self, queue: &mut Vec) { self.0.with(|f| { let mut of = f.get(); of.weak_trace += 1; f.set(of) }); - false } unsafe fn root(&self) { self.0.with(|f| { diff --git a/gc/tests/gymnastics_cycle.rs b/gc/tests/gymnastics_cycle.rs index 54e7618..8a4cc6a 100644 --- a/gc/tests/gymnastics_cycle.rs +++ b/gc/tests/gymnastics_cycle.rs @@ -1,4 +1,4 @@ -use gc::{force_collect, Gc, GcCell}; +use gc::{force_collect, Gc, GcCell, GcPointer}; use gc_derive::Trace; use std::cell::Cell; diff --git a/gc/tests/trace_impl.rs b/gc/tests/trace_impl.rs index 77d7e08..3df7fb4 100644 --- a/gc/tests/trace_impl.rs +++ b/gc/tests/trace_impl.rs @@ -4,7 +4,7 @@ use std::rc::Rc; thread_local!(static X: RefCell = RefCell::new(0)); -use gc::Trace; +use gc::{GcPointer, Trace}; #[derive(Copy, Clone, Finalize)] struct Foo; @@ -16,9 +16,10 @@ unsafe impl Trace for Foo { *m += 1; }) } - unsafe fn weak_trace(&self) -> bool { + unsafe fn is_marked_ephemeron(&self) -> bool { false } + unsafe fn weak_trace(&self, queue: &mut Vec) {} unsafe fn root(&self) {} unsafe fn unroot(&self) {} fn finalize_glue(&self) {} diff --git a/gc/tests/unsized.rs b/gc/tests/unsized.rs index 329aa40..3c00706 100644 --- a/gc/tests/unsized.rs +++ b/gc/tests/unsized.rs @@ -1,4 +1,4 @@ -use gc::{Gc, Trace}; +use gc::{Gc, GcPointer, Trace}; use gc_derive::{Finalize, Trace}; trait Foo: Trace {} diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs index a25adb2..7036f28 100644 --- a/gc/tests/weak_gc.rs +++ b/gc/tests/weak_gc.rs @@ -1,16 +1,16 @@ -use gc::{Gc, GcCell, WeakGc}; +use gc::{Gc, GcCell, GcPointer, WeakGc}; #[test] fn weak_gc_try_deref_some_value() { let weak = WeakGc::new(GcCell::new(1)); - assert_eq!(weak.value(), Some(&(GcCell::new(1)))); + assert_eq!(weak.value(), &(GcCell::new(1))); } #[test] fn weak_gc_from_existing() { let gc = Gc::new(GcCell::new(1)); let weak_gc = gc.clone_weak_ref(); - assert_eq!(weak_gc.value(), Some(&(GcCell::new(1)))); + assert_eq!(weak_gc.value(), &(GcCell::new(1))); } #[test] @@ -23,6 +23,4 @@ fn weak_gc_different_copies() { let _weak_gc3 = WeakGc::new(GcCell::new(2)); gc::force_collect(); } - - assert_eq!(weak_gc2.has_strong_refs(), true); } diff --git a/gc_derive/Cargo.toml b/gc_derive/Cargo.toml index 7834db3..3b8f73f 100644 --- a/gc_derive/Cargo.toml +++ b/gc_derive/Cargo.toml @@ -18,4 +18,4 @@ proc-macro = true syn = "1.0" proc-macro2 = "1.0" quote = "1.0" -synstructure = "0.12" +synstructure = "0.12" \ No newline at end of file diff --git a/gc_derive/src/lib.rs b/gc_derive/src/lib.rs index 9539835..23b16ce 100644 --- a/gc_derive/src/lib.rs +++ b/gc_derive/src/lib.rs @@ -11,7 +11,7 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { .any(|attr| attr.path.is_ident("unsafe_ignore_trace")) }); let trace_body = s.each(|bi| quote!(mark(#bi))); - let weak_trace_body = s.fold(false, |acc, bi| quote! { #acc || return mark(#bi)}); + let weak_trace_body = s.each(|bi| quote!(mark(#bi, queue))); s.add_bounds(AddBounds::Fields); let trace_impl = s.unsafe_bound_impl( @@ -25,11 +25,14 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { } match *self { #trace_body } } - #[inline] unsafe fn weak_trace(&self) -> bool { + #[inline] unsafe fn is_marked_ephemeron(&self) -> bool { + false + } + #[inline] unsafe fn weak_trace(&self, queue: &mut Vec) { #[allow(dead_code, unreachable_code)] #[inline] - unsafe fn mark(it: &T) -> bool { - ::gc::Trace::weak_trace(it) + unsafe fn mark(it: &T, queue: &mut Vec<::gc::GcPointer>) { + ::gc::Trace::weak_trace(it, queue) } match *self { #weak_trace_body } } From 2b21f59ebc2fadfa06240af0b32dcb4fb3315f0c Mon Sep 17 00:00:00 2001 From: nekevss Date: Mon, 17 Oct 2022 19:05:48 -0400 Subject: [PATCH 07/10] Fixed finalize function. All tests passing --- gc/src/gc.rs | 3 +-- gc/src/trace.rs | 2 +- gc/src/weak/pair.rs | 1 - gc/tests/gc_semantics.rs | 2 +- gc/tests/resurrections.rs | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 gc/tests/resurrections.rs diff --git a/gc/src/gc.rs b/gc/src/gc.rs index 44fe6c4..b367a06 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -1,5 +1,4 @@ use crate::trace::Trace; -use crate::weak::Ephemeron; use crate::Finalize; use std::cell::{Cell, RefCell}; use std::mem; @@ -305,7 +304,7 @@ fn collect_garbage(st: &mut GcState) { // prior to finalization as they could have been marked by a different // trace after initially being added to the queue if !(*node.as_ptr()).header.is_marked() { - Finalize::finalize(&(*node.as_ptr()).data) + Trace::finalize_glue(&(*node.as_ptr()).data) } } } diff --git a/gc/src/trace.rs b/gc/src/trace.rs index 0ea97ba..d92d9fb 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -7,7 +7,7 @@ use std::num::{ NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }; use std::path::{Path, PathBuf}; -use std::rc::{Rc, Weak}; +use std::rc::Rc; use std::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, diff --git a/gc/src/weak/pair.rs b/gc/src/weak/pair.rs index a37cb3f..debcc96 100644 --- a/gc/src/weak/pair.rs +++ b/gc/src/weak/pair.rs @@ -17,7 +17,6 @@ use std::rc::Rc; // WeakPair // ////////////// - // The WeakPair struct is a garbage collected pointer to an Ephemeron pub struct WeakPair { ptr_root: Cell>>>, diff --git a/gc/tests/gc_semantics.rs b/gc/tests/gc_semantics.rs index db05650..3d734ee 100644 --- a/gc/tests/gc_semantics.rs +++ b/gc/tests/gc_semantics.rs @@ -78,7 +78,7 @@ unsafe impl Trace for GcWatch { unsafe fn is_marked_ephemeron(&self) -> bool { false } - unsafe fn weak_trace(&self, queue: &mut Vec) { + unsafe fn weak_trace(&self, _queue: &mut Vec) { self.0.with(|f| { let mut of = f.get(); of.weak_trace += 1; diff --git a/gc/tests/resurrections.rs b/gc/tests/resurrections.rs new file mode 100644 index 0000000..8d2a095 --- /dev/null +++ b/gc/tests/resurrections.rs @@ -0,0 +1,37 @@ +use gc::{force_collect, Finalize, Gc, GcCell, GcPointer}; +use gc_derive::{Finalize, Trace}; + +#[derive(Finalize, Trace)] +struct Foo { + bar: GcCell>>, +} + +#[derive(Trace)] +struct Bar { + string: String, + foo: Gc, + this: GcCell>>, +} + +impl Finalize for Bar { + fn finalize(&self) { + println!("Bar's finalizer has run :)"); + *self.foo.bar.borrow_mut() = self.this.borrow().clone(); + } +} + +#[test] +fn resurrection_by_finalizer() { + let foo = Gc::new(Foo { + bar: GcCell::new(None), + }); + let bar = Gc::new(Bar { + string: "Hello, world!".to_string(), + foo: foo.clone(), + this: GcCell::new(None), + }); + *bar.this.borrow_mut() = Some(bar.clone()); + drop(bar); + force_collect(); + assert_eq!(foo.bar.borrow().as_ref().unwrap().string, "Hello, world!"); +} From b2b77ed38e60740b806745ce4856cd72d271ffa5 Mon Sep 17 00:00:00 2001 From: nekevss Date: Tue, 18 Oct 2022 21:03:16 -0400 Subject: [PATCH 08/10] Work from review comments --- gc/src/gc.rs | 1 - gc/src/lib.rs | 2 +- gc/src/weak/ephemeron.rs | 90 ++++++++++++++------------- gc/src/weak/mod.rs | 6 +- gc/src/weak/pair.rs | 129 +++++++++------------------------------ gc/src/weak/weak_gc.rs | 16 ++--- gc/tests/weak_gc.rs | 7 ++- 7 files changed, 90 insertions(+), 161 deletions(-) diff --git a/gc/src/gc.rs b/gc/src/gc.rs index b367a06..9dabddf 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -1,5 +1,4 @@ use crate::trace::Trace; -use crate::Finalize; use std::cell::{Cell, RefCell}; use std::mem; use std::ptr::{self, NonNull}; diff --git a/gc/src/lib.rs b/gc/src/lib.rs index aecf1cf..0b25b9b 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -232,7 +232,7 @@ impl Gc { } #[inline] - pub fn clone_weak_ref(&self) -> WeakGc { + pub fn clone_weak_gc(&self) -> WeakGc { unsafe { // An Ephemeron is not rooted. let weak_gc = WeakGc::from_gc_box(self.ptr_root.get()); diff --git a/gc/src/weak/ephemeron.rs b/gc/src/weak/ephemeron.rs index 6c95f7d..66473d8 100644 --- a/gc/src/weak/ephemeron.rs +++ b/gc/src/weak/ephemeron.rs @@ -18,35 +18,35 @@ use std::ptr::NonNull; /// - Phase Two: Trace keys of queued ephemerons. If reachable, /// /// [Reference]: https://docs.racket-lang.org/reference/ephemerons.html#%28tech._ephemeron%29 -pub struct Ephemeron { - key: Cell>>, - value: Cell>>>, +pub struct Ephemeron { + key: Cell>>, + value: Cell>>>, } -impl Ephemeron { - pub(crate) fn new_weak(value: T) -> Self { - assert!(mem::align_of::>() > 1); +impl Ephemeron { + pub(crate) fn new_weak(value: K) -> Ephemeron { + assert!(mem::align_of::>() > 1); unsafe { let ptr = GcBox::new(value, GcBoxType::Weak); let ephem = Ephemeron { key: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - value: Cell::new(None), + value: Cell::new(None) }; ephem.set_root(); ephem } } - pub(crate) fn new_weak_pair(key: T, value: Option) -> Self { - assert!(mem::align_of::>() > 1); + pub(crate) fn new_weak_pair(key: K, value: Option) -> Ephemeron { + assert!(mem::align_of::>() > 1); unsafe { let key_ptr = GcBox::new(key, GcBoxType::Weak); let value = if let Some(v) = value { - let val_ptr = GcBox::new(v, GcBoxType::Weak); - Cell::new(Some(NonNull::new_unchecked(val_ptr.as_ptr()))) + let new_gc_box = GcBox::new(v, GcBoxType::Weak); + Cell::new(Some(NonNull::new_unchecked(new_gc_box.as_ptr()))) } else { Cell::new(None) }; @@ -61,26 +61,32 @@ impl Ephemeron { } #[inline] - pub(crate) fn set_value(&self, value: Option) { + pub fn set_value(&self, value: V) { unsafe { - if let Some(v) = value { - let val_ptr = GcBox::new(v, GcBoxType::Weak); - self.value - .set(Some(NonNull::new_unchecked(val_ptr.as_ptr()))); - } else { - self.value.set(None); - } + let new_value = GcBox::new(value, GcBoxType::Weak); + self.value.set(Some(NonNull::new_unchecked(new_value.as_ptr()))); } } + } -impl Ephemeron { +impl Ephemeron { + #[inline] - pub(crate) fn weak_from_gc_box(value: NonNull>) -> Self { + pub(crate) fn new_pair_from_gc_pointers( + key: NonNull>, + value: Option>>, + ) -> Ephemeron { unsafe { + let value = if let Some(v) = value { + Cell::new(Some(NonNull::new_unchecked(v.as_ptr()))) + } else { + Cell::new(None) + }; + let ephem = Ephemeron { - key: Cell::new(NonNull::new_unchecked(value.as_ptr())), - value: Cell::new(None), + key: Cell::new(NonNull::new_unchecked(key.as_ptr())), + value, }; ephem.set_root(); ephem @@ -88,20 +94,11 @@ impl Ephemeron { } #[inline] - pub(crate) fn weak_pair_from_gc_boxes( - key: NonNull>, - value: Option>>, - ) -> Self { + pub(crate) fn weak_from_gc_box(value: NonNull>) -> Ephemeron { unsafe { - let value = if let Some(val) = value { - Cell::new(Some(NonNull::new_unchecked(val.as_ptr()))) - } else { - Cell::new(None) - }; - let ephem = Ephemeron { - key: Cell::new(NonNull::new_unchecked(key.as_ptr())), - value, + key: Cell::new(NonNull::new_unchecked(value.as_ptr())), + value: Cell::new(None), }; ephem.set_root(); ephem @@ -130,13 +127,13 @@ impl Ephemeron { } #[inline] - fn inner_key_ptr(&self) -> *mut GcBox { + fn inner_key_ptr(&self) -> *mut GcBox { assert!(finalizer_safe()); unsafe { clear_root_bit(self.key.get()).as_ptr() } } #[inline] - fn inner_value_ptr(&self) -> Option<*mut GcBox> { + fn inner_value_ptr(&self) -> Option<*mut GcBox> { assert!(finalizer_safe()); if let Some(gc_box) = self.value.get() { @@ -148,12 +145,12 @@ impl Ephemeron { } #[inline] - fn inner_key(&self) -> &GcBox { + fn inner_key(&self) -> &GcBox { unsafe { &*self.inner_key_ptr() } } #[inline] - fn inner_value(&self) -> Option<&GcBox> { + fn inner_value(&self) -> Option<&GcBox> { unsafe { if let Some(inner_value) = self.inner_value_ptr() { Some(&*inner_value) @@ -164,12 +161,12 @@ impl Ephemeron { } #[inline] - pub fn key_value(&self) -> &T { + pub fn key_value(&self) -> &K { self.inner_key().value() } #[inline] - pub fn value(&self) -> Option<&T> { + pub fn value(&self) -> Option<&V> { if let Some(gcbox) = self.inner_value() { Some(gcbox.value()) } else { @@ -185,14 +182,19 @@ impl Ephemeron { #[inline] unsafe fn weak_trace_value(&self, queue: &mut Vec) { if let Some(gcbox) = self.inner_value() { - gcbox.weak_trace_inner(queue) + gcbox.weak_trace_inner(queue); } } } -impl Finalize for Ephemeron {} +impl Finalize for Ephemeron { + #[inline] + fn finalize(&self) { + self.value.set(None) + } +} -unsafe impl Trace for Ephemeron { +unsafe impl Trace for Ephemeron { #[inline] unsafe fn trace(&self) { /* An ephemeron is never traced with Phase One Trace */ diff --git a/gc/src/weak/mod.rs b/gc/src/weak/mod.rs index 1f65e0e..c5f4cde 100644 --- a/gc/src/weak/mod.rs +++ b/gc/src/weak/mod.rs @@ -12,9 +12,9 @@ pub(crate) use ephemeron::Ephemeron; pub use pair::WeakPair; pub use weak_gc::WeakGc; -pub(crate) unsafe fn clear_root_bit( - ptr: NonNull>>, -) -> NonNull>> { +pub(crate) unsafe fn clear_root_bit( + ptr: NonNull>>, +) -> NonNull>> { let ptr = ptr.as_ptr(); let data = ptr as *mut u8; let addr = data as isize; diff --git a/gc/src/weak/pair.rs b/gc/src/weak/pair.rs index debcc96..a95d8c1 100644 --- a/gc/src/weak/pair.rs +++ b/gc/src/weak/pair.rs @@ -4,31 +4,26 @@ pub use crate::trace::{Finalize, Trace}; use crate::weak::{clear_root_bit, Ephemeron}; use crate::{set_data_ptr, GcPointer}; use std::cell::Cell; -use std::cmp::Ordering; -use std::fmt::{self, Debug, Display}; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; +use std::fmt; use std::mem; use std::ops::Deref; use std::ptr::NonNull; -use std::rc::Rc; ////////////// // WeakPair // ////////////// // The WeakPair struct is a garbage collected pointer to an Ephemeron -pub struct WeakPair { - ptr_root: Cell>>>, - marker: PhantomData>, +pub struct WeakPair { + ptr_root: Cell>>>, } -impl WeakPair { +impl WeakPair { /// Crate a new Weak type Gc /// /// This method can trigger a collection - pub fn new(key: T, value: Option) -> Self { - assert!(mem::align_of::>() > 1); + pub fn new(key: K, value: Option) -> Self { + assert!(mem::align_of::>() > 1); unsafe { // Allocate the memory for the object @@ -38,7 +33,6 @@ impl WeakPair { (*ptr.as_ptr()).value().unroot(); let weak_gc = WeakPair { ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -46,12 +40,12 @@ impl WeakPair { } #[inline] - pub fn set_value(&self, value: Option) { + pub fn set_value(&self, value: V) { self.inner().value().set_value(value) } } -impl WeakPair { +impl WeakPair { fn rooted(&self) -> bool { self.ptr_root.get().as_ptr() as *mut u8 as usize & 1 != 0 } @@ -69,7 +63,7 @@ impl WeakPair { } #[inline] - fn inner_ptr(&self) -> *mut GcBox> { + fn inner_ptr(&self) -> *mut GcBox> { // If we are currently in the dropping phase of garbage collection, // it would be undefined behavior to dereference this pointer. // By opting into `Trace` you agree to not dereference this pointer @@ -82,36 +76,35 @@ impl WeakPair { } #[inline] - fn inner(&self) -> &GcBox> { + fn inner(&self) -> &GcBox> { unsafe { &*self.inner_ptr() } } } -impl WeakPair { +impl WeakPair { #[inline] - pub fn key_value(&self) -> &T { + pub fn key_value(&self) -> &K { self.inner().value().key_value() } #[inline] - pub fn value(&self) -> Option<&T> { + pub fn value(&self) -> Option<&V> { self.inner().value().value() } #[inline] - pub fn value_tuple(&self) -> (&T, Option<&T>) { + pub fn value_tuple(&self) -> (&K, Option<&V>) { (self.key_value(), self.value()) } #[inline] - pub(crate) fn from_gc_boxes(key: NonNull>, value: Option>>) -> Self { + pub(crate) fn from_gc_boxes(key: NonNull>, value: Option>>) -> Self { unsafe { - let eph = Ephemeron::weak_pair_from_gc_boxes(key, value); + let eph = Ephemeron::new_pair_from_gc_pointers(key, value); let ptr = GcBox::new(eph, GcBoxType::Ephemeron); let weak_gc = WeakPair { ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -119,9 +112,9 @@ impl WeakPair { } } -impl Finalize for WeakPair {} +impl Finalize for WeakPair {} -unsafe impl Trace for WeakPair { +unsafe impl Trace for WeakPair { #[inline] unsafe fn trace(&self) { // Set the strong reference here to false in the case that a trace has run and no @@ -144,13 +137,13 @@ unsafe impl Trace for WeakPair { #[inline] unsafe fn root(&self) { - assert!(!self.rooted(), "Can't double-root a WeakPair"); + assert!(!self.rooted(), "Can't double-root a WeakPair"); self.set_root(); } #[inline] unsafe fn unroot(&self) { - assert!(self.rooted(), "Can't double-unroot a WeakPair"); + assert!(self.rooted(), "Can't double-unroot a WeakPair"); self.clear_root(); } @@ -160,13 +153,12 @@ unsafe impl Trace for WeakPair { } } -impl Clone for WeakPair { +impl Clone for WeakPair { #[inline] fn clone(&self) -> Self { unsafe { let weak_gc = WeakPair { ptr_root: Cell::new(self.ptr_root.get()), - marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -174,84 +166,23 @@ impl Clone for WeakPair { } } -impl Deref for WeakPair { - type Target = T; +impl Deref for WeakPair { + type Target = K; #[inline] - fn deref(&self) -> &T { + fn deref(&self) -> &K { &self.inner().value().key_value() } } -impl Default for WeakPair { +impl Default for WeakPair { #[inline] fn default() -> Self { Self::new(Default::default(), Default::default()) } } -impl PartialEq for WeakPair { - #[inline(always)] - fn eq(&self, other: &Self) -> bool { - **self == **other - } -} - -impl Eq for WeakPair {} - -impl PartialOrd for WeakPair { - #[inline(always)] - fn partial_cmp(&self, other: &Self) -> Option { - (**self).partial_cmp(&**other) - } - - #[inline(always)] - fn lt(&self, other: &Self) -> bool { - **self < **other - } - - #[inline(always)] - fn le(&self, other: &Self) -> bool { - **self <= **other - } - - #[inline(always)] - fn gt(&self, other: &Self) -> bool { - **self > **other - } - - #[inline(always)] - fn ge(&self, other: &Self) -> bool { - **self >= **other - } -} - -impl Ord for WeakPair { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - (**self).cmp(&**other) - } -} - -impl Hash for WeakPair { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } -} - -impl Display for WeakPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&**self, f) - } -} - -impl Debug for WeakPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt(&**self, f) - } -} - -impl fmt::Pointer for WeakPair { +impl fmt::Pointer for WeakPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Pointer::fmt(&self.inner(), f) } @@ -259,14 +190,14 @@ impl fmt::Pointer for WeakPair { // TODO: implement FROM trait for WeakPair -impl std::borrow::Borrow for WeakPair { - fn borrow(&self) -> &T { +impl std::borrow::Borrow for WeakPair { + fn borrow(&self) -> &K { &**self } } -impl std::convert::AsRef for WeakPair { - fn as_ref(&self) -> &T { +impl std::convert::AsRef for WeakPair { + fn as_ref(&self) -> &K { &**self } } diff --git a/gc/src/weak/weak_gc.rs b/gc/src/weak/weak_gc.rs index 4cc61e9..6aa2e82 100644 --- a/gc/src/weak/weak_gc.rs +++ b/gc/src/weak/weak_gc.rs @@ -7,11 +7,9 @@ use std::cell::Cell; use std::cmp::Ordering; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; use std::mem; use std::ops::Deref; use std::ptr::NonNull; -use std::rc::Rc; //////////// // WeakGc // @@ -22,8 +20,7 @@ use std::rc::Rc; /// This implementation uses an Ephemeron as a generalized weak /// box to trace and sweep the inner values. pub struct WeakGc { - ptr_root: Cell>>>, - marker: PhantomData>, + ptr_root: Cell>>>, } impl WeakGc { @@ -35,13 +32,12 @@ impl WeakGc { unsafe { // Allocate the memory for the object - let eph_value = Ephemeron::new_weak(value); + let eph_value = Ephemeron::::new_weak(value); let ptr = GcBox::new(eph_value, GcBoxType::Ephemeron); (*ptr.as_ptr()).value().unroot(); let weak_gc = WeakGc { ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -67,7 +63,7 @@ impl WeakGc { } #[inline] - fn inner_ptr(&self) -> *mut GcBox> { + fn inner_ptr(&self) -> *mut GcBox> { // If we are currently in the dropping phase of garbage collection, // it would be undefined behavior to dereference this pointer. // By opting into `Trace` you agree to not dereference this pointer @@ -80,7 +76,7 @@ impl WeakGc { } #[inline] - fn inner(&self) -> &GcBox> { + fn inner(&self) -> &GcBox> { unsafe { &*self.inner_ptr() } } } @@ -94,12 +90,11 @@ impl WeakGc { #[inline] pub(crate) fn from_gc_box(gc_box: NonNull>) -> Self { unsafe { - let eph = Ephemeron::weak_from_gc_box(gc_box); + let eph = Ephemeron::::weak_from_gc_box(gc_box); let ptr = GcBox::new(eph, GcBoxType::Ephemeron); let weak_gc = WeakGc { ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), - marker: PhantomData, }; weak_gc.set_root(); weak_gc @@ -154,7 +149,6 @@ impl Clone for WeakGc { unsafe { let weak_gc = WeakGc { ptr_root: Cell::new(self.ptr_root.get()), - marker: PhantomData, }; weak_gc.set_root(); weak_gc diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs index 7036f28..1c3ece2 100644 --- a/gc/tests/weak_gc.rs +++ b/gc/tests/weak_gc.rs @@ -9,14 +9,14 @@ fn weak_gc_try_deref_some_value() { #[test] fn weak_gc_from_existing() { let gc = Gc::new(GcCell::new(1)); - let weak_gc = gc.clone_weak_ref(); + let weak_gc = gc.clone_weak_gc(); assert_eq!(weak_gc.value(), &(GcCell::new(1))); } #[test] fn weak_gc_different_copies() { let gc = Gc::new(GcCell::new(1)); - let weak_gc1 = gc.clone_weak_ref(); + let weak_gc1 = gc.clone_weak_gc(); let weak_gc2 = weak_gc1.clone(); { @@ -24,3 +24,6 @@ fn weak_gc_different_copies() { gc::force_collect(); } } + +#[test] +fn weak_gc_ \ No newline at end of file From 269c41ae5395b1960e30bba8c05e48d97ab8bb2e Mon Sep 17 00:00:00 2001 From: nekevss Date: Wed, 19 Oct 2022 23:03:41 -0400 Subject: [PATCH 09/10] Add methods to gc and clean up last commit --- gc/src/lib.rs | 10 +++++++++- gc/src/weak/pair.rs | 25 ++++++++++++++++++++++++- gc/tests/trace_impl.rs | 2 +- gc/tests/weak_gc.rs | 9 +++------ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 0b25b9b..ddff942 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -234,11 +234,19 @@ impl Gc { #[inline] pub fn clone_weak_gc(&self) -> WeakGc { unsafe { - // An Ephemeron is not rooted. let weak_gc = WeakGc::from_gc_box(self.ptr_root.get()); weak_gc } } + + #[inline] + pub fn create_weak_pair(&self, value: Option) -> WeakPair + where + V: Trace, + { + let weak_pair = WeakPair::from_gc_value_pair(self.ptr_root.get(), value); + weak_pair + } } impl Finalize for Gc {} diff --git a/gc/src/weak/pair.rs b/gc/src/weak/pair.rs index a95d8c1..d4ad959 100644 --- a/gc/src/weak/pair.rs +++ b/gc/src/weak/pair.rs @@ -98,7 +98,7 @@ impl WeakPair { } #[inline] - pub(crate) fn from_gc_boxes(key: NonNull>, value: Option>>) -> Self { + pub(crate) fn from_gc_pair(key: NonNull>, value: Option>>) -> Self { unsafe { let eph = Ephemeron::new_pair_from_gc_pointers(key, value); let ptr = GcBox::new(eph, GcBoxType::Ephemeron); @@ -112,6 +112,29 @@ impl WeakPair { } } +impl WeakPair { + #[inline] + pub(crate) fn from_gc_value_pair(key:NonNull>, value: Option) -> Self { + unsafe { + let value_ptr = if let Some(v) = value { + let gcbox = GcBox::new(v, GcBoxType::Weak); + Some(NonNull::new_unchecked(gcbox.as_ptr())) + } else { + None + }; + + let eph = Ephemeron::new_pair_from_gc_pointers(key, value_ptr); + let ptr = GcBox::new(eph, GcBoxType::Ephemeron); + + let weak_pair = WeakPair { + ptr_root: Cell::new(NonNull::new_unchecked(ptr.as_ptr())), + }; + weak_pair.set_root(); + weak_pair + } + } +} + impl Finalize for WeakPair {} unsafe impl Trace for WeakPair { diff --git a/gc/tests/trace_impl.rs b/gc/tests/trace_impl.rs index 3df7fb4..39943bc 100644 --- a/gc/tests/trace_impl.rs +++ b/gc/tests/trace_impl.rs @@ -19,7 +19,7 @@ unsafe impl Trace for Foo { unsafe fn is_marked_ephemeron(&self) -> bool { false } - unsafe fn weak_trace(&self, queue: &mut Vec) {} + unsafe fn weak_trace(&self, _queue: &mut Vec) {} unsafe fn root(&self) {} unsafe fn unroot(&self) {} fn finalize_glue(&self) {} diff --git a/gc/tests/weak_gc.rs b/gc/tests/weak_gc.rs index 1c3ece2..c2c2edb 100644 --- a/gc/tests/weak_gc.rs +++ b/gc/tests/weak_gc.rs @@ -1,4 +1,4 @@ -use gc::{Gc, GcCell, GcPointer, WeakGc}; +use gc::{Gc, GcCell, WeakGc}; #[test] fn weak_gc_try_deref_some_value() { @@ -17,13 +17,10 @@ fn weak_gc_from_existing() { fn weak_gc_different_copies() { let gc = Gc::new(GcCell::new(1)); let weak_gc1 = gc.clone_weak_gc(); - let weak_gc2 = weak_gc1.clone(); + let _weak_gc2 = weak_gc1.clone(); { let _weak_gc3 = WeakGc::new(GcCell::new(2)); gc::force_collect(); } -} - -#[test] -fn weak_gc_ \ No newline at end of file +} \ No newline at end of file From ae3d2a26d8103b0cb0ec4083ad1182c38ea4bc06 Mon Sep 17 00:00:00 2001 From: nekevss Date: Wed, 19 Oct 2022 23:06:17 -0400 Subject: [PATCH 10/10] Making a couple changes --- gc/src/weak/ephemeron.rs | 2 +- gc/src/weak/pair.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gc/src/weak/ephemeron.rs b/gc/src/weak/ephemeron.rs index 66473d8..8033d81 100644 --- a/gc/src/weak/ephemeron.rs +++ b/gc/src/weak/ephemeron.rs @@ -137,7 +137,7 @@ impl Ephemeron { assert!(finalizer_safe()); if let Some(gc_box) = self.value.get() { - let val = unsafe { gc_box.as_ptr() }; + let val = gc_box.as_ptr(); Some(val) } else { None diff --git a/gc/src/weak/pair.rs b/gc/src/weak/pair.rs index d4ad959..075b401 100644 --- a/gc/src/weak/pair.rs +++ b/gc/src/weak/pair.rs @@ -98,7 +98,7 @@ impl WeakPair { } #[inline] - pub(crate) fn from_gc_pair(key: NonNull>, value: Option>>) -> Self { + pub fn from_gc_pair(key: NonNull>, value: Option>>) -> Self { unsafe { let eph = Ephemeron::new_pair_from_gc_pointers(key, value); let ptr = GcBox::new(eph, GcBoxType::Ephemeron);