Skip to main content

turbo_tasks/
trace.rs

1use std::{
2    borrow::Cow,
3    cell::RefCell,
4    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
5    marker::PhantomData,
6    ops::Deref,
7    path::{Path, PathBuf},
8    pin::Pin,
9    sync::{Arc, Mutex as StdMutex, atomic::*},
10    time::Duration,
11};
12
13use auto_hash_map::{AutoMap, AutoSet};
14use either::Either;
15use indexmap::{IndexMap, IndexSet};
16use smallvec::SmallVec;
17use turbo_frozenmap::{FrozenMap, FrozenSet};
18use turbo_rcstr::RcStr;
19use turbo_tasks_hash::HashAlgorithm;
20
21use crate::{RawVc, event::Event};
22
23pub struct TraceRawVcsContext {
24    list: Vec<RawVc>,
25}
26
27impl TraceRawVcsContext {
28    pub(crate) fn new() -> Self {
29        Self { list: Vec::new() }
30    }
31
32    pub(crate) fn into_vec(self) -> Vec<RawVc> {
33        self.list
34    }
35}
36
37/// Trait that allows to walk data to find all [RawVc]s contained.
38///
39/// This is important for Garbage Collection to mark all Cells and therefore
40/// Tasks that are still in use.
41///
42/// It can also be used to optimize transferring of Tasks, where knowning the
43/// referenced Cells/Tasks allows pushing them earlier.
44///
45/// `#[derive(TraceRawVcs)]` is available.
46/// `#[trace_ignore]` can be used on fields to skip tracing for them.
47pub trait TraceRawVcs {
48    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext);
49    fn get_raw_vcs(&self) -> Vec<RawVc> {
50        let mut trace_context = TraceRawVcsContext::new();
51        self.trace_raw_vcs(&mut trace_context);
52        trace_context.into_vec()
53    }
54}
55
56macro_rules! ignore {
57  ($ty:ty) => {
58    impl TraceRawVcs for $ty {
59      fn trace_raw_vcs(&self, _context: &mut TraceRawVcsContext) {}
60    }
61  };
62
63  ($ty:ty, $($tys:ty),+) => {
64    ignore!($ty);
65    ignore!($($tys),+);
66  }
67}
68
69ignore!(
70    i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, f32, f64, char, bool, isize, usize
71);
72ignore!(
73    AtomicI8,
74    AtomicU8,
75    AtomicI16,
76    AtomicU16,
77    AtomicI32,
78    AtomicU32,
79    AtomicI64,
80    AtomicU64,
81    AtomicBool,
82    AtomicUsize
83);
84ignore!((), str, String, Duration, RcStr);
85ignore!(Path, PathBuf);
86ignore!(serde_json::Value, serde_json::Map<String, serde_json::Value>);
87ignore!(HashAlgorithm);
88ignore!(Event);
89
90// This is not technically correct, as `anyhow::Error` contains a boxed source error, which could
91// contain a `Vc`, but most errors don't do this, and implementing `TraceRawVcs` over every possible
92// error type is impractical.
93//
94// For serialized errors: This is fine, we throw away the anyhow source upon serialization.
95ignore!(anyhow::Error);
96
97impl<T: ?Sized> TraceRawVcs for PhantomData<T> {
98    fn trace_raw_vcs(&self, _trace_context: &mut TraceRawVcsContext) {}
99}
100
101// based on stdlib's internal `tuple_impls!` macro
102macro_rules! impl_trace_tuple {
103    ($T:ident) => {
104        impl_trace_tuple!(@impl $T);
105    };
106    ($T:ident $( $U:ident )+) => {
107        impl_trace_tuple!($( $U )+);
108        impl_trace_tuple!(@impl $T $( $U )+);
109    };
110    (@impl $( $T:ident )+) => {
111        impl<$($T: TraceRawVcs),+> TraceRawVcs for ($($T,)+) {
112            #[allow(non_snake_case)]
113            fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
114                let ($($T,)+) = self;
115                $(
116                    TraceRawVcs::trace_raw_vcs($T, trace_context);
117                )+
118            }
119        }
120    };
121}
122
123impl_trace_tuple!(E D C B A Z Y X W V U T);
124
125/// Function pointers (the lowercase `fn` type, not `Fn`) don't contain any data, so it's not
126/// possible for them to contain a `Vc`.
127macro_rules! impl_trace_fn_ptr {
128    ($T:ident) => {
129        impl_trace_fn_ptr!(@impl $T);
130    };
131    ($T:ident $( $U:ident )+) => {
132        impl_trace_fn_ptr!($( $U )+);
133        impl_trace_fn_ptr!(@impl $T $( $U )+);
134    };
135    (@impl $( $T:ident )+) => {
136        impl<$($T,)+ Return> TraceRawVcs for fn($($T),+) -> Return {
137            fn trace_raw_vcs(&self, _trace_context: &mut TraceRawVcsContext) {}
138        }
139    };
140}
141
142impl_trace_fn_ptr!(E D C B A Z Y X W V U T);
143
144impl<T: TraceRawVcs> TraceRawVcs for Option<T> {
145    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
146        if let Some(item) = self {
147            TraceRawVcs::trace_raw_vcs(item, trace_context);
148        }
149    }
150}
151
152impl<T: TraceRawVcs> TraceRawVcs for Vec<T> {
153    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
154        for item in self.iter() {
155            TraceRawVcs::trace_raw_vcs(item, trace_context);
156        }
157    }
158}
159
160impl<T: TraceRawVcs> TraceRawVcs for Box<[T]> {
161    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
162        for item in self.iter() {
163            TraceRawVcs::trace_raw_vcs(item, trace_context);
164        }
165    }
166}
167
168impl<T: TraceRawVcs, const N: usize> TraceRawVcs for SmallVec<[T; N]> {
169    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
170        for item in self.iter() {
171            TraceRawVcs::trace_raw_vcs(item, trace_context);
172        }
173    }
174}
175
176impl<T: TraceRawVcs, const N: usize> TraceRawVcs for [T; N] {
177    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
178        for item in self.iter() {
179            TraceRawVcs::trace_raw_vcs(item, trace_context);
180        }
181    }
182}
183
184impl<T: TraceRawVcs, S> TraceRawVcs for HashSet<T, S> {
185    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
186        for item in self.iter() {
187            TraceRawVcs::trace_raw_vcs(item, trace_context);
188        }
189    }
190}
191
192impl<T: TraceRawVcs, S, const I: usize> TraceRawVcs for AutoSet<T, S, I> {
193    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
194        for item in self.iter() {
195            TraceRawVcs::trace_raw_vcs(item, trace_context);
196        }
197    }
198}
199
200impl<T: TraceRawVcs> TraceRawVcs for BTreeSet<T> {
201    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
202        for item in self.iter() {
203            TraceRawVcs::trace_raw_vcs(item, trace_context);
204        }
205    }
206}
207
208impl<T: TraceRawVcs, S> TraceRawVcs for IndexSet<T, S> {
209    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
210        for item in self.iter() {
211            TraceRawVcs::trace_raw_vcs(item, trace_context);
212        }
213    }
214}
215
216impl<T: TraceRawVcs> TraceRawVcs for FrozenSet<T> {
217    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
218        for item in self.iter() {
219            TraceRawVcs::trace_raw_vcs(item, trace_context);
220        }
221    }
222}
223
224impl<K: TraceRawVcs, V: TraceRawVcs, S> TraceRawVcs for HashMap<K, V, S> {
225    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
226        for (key, value) in self.iter() {
227            TraceRawVcs::trace_raw_vcs(key, trace_context);
228            TraceRawVcs::trace_raw_vcs(value, trace_context);
229        }
230    }
231}
232
233impl<K: TraceRawVcs, V: TraceRawVcs, S, const I: usize> TraceRawVcs for AutoMap<K, V, S, I> {
234    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
235        for (key, value) in self.iter() {
236            TraceRawVcs::trace_raw_vcs(key, trace_context);
237            TraceRawVcs::trace_raw_vcs(value, trace_context);
238        }
239    }
240}
241
242impl<K: TraceRawVcs, V: TraceRawVcs> TraceRawVcs for BTreeMap<K, V> {
243    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
244        for (key, value) in self.iter() {
245            TraceRawVcs::trace_raw_vcs(key, trace_context);
246            TraceRawVcs::trace_raw_vcs(value, trace_context);
247        }
248    }
249}
250
251impl<K: TraceRawVcs, V: TraceRawVcs, S> TraceRawVcs for IndexMap<K, V, S> {
252    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
253        for (key, value) in self.iter() {
254            TraceRawVcs::trace_raw_vcs(key, trace_context);
255            TraceRawVcs::trace_raw_vcs(value, trace_context);
256        }
257    }
258}
259
260impl<K: TraceRawVcs, V: TraceRawVcs> TraceRawVcs for FrozenMap<K, V> {
261    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
262        for (key, value) in self.iter() {
263            TraceRawVcs::trace_raw_vcs(key, trace_context);
264            TraceRawVcs::trace_raw_vcs(value, trace_context);
265        }
266    }
267}
268
269impl<T> TraceRawVcs for Pin<T>
270where
271    T: Deref,
272    <T as Deref>::Target: TraceRawVcs,
273{
274    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
275        TraceRawVcs::trace_raw_vcs(&**self, trace_context);
276    }
277}
278
279impl<T: TraceRawVcs + ?Sized> TraceRawVcs for Box<T> {
280    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
281        TraceRawVcs::trace_raw_vcs(&**self, trace_context);
282    }
283}
284
285impl<T: TraceRawVcs + ?Sized> TraceRawVcs for Arc<T> {
286    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
287        TraceRawVcs::trace_raw_vcs(&**self, trace_context);
288    }
289}
290
291impl<B: TraceRawVcs + ToOwned + ?Sized> TraceRawVcs for Cow<'_, B> {
292    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
293        TraceRawVcs::trace_raw_vcs(&**self, trace_context);
294    }
295}
296
297impl TraceRawVcs for RawVc {
298    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
299        trace_context.list.push(*self);
300    }
301}
302
303impl<T: TraceRawVcs, E: TraceRawVcs> TraceRawVcs for Result<T, E> {
304    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
305        match self {
306            Ok(o) => o.trace_raw_vcs(trace_context),
307            Err(e) => e.trace_raw_vcs(trace_context),
308        }
309    }
310}
311
312// NOTE: Implementing `TraceRawVcs` on a synchronous lock with guards that do not implement `Send`
313// is okay, as it won't be held across await points. We shouldn't attempt to implement it on an
314// asynchronous locks (e.g. via `tokio::sync::Mutex::blocking_lock`).
315//
316// If a future garbage collection tracing implementation holds a global lock on execution, we could
317// end up deadlocked if a currently executing task is holding a lock across an await point.
318//
319// Similarly, if we ever implement synchronous tasks with synchronous reads of `Vc`s, we need to add
320// a mechanism to loudly fail if we're holding a traced lock across a task read.
321impl<T: TraceRawVcs + ?Sized> TraceRawVcs for StdMutex<T> {
322    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
323        self.lock().unwrap().trace_raw_vcs(trace_context);
324    }
325}
326
327impl<T: TraceRawVcs + ?Sized> TraceRawVcs for parking_lot::Mutex<T> {
328    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
329        self.lock().trace_raw_vcs(trace_context);
330    }
331}
332
333impl<T: TraceRawVcs + ?Sized> TraceRawVcs for RefCell<T> {
334    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
335        self.borrow().trace_raw_vcs(trace_context);
336    }
337}
338
339impl<T: TraceRawVcs + ?Sized> TraceRawVcs for &T {
340    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
341        (**self).trace_raw_vcs(trace_context);
342    }
343}
344impl<T: TraceRawVcs + ?Sized> TraceRawVcs for &mut T {
345    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
346        (**self).trace_raw_vcs(trace_context);
347    }
348}
349
350impl<L: TraceRawVcs, R: TraceRawVcs> TraceRawVcs for Either<L, R> {
351    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
352        match self {
353            Either::Left(l) => l.trace_raw_vcs(trace_context),
354            Either::Right(r) => r.trace_raw_vcs(trace_context),
355        }
356    }
357}
358
359pub use turbo_tasks_macros::TraceRawVcs;