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}