Skip to main content

gcmodule/
trace_impls.rs

1use crate::trace::{Trace, Tracer};
2
3/// Mark types as acyclic. Opt-out the cycle collector.
4///
5/// See [`Trace::is_type_tracked`](trait.Trace.html#method.is_type_tracked) for details.
6/// In general, types including trait objects (directly or indirectly) should
7/// not be acyclic.
8///
9/// ## Examples
10///
11/// ```
12/// use gcmodule::trace_acyclic;
13///
14/// struct X(u32);
15/// struct Y(String);
16/// struct Z<T>(fn (T));
17///
18/// trace_acyclic!(X);
19/// trace_acyclic!(Y);
20/// trace_acyclic!(<T> Z<T>);
21/// ```
22macro_rules! trace_acyclic {
23    ( <$( $g:ident ),*> $( $t: tt )* ) => {
24        impl<$( $g: 'static ),*> $crate::Trace for $($t)* {
25            #[inline]
26            fn is_type_tracked() -> bool where Self: Sized { false }
27        }
28    };
29    ( $( $t: ty ),* ) => {
30        $( trace_acyclic!(<> $t); )*
31    };
32}
33
34/// Implement [`Trace`](trait.Trace.html) for simple container types.
35///
36/// ## Examples
37///
38/// ```
39/// use gcmodule::Trace;
40/// use gcmodule::trace_fields;
41///
42/// struct X<T1, T2> { a: T1, b: T2 };
43/// struct Y<T>(Box<T>);
44/// struct Z(Box<dyn Trace>);
45///
46/// trace_fields!(
47///     X<T1, T2> { a: T1, b: T2 }
48///     Y<T> { 0: T }
49///     Z { 0 }
50/// );
51/// ```
52macro_rules! trace_fields {
53    ( $( $type:ty { $( $field:tt $(: $tp:ident )? ),* } )* ) => {
54        $(
55            impl< $( $( $tp: $crate::Trace )? ),* > $crate::Trace for $type {
56                fn trace(&self, tracer: &mut $crate::Tracer) {
57                    let _ = tracer;
58                    $( (&self . $field ).trace(tracer); )*
59                }
60                #[inline]
61                fn is_type_tracked() -> bool {
62                    $( $( if $tp::is_type_tracked() { return true } )? )*
63                    false
64                }
65            }
66        )*
67    };
68}
69
70trace_acyclic!(bool, char, f32, f64, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize);
71trace_acyclic!(());
72trace_acyclic!(String, &'static str);
73
74mod tuples {
75    trace_fields!(
76        (A, B) { 0: A, 1: B }
77        (A, B, C) { 0: A, 1: B, 2: C }
78        (A, B, C, D) { 0: A, 1: B, 2: C, 3: D }
79        (A, B, C, D, E) { 0: A, 1: B, 2: C, 3: D, 4: E }
80    );
81}
82
83mod borrow {
84    use super::*;
85    use std::borrow::Cow;
86
87    impl<T: ToOwned + ?Sized> Trace for Cow<'static, T>
88    where
89        T::Owned: Trace,
90    {
91        fn trace(&self, tracer: &mut Tracer) {
92            match self {
93                Cow::Owned(v) => v.trace(tracer),
94                _ => (),
95            }
96        }
97
98        #[inline]
99        fn is_type_tracked() -> bool {
100            T::Owned::is_type_tracked()
101        }
102    }
103}
104
105mod boxed {
106    use super::*;
107
108    impl<T: Trace> Trace for Box<T> {
109        fn trace(&self, tracer: &mut Tracer) {
110            self.as_ref().trace(tracer);
111        }
112
113        #[inline]
114        fn is_type_tracked() -> bool {
115            T::is_type_tracked()
116        }
117    }
118
119    impl Trace for Box<dyn Trace> {
120        fn trace(&self, tracer: &mut Tracer) {
121            self.as_ref().trace(tracer);
122        }
123
124        #[inline]
125        fn is_type_tracked() -> bool {
126            // Trait objects can have complex non-atomic structure.
127            true
128        }
129    }
130
131    impl Trace for Box<dyn Trace + Send> {
132        fn trace(&self, tracer: &mut Tracer) {
133            self.as_ref().trace(tracer);
134        }
135
136        #[inline]
137        fn is_type_tracked() -> bool {
138            true
139        }
140    }
141
142    impl Trace for Box<dyn Trace + Send + Sync> {
143        fn trace(&self, tracer: &mut Tracer) {
144            self.as_ref().trace(tracer);
145        }
146
147        #[inline]
148        fn is_type_tracked() -> bool {
149            true
150        }
151    }
152}
153
154mod cell {
155    use super::*;
156    use std::cell;
157
158    impl<T: Copy + Trace> Trace for cell::Cell<T> {
159        fn trace(&self, tracer: &mut Tracer) {
160            self.get().trace(tracer);
161        }
162
163        #[inline]
164        fn is_type_tracked() -> bool {
165            T::is_type_tracked()
166        }
167    }
168
169    impl<T: Trace> Trace for cell::RefCell<T> {
170        fn trace(&self, tracer: &mut Tracer) {
171            // If the RefCell is currently borrowed we
172            // assume there's an outstanding reference to this
173            // cycle so it's ok if we don't trace through it.
174            // If the borrow gets leaked somehow then we're going
175            // to leak the cycle.
176            if let Ok(x) = self.try_borrow() {
177                x.trace(tracer);
178            }
179        }
180
181        #[inline]
182        fn is_type_tracked() -> bool {
183            T::is_type_tracked()
184        }
185    }
186}
187
188mod collections {
189    use super::*;
190    use std::collections;
191    use std::hash;
192
193    impl<K: Trace, V: Trace> Trace for collections::BTreeMap<K, V> {
194        fn trace(&self, tracer: &mut Tracer) {
195            for (k, v) in self {
196                k.trace(tracer);
197                v.trace(tracer);
198            }
199        }
200
201        #[inline]
202        fn is_type_tracked() -> bool {
203            K::is_type_tracked() || V::is_type_tracked()
204        }
205    }
206
207    impl<K: Eq + hash::Hash + Trace, V: Trace> Trace for collections::HashMap<K, V> {
208        fn trace(&self, tracer: &mut Tracer) {
209            for (k, v) in self {
210                k.trace(tracer);
211                v.trace(tracer);
212            }
213        }
214
215        #[inline]
216        fn is_type_tracked() -> bool {
217            K::is_type_tracked() || V::is_type_tracked()
218        }
219    }
220
221    impl<T: Trace> Trace for collections::LinkedList<T> {
222        fn trace(&self, tracer: &mut Tracer) {
223            for t in self {
224                t.trace(tracer);
225            }
226        }
227
228        #[inline]
229        fn is_type_tracked() -> bool {
230            T::is_type_tracked()
231        }
232    }
233
234    impl<T: Trace> Trace for collections::VecDeque<T> {
235        fn trace(&self, tracer: &mut Tracer) {
236            for t in self {
237                t.trace(tracer);
238            }
239        }
240
241        #[inline]
242        fn is_type_tracked() -> bool {
243            T::is_type_tracked()
244        }
245    }
246}
247
248mod vec {
249    use super::*;
250    impl<T: Trace> Trace for Vec<T> {
251        fn trace(&self, tracer: &mut Tracer) {
252            for t in self {
253                t.trace(tracer);
254            }
255        }
256
257        #[inline]
258        fn is_type_tracked() -> bool {
259            T::is_type_tracked()
260        }
261    }
262}
263
264// See https://github.com/rust-lang/rust/issues/56105#issuecomment-465709105
265#[allow(unknown_lints)]
266#[allow(coherence_leak_check)]
267mod func {
268    trace_acyclic!(<X> fn() -> X);
269
270    trace_acyclic!(<A, X> fn(&A) -> X);
271    trace_acyclic!(<A, X> fn(A) -> X);
272
273    trace_acyclic!(<A, B, X> fn(&A, &B) -> X);
274    trace_acyclic!(<A, B, X> fn(A, &B) -> X);
275    trace_acyclic!(<A, B, X> fn(&A, B) -> X);
276    trace_acyclic!(<A, B, X> fn(A, B) -> X);
277
278    trace_acyclic!(<A, B, C, X> fn(&A, &B, &C) -> X);
279    trace_acyclic!(<A, B, C, X> fn(A, &B, &C) -> X);
280    trace_acyclic!(<A, B, C, X> fn(&A, B, &C) -> X);
281    trace_acyclic!(<A, B, C, X> fn(A, B, &C) -> X);
282    trace_acyclic!(<A, B, C, X> fn(&A, &B, C) -> X);
283    trace_acyclic!(<A, B, C, X> fn(A, &B, C) -> X);
284    trace_acyclic!(<A, B, C, X> fn(&A, B, C) -> X);
285    trace_acyclic!(<A, B, C, X> fn(A, B, C) -> X);
286
287    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, &C, &D) -> X);
288    trace_acyclic!(<A, B, C, D, X> fn(A, &B, &C, &D) -> X);
289    trace_acyclic!(<A, B, C, D, X> fn(&A, B, &C, &D) -> X);
290    trace_acyclic!(<A, B, C, D, X> fn(A, B, &C, &D) -> X);
291    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, C, &D) -> X);
292    trace_acyclic!(<A, B, C, D, X> fn(A, &B, C, &D) -> X);
293    trace_acyclic!(<A, B, C, D, X> fn(&A, B, C, &D) -> X);
294    trace_acyclic!(<A, B, C, D, X> fn(A, B, C, &D) -> X);
295    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, &C, D) -> X);
296    trace_acyclic!(<A, B, C, D, X> fn(A, &B, &C, D) -> X);
297    trace_acyclic!(<A, B, C, D, X> fn(&A, B, &C, D) -> X);
298    trace_acyclic!(<A, B, C, D, X> fn(A, B, &C, D) -> X);
299    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, C, D) -> X);
300    trace_acyclic!(<A, B, C, D, X> fn(A, &B, C, D) -> X);
301    trace_acyclic!(<A, B, C, D, X> fn(&A, B, C, D) -> X);
302    trace_acyclic!(<A, B, C, D, X> fn(A, B, C, D) -> X);
303
304    trace_acyclic!(<A, B, C, D, E, X> fn(A, B, C, D, E) -> X);
305    trace_acyclic!(<A, B, C, D, E, F, X> fn(A, B, C, D, E, F) -> X);
306}
307
308mod ffi {
309    use std::ffi;
310
311    trace_acyclic!(ffi::CString, ffi::NulError, ffi::OsString);
312}
313
314mod net {
315    use std::net;
316
317    trace_acyclic!(
318        net::AddrParseError,
319        net::Ipv4Addr,
320        net::Ipv6Addr,
321        net::SocketAddrV4,
322        net::SocketAddrV6,
323        net::TcpListener,
324        net::TcpStream,
325        net::UdpSocket
326    );
327}
328
329mod option {
330    use super::*;
331
332    impl<T: Trace> Trace for Option<T> {
333        fn trace(&self, tracer: &mut Tracer) {
334            if let Some(ref t) = *self {
335                t.trace(tracer);
336            }
337        }
338
339        fn is_type_tracked() -> bool {
340            T::is_type_tracked()
341        }
342    }
343}
344
345mod path {
346    use std::path;
347
348    trace_acyclic!(path::PathBuf);
349}
350
351mod process {
352    use std::process;
353
354    trace_acyclic!(
355        process::Child,
356        process::ChildStderr,
357        process::ChildStdin,
358        process::ChildStdout,
359        process::Command,
360        process::ExitStatus,
361        process::Output,
362        process::Stdio
363    );
364}
365
366mod rc {
367    use std::rc;
368
369    trace_acyclic!(<T> rc::Rc<T>);
370    trace_acyclic!(<T> rc::Weak<T>);
371}
372
373mod result {
374    use super::*;
375
376    impl<T: Trace, U: Trace> Trace for Result<T, U> {
377        fn trace(&self, tracer: &mut Tracer) {
378            match *self {
379                Ok(ref t) => t.trace(tracer),
380                Err(ref u) => u.trace(tracer),
381            }
382        }
383
384        fn is_type_tracked() -> bool {
385            T::is_type_tracked() || U::is_type_tracked()
386        }
387    }
388}
389
390mod sync {
391    use super::*;
392    use std::sync;
393
394    // See comment in Mutex for why this is acyclic.
395    trace_acyclic!(<T> sync::Arc<T>);
396
397    impl<T: Trace> Trace for sync::Mutex<T> {
398        fn trace(&self, tracer: &mut Tracer) {
399            // For single-thread collector (ObjectSpace):
400            // Locking is optional. See RefCell.
401            //
402            // For multi-thread collector (ThreadedObjectSpace):
403            // `ThreadedCcRef` is expected to be the only way to access a `T`
404            // stored in `ThreadedCc<T>`. `ThreadedCcRef` takes a lock so
405            // collector does not run. When the collector runs, `ThreadedCcRef`
406            // are dropped so locks are released.
407            // A special is when `T` is `Arc<Mutex<M>>`. It allows mutating `M`
408            // without going through `ThreadedCcRef`. This is handled by marking
409            // `Arc` as acyclic. The collector only cares about `trace`, and
410            // `trace` result for an `Arc` cannot be changed by another thread,
411            // even if `M` is mutable.
412            if let Ok(x) = self.try_lock() {
413                x.trace(tracer);
414            }
415        }
416
417        #[inline]
418        fn is_type_tracked() -> bool {
419            T::is_type_tracked()
420        }
421    }
422
423    impl<T: Trace> Trace for sync::RwLock<T> {
424        fn trace(&self, tracer: &mut Tracer) {
425            // See Mutex for why locking is optional.
426            //
427            // If read or write locks are already taken, that indicates
428            // outstanding references that keeps the objects alive.
429            if let Ok(x) = self.try_write() {
430                x.trace(tracer);
431            }
432        }
433
434        #[inline]
435        fn is_type_tracked() -> bool {
436            T::is_type_tracked()
437        }
438    }
439}
440
441mod thread {
442    use std::thread;
443
444    trace_acyclic!(<T> thread::JoinHandle<T>);
445    trace_acyclic!(<T> thread::LocalKey<T>);
446    trace_acyclic!(thread::Thread);
447}
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452    use crate::Cc;
453    use std::cell::{Cell, RefCell};
454    use std::rc::Rc;
455
456    #[test]
457    fn test_is_type_tracked() {
458        assert!(!u8::is_type_tracked());
459        assert!(!<f32 as Trace>::is_type_tracked());
460        assert!(!String::is_type_tracked());
461        assert!(!Option::<u32>::is_type_tracked());
462        assert!(!Vec::<u8>::is_type_tracked());
463        assert!(!<(bool, f64)>::is_type_tracked());
464        assert!(!Cell::<u32>::is_type_tracked());
465        assert!(!RefCell::<String>::is_type_tracked());
466        assert!(Box::<dyn Trace>::is_type_tracked());
467        assert!(RefCell::<Box::<dyn Trace>>::is_type_tracked());
468        assert!(RefCell::<Vec::<Box::<dyn Trace>>>::is_type_tracked());
469        assert!(Vec::<RefCell::<Box::<dyn Trace>>>::is_type_tracked());
470        assert!(!Cc::<u8>::is_type_tracked());
471        assert!(!Vec::<Cc::<u8>>::is_type_tracked());
472
473        assert!(!<fn(u8) -> u8>::is_type_tracked());
474        assert!(!<fn(&u8) -> u8>::is_type_tracked());
475    }
476
477    #[test]
478    fn test_is_cyclic_type_tracked() {
479        type C1 = RefCell<Option<Rc<Box<S1>>>>;
480        struct S1(C1);
481        impl Trace for S1 {
482            fn trace(&self, t: &mut Tracer) {
483                self.0.trace(t);
484            }
485            fn is_type_tracked() -> bool {
486                // This is not an infinite loop because Rc is not tracked.
487                C1::is_type_tracked()
488            }
489        }
490
491        type C2 = RefCell<Option<Cc<Box<S2>>>>;
492        struct S2(C2);
493        impl Trace for S2 {
494            fn trace(&self, t: &mut Tracer) {
495                self.0.trace(t);
496            }
497            fn is_type_tracked() -> bool {
498                // C2::is_type_tracked() can cause an infinite loop.
499                true
500            }
501        }
502
503        assert!(!S1::is_type_tracked());
504        assert!(S2::is_type_tracked());
505    }
506}