aiscript_arena/
gc_weak.rs

1use crate::collect::Collect;
2use crate::context::Finalization;
3use crate::gc::Gc;
4use crate::types::GcBox;
5use crate::{Collection, Mutation};
6
7use core::fmt::{self, Debug};
8
9pub struct GcWeak<'gc, T: ?Sized + 'gc> {
10    pub(crate) inner: Gc<'gc, T>,
11}
12
13impl<'gc, T: ?Sized + 'gc> Copy for GcWeak<'gc, T> {}
14
15impl<'gc, T: ?Sized + 'gc> Clone for GcWeak<'gc, T> {
16    #[inline]
17    fn clone(&self) -> GcWeak<'gc, T> {
18        *self
19    }
20}
21
22impl<'gc, T: ?Sized + 'gc> Debug for GcWeak<'gc, T> {
23    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
24        write!(fmt, "(GcWeak)")
25    }
26}
27
28unsafe impl<'gc, T: ?Sized + 'gc> Collect for GcWeak<'gc, T> {
29    #[inline]
30    fn trace(&self, cc: &Collection) {
31        unsafe {
32            cc.trace_weak(GcBox::erase(self.inner.ptr));
33        }
34    }
35}
36
37impl<'gc, T: ?Sized + 'gc> GcWeak<'gc, T> {
38    #[inline]
39    pub fn upgrade(self, mc: &Mutation<'gc>) -> Option<Gc<'gc, T>> {
40        let ptr = unsafe { GcBox::erase(self.inner.ptr) };
41        mc.upgrade(ptr).then_some(self.inner)
42    }
43
44    /// During collection, return whether the value referenced by this `GcWeak` has already been
45    /// dropped.
46    #[inline]
47    pub fn is_dropped(self, _cc: &Collection) -> bool {
48        !unsafe { self.inner.ptr.as_ref() }.header.is_live()
49    }
50
51    /// Returns true when a pointer is *dead* during finalization.
52    ///
53    /// This is a weaker condition than being *dropped*, as the pointer *may* still be valid. Being
54    /// *dead* means that there were no strong pointers pointing to this weak pointer that were
55    /// found by the marking phase, and if it is not already dropped, it *will* be dropped as soon
56    /// as collection resumes.
57    ///
58    /// If the pointer is still valid, it may be resurrected using `GcWeak::upgrade` or
59    /// `GcWeak::resurrect`.
60    ///
61    /// NOTE: This returns true if the pointer was destined to be collected at the **start** of the
62    /// current finalization callback. Resurrecting one pointer can transitively resurrect others,
63    /// and this method does not reflect this from within the same finalization call! If transitive
64    /// resurrection is important, you may have to carefully call finalize multiple times for one
65    /// collection cycle with marking stages in-between, and in the precise order that you want.
66    #[inline]
67    pub fn is_dead(self, fc: &Finalization<'gc>) -> bool {
68        Gc::is_dead(fc, self.inner)
69    }
70
71    /// Manually marks a dead (but non-dropped) `GcWeak` as strongly reachable and keeps it alive.
72    ///
73    /// This is similar to a write barrier in that it moves the collection phase back to `Marking`
74    /// if it is not already there. All transitively held pointers from this will also be marked as
75    /// reachable once marking resumes.
76    ///
77    /// Returns the upgraded `Gc` pointer as a convenience. Whether or not the strong pointer is
78    /// stored anywhere, the value and all transitively reachable values are still guaranteed to not
79    /// be dropped this collection cycle.
80    #[inline]
81    pub fn resurrect(self, fc: &Finalization<'gc>) -> Option<Gc<'gc, T>> {
82        // SAFETY: We know that we are currently marking, so any non-dropped pointer is safe to
83        // resurrect.
84        if unsafe { self.inner.ptr.as_ref() }.header.is_live() {
85            Gc::resurrect(fc, self.inner);
86            Some(self.inner)
87        } else {
88            None
89        }
90    }
91
92    /// Returns true if two `GcWeak`s point to the same allocation.
93    ///
94    /// Similarly to `Rc::ptr_eq` and `Arc::ptr_eq`, this function ignores the metadata of `dyn`
95    /// pointers.
96    #[inline]
97    pub fn ptr_eq(this: GcWeak<'gc, T>, other: GcWeak<'gc, T>) -> bool {
98        // TODO: Equivalent to `core::ptr::addr_eq`:
99        // https://github.com/rust-lang/rust/issues/116324
100        core::ptr::eq(this.as_ptr(), other.as_ptr())
101    }
102
103    #[inline]
104    pub fn as_ptr(self) -> *const T {
105        Gc::as_ptr(self.inner)
106    }
107}
108
109impl<'gc, T: 'gc> GcWeak<'gc, T> {
110    /// Cast the internal pointer to a different type.
111    ///
112    /// SAFETY:
113    /// It must be valid to dereference a `*mut U` that has come from casting a `*mut T`.
114    #[inline]
115    pub unsafe fn cast<U: 'gc>(this: GcWeak<'gc, T>) -> GcWeak<'gc, U> {
116        unsafe {
117            GcWeak {
118                inner: Gc::cast::<U>(this.inner),
119            }
120        }
121    }
122
123    /// Retrieve a `GcWeak` from a raw pointer obtained from `GcWeak::as_ptr`
124    ///
125    /// SAFETY:
126    /// The provided pointer must have been obtained from `GcWeak::as_ptr` or `Gc::as_ptr`, and
127    /// the pointer must not have been *fully* collected yet (it may be a dropped but valid weak
128    /// pointer).
129    #[inline]
130    pub unsafe fn from_ptr(ptr: *const T) -> GcWeak<'gc, T> {
131        unsafe {
132            GcWeak {
133                inner: Gc::from_ptr(ptr),
134            }
135        }
136    }
137}