rustpython_vm/object/
traverse.rs

1use std::ptr::NonNull;
2
3use rustpython_common::lock::{PyMutex, PyRwLock};
4
5use crate::{function::Either, object::PyObjectPayload, AsObject, PyObject, PyObjectRef, PyRef};
6
7pub type TraverseFn<'a> = dyn FnMut(&PyObject) + 'a;
8
9/// This trait is used as a "Optional Trait"(I 'd like to use `Trace?` but it's not allowed yet) for PyObjectPayload type
10///
11/// impl for PyObjectPayload, `pyclass` proc macro will handle the actual dispatch if type impl `Trace`
12/// Every PyObjectPayload impl `MaybeTrace`, which may or may not be traceable
13pub trait MaybeTraverse {
14    /// if is traceable, will be used by vtable to determine
15    const IS_TRACE: bool = false;
16    // if this type is traceable, then call with tracer_fn, default to do nothing
17    fn try_traverse(&self, traverse_fn: &mut TraverseFn);
18}
19
20/// Type that need traverse it's children should impl `Traverse`(Not `MaybeTraverse`)
21/// # Safety
22/// Please carefully read [`traverse()`] and follow the guideline
23pub unsafe trait Traverse {
24    /// impl `traverse()` with caution! Following those guideline so traverse doesn't cause memory error!:
25    /// - Make sure that every owned object(Every PyObjectRef/PyRef) is called with traverse_fn **at most once**.
26    ///   If some field is not called, the worst results is just memory leak,
27    ///   but if some field is called repeatedly, panic and deadlock can happen.
28    ///
29    /// - _**DO NOT**_ clone a `PyObjectRef` or `Pyef<T>` in `traverse()`
30    fn traverse(&self, traverse_fn: &mut TraverseFn);
31}
32
33unsafe impl Traverse for PyObjectRef {
34    fn traverse(&self, traverse_fn: &mut TraverseFn) {
35        traverse_fn(self)
36    }
37}
38
39unsafe impl<T: PyObjectPayload> Traverse for PyRef<T> {
40    fn traverse(&self, traverse_fn: &mut TraverseFn) {
41        traverse_fn(self.as_object())
42    }
43}
44
45unsafe impl Traverse for () {
46    fn traverse(&self, _traverse_fn: &mut TraverseFn) {}
47}
48
49unsafe impl<T: Traverse> Traverse for Option<T> {
50    #[inline]
51    fn traverse(&self, traverse_fn: &mut TraverseFn) {
52        if let Some(v) = self {
53            v.traverse(traverse_fn);
54        }
55    }
56}
57
58unsafe impl<T> Traverse for [T]
59where
60    T: Traverse,
61{
62    #[inline]
63    fn traverse(&self, traverse_fn: &mut TraverseFn) {
64        for elem in self {
65            elem.traverse(traverse_fn);
66        }
67    }
68}
69
70unsafe impl<T> Traverse for Box<[T]>
71where
72    T: Traverse,
73{
74    #[inline]
75    fn traverse(&self, traverse_fn: &mut TraverseFn) {
76        for elem in &**self {
77            elem.traverse(traverse_fn);
78        }
79    }
80}
81
82unsafe impl<T> Traverse for Vec<T>
83where
84    T: Traverse,
85{
86    #[inline]
87    fn traverse(&self, traverse_fn: &mut TraverseFn) {
88        for elem in self {
89            elem.traverse(traverse_fn);
90        }
91    }
92}
93
94unsafe impl<T: Traverse> Traverse for PyRwLock<T> {
95    #[inline]
96    fn traverse(&self, traverse_fn: &mut TraverseFn) {
97        // if can't get a lock, this means something else is holding the lock,
98        // but since gc stopped the world, during gc the lock is always held
99        // so it is safe to ignore those in gc
100        if let Some(inner) = self.try_read_recursive() {
101            inner.traverse(traverse_fn)
102        }
103    }
104}
105
106/// Safety: We can't hold lock during traverse it's child because it may cause deadlock.
107/// TODO(discord9): check if this is thread-safe to do
108/// (Outside of gc phase, only incref/decref will call trace,
109/// and refcnt is atomic, so it should be fine?)
110unsafe impl<T: Traverse> Traverse for PyMutex<T> {
111    #[inline]
112    fn traverse(&self, traverse_fn: &mut TraverseFn) {
113        let mut chs: Vec<NonNull<PyObject>> = Vec::new();
114        if let Some(obj) = self.try_lock() {
115            obj.traverse(&mut |ch| {
116                chs.push(NonNull::from(ch));
117            })
118        }
119        chs.iter()
120            .map(|ch| {
121                // Safety: during gc, this should be fine, because nothing should write during gc's tracing?
122                let ch = unsafe { ch.as_ref() };
123                traverse_fn(ch);
124            })
125            .count();
126    }
127}
128
129macro_rules! trace_tuple {
130    ($(($NAME: ident, $NUM: tt)),*) => {
131        unsafe impl<$($NAME: Traverse),*> Traverse for ($($NAME),*) {
132            #[inline]
133            fn traverse(&self, traverse_fn: &mut TraverseFn) {
134                $(
135                    self.$NUM.traverse(traverse_fn);
136                )*
137            }
138        }
139
140    };
141}
142
143unsafe impl<A: Traverse, B: Traverse> Traverse for Either<A, B> {
144    #[inline]
145    fn traverse(&self, tracer_fn: &mut TraverseFn) {
146        match self {
147            Either::A(a) => a.traverse(tracer_fn),
148            Either::B(b) => b.traverse(tracer_fn),
149        }
150    }
151}
152
153// only tuple with 12 elements or less is supported,
154// because long tuple is extremely rare in almost every case
155unsafe impl<A: Traverse> Traverse for (A,) {
156    #[inline]
157    fn traverse(&self, tracer_fn: &mut TraverseFn) {
158        self.0.traverse(tracer_fn);
159    }
160}
161trace_tuple!((A, 0), (B, 1));
162trace_tuple!((A, 0), (B, 1), (C, 2));
163trace_tuple!((A, 0), (B, 1), (C, 2), (D, 3));
164trace_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4));
165trace_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5));
166trace_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6));
167trace_tuple!(
168    (A, 0),
169    (B, 1),
170    (C, 2),
171    (D, 3),
172    (E, 4),
173    (F, 5),
174    (G, 6),
175    (H, 7)
176);
177trace_tuple!(
178    (A, 0),
179    (B, 1),
180    (C, 2),
181    (D, 3),
182    (E, 4),
183    (F, 5),
184    (G, 6),
185    (H, 7),
186    (I, 8)
187);
188trace_tuple!(
189    (A, 0),
190    (B, 1),
191    (C, 2),
192    (D, 3),
193    (E, 4),
194    (F, 5),
195    (G, 6),
196    (H, 7),
197    (I, 8),
198    (J, 9)
199);
200trace_tuple!(
201    (A, 0),
202    (B, 1),
203    (C, 2),
204    (D, 3),
205    (E, 4),
206    (F, 5),
207    (G, 6),
208    (H, 7),
209    (I, 8),
210    (J, 9),
211    (K, 10)
212);
213trace_tuple!(
214    (A, 0),
215    (B, 1),
216    (C, 2),
217    (D, 3),
218    (E, 4),
219    (F, 5),
220    (G, 6),
221    (H, 7),
222    (I, 8),
223    (J, 9),
224    (K, 10),
225    (L, 11)
226);
Morty Proxy This is a proxified and sanitized view of the page, visit original site.