Skip to main content

turbo_tasks/task/
task_input.rs

1use std::{
2    collections::{BTreeMap, BTreeSet},
3    fmt::Debug,
4    future::Future,
5    hash::Hash,
6    ops::{Deref, DerefMut},
7    pin::Pin,
8    sync::Arc,
9    task::{Context, Poll},
10    time::Duration,
11};
12
13use anyhow::Result;
14use bincode::{
15    Decode, Encode,
16    de::Decoder,
17    enc::Encoder,
18    error::{DecodeError, EncodeError},
19};
20use either::Either;
21use turbo_frozenmap::{FrozenMap, FrozenSet};
22use turbo_rcstr::RcStr;
23use turbo_tasks_hash::HashAlgorithm;
24
25// This import is necessary for derive macros to work, as their expansion refers to the crate
26// name directly.
27use crate::{self as turbo_tasks, OrdResolvedVc, ReadRef};
28use crate::{
29    DynTaskInputs, ResolvedVc, TaskId, TransientInstance, TransientValue, ValueTypeId, Vc,
30    trace::TraceRawVcs,
31};
32
33/// An 8-byte hand-rolled [`Future`] that immediately resolves to `Ok(self.clone())` of the
34/// referenced value.
35///
36/// Used by the [`TaskInput::resolve_input`] default implementation
37struct CloneReady<'a, T> {
38    pub inner: Option<&'a T>,
39}
40
41impl<'a, T: Clone> Future for CloneReady<'a, T> {
42    type Output = Result<T>;
43
44    fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
45        Poll::Ready(Ok(self
46            .inner
47            .take()
48            .expect("future already polled to completion")
49            .clone()))
50    }
51}
52
53// `CloneReady` holds only a shared reference; it has no self-referential state.
54impl<'a, T> Unpin for CloneReady<'a, T> {}
55
56/// Trait to implement in order for a type to be accepted as a
57/// [`#[turbo_tasks::function]`][crate::function] argument.
58///
59/// ## Serialization
60///
61/// For persistent caching of a task, arguments must be serializable. All `TaskInput`s must
62/// implement the bincode [`Encode`] and [`Decode`] traits.
63///
64/// Transient task inputs are required to implement [`Encode`] and [`Decode`], but are allowed to
65/// panic at runtime. This requirement could be lifted in the future.
66///
67/// Bincode encoding must be deterministic and compatible with [`Eq`] comparisons. If two
68/// `TaskInput`s compare equal they must also encode to the same bytes.
69///
70/// ## Hash and Eq
71///
72/// Arguments are used as part of keys in a `HashMap`, so they must implement of [`PartialEq`],
73/// [`Eq`], and [`Hash`] traits.
74///
75/// ## [`Vc<T>`][Vc]
76///
77/// A [`Vc`] is a pointer to a cell. It implements `TaskInput` and serves as a "pass by reference"
78/// argument:
79///
80/// - **Memoization**: [`Vc`] is keyed by pointer for memoization purposes. Identical values in
81///   different cells are treated as distinct.
82/// - **Singleton Pattern**: To ensure memoization efficiency, the singleton pattern can be employed
83///   to guarantee that identical values yield the same `Vc`. For more info see [Singleton Pattern
84///   Guide][singleton].
85///
86/// [singleton]: https://turbopack-rust-docs.vercel.sh/turbo-engine/singleton.html
87///
88/// ## Deriving `TaskInput`
89///
90/// Structs or enums can be made into task inputs by deriving `TaskInput`:
91///
92/// ```rust
93/// # use turbo_tasks::{
94/// #     macro_helpers::bincode::{Decode, Encode},
95/// #     trace::TraceRawVcs,
96/// # };
97/// #[turbo_tasks::task_input]
98/// #[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
99/// struct MyStruct {
100///     // Fields go here...
101/// }
102/// ```
103///
104/// Derived `TaskInput` types **passed by value**. When called, arguments are moved into a `Box`,
105/// and then cloned before being passed into the function. If the task is invalidated, the
106/// `TaskInput` is cloned again to allow the function to be re-executed. It's recommended to ensure
107/// that these types are cheap to clone.
108///
109/// Reference-counted types like [`Arc`] are cheap to clone, but each reference contained in a
110/// `TaskInput` will be serialized independently in the persistent cache, and may consume extra disk
111/// space. If an [`Arc`] points to a large type, consider wrapping that type in [`Vc`], so that only
112/// one copy of the value will be serialized.
113pub trait TaskInput:
114    Send + Sync + Clone + Debug + PartialEq + Eq + Hash + TraceRawVcs + Encode + Decode<()>
115{
116    /// This method should resolve any [`Vc`]s nested inside of this object, cloning the object in
117    /// the process. If the input is unresolved ([`TaskInput::is_resolved`]) a "local" resolution
118    /// task is created that runs this method.
119    fn resolve_input(&self) -> impl Future<Output = Result<Self>> + Send + '_ {
120        CloneReady { inner: Some(self) }
121    }
122
123    /// This should return `true` if there are any unresolved [`Vc`]s in the type.
124    ///
125    /// Note that [`Vc`]s can sometimes be internally resolved, so you should call
126    /// [`Vc::is_resolved`] (or rely on the derive macro for this trait) instead of returning `true`
127    /// for any [`Vc`]. [`ResolvedVc::is_resolved`] always returns `true`.
128    ///
129    /// If this returns `true`, a "local" resolution task calling [`TaskInput::resolve_input`] will
130    /// be spawned before the function accepting the arguments is run.
131    ///
132    /// If this returns `false`, the `TaskInput` will be [cloned][Clone] instead of resolved, and
133    /// the function's task will be spawned directly without a resolution step.
134    fn is_resolved(&self) -> bool {
135        true
136    }
137
138    /// This should return true if this object contains a [`Vc`] (or any subtype of [`Vc`]) pointing
139    /// to a cell owned by a transient task.
140    ///
141    /// Any function called with a transient `TaskInput` will be transient. Any [`Vc`] constructed
142    /// in a transient task or in a top-level [`run_once`][crate::run_once] closure will be
143    /// transient.
144    ///
145    /// Internally, a [`Vc`] can be determined to be transient by comparing the owning task's id
146    /// with the [`TRANSIENT_TASK_BIT`][crate::TRANSIENT_TASK_BIT] mask.
147    fn is_transient(&self) -> bool;
148}
149
150macro_rules! impl_task_input {
151    ($($t:ty),*) => {
152        $(
153            impl TaskInput for $t {
154                fn is_transient(&self) -> bool {
155                    false
156                }
157            }
158        )*
159    };
160}
161
162impl_task_input! {
163    (),
164    bool,
165    u8,
166    u16,
167    u32,
168    i32,
169    u64,
170    u128,
171    usize,
172    RcStr,
173    TaskId,
174    ValueTypeId,
175    Duration,
176    String,
177    HashAlgorithm
178}
179
180impl<T> TaskInput for Vec<T>
181where
182    T: TaskInput,
183{
184    fn is_resolved(&self) -> bool {
185        self.iter().all(TaskInput::is_resolved)
186    }
187
188    fn is_transient(&self) -> bool {
189        self.iter().any(TaskInput::is_transient)
190    }
191
192    async fn resolve_input(&self) -> Result<Self> {
193        let mut resolved = Vec::with_capacity(self.len());
194        for value in self {
195            resolved.push(value.resolve_input().await?);
196        }
197        Ok(resolved)
198    }
199}
200
201impl<T> TaskInput for Box<T>
202where
203    T: TaskInput,
204{
205    fn is_resolved(&self) -> bool {
206        self.as_ref().is_resolved()
207    }
208
209    fn is_transient(&self) -> bool {
210        self.as_ref().is_transient()
211    }
212
213    async fn resolve_input(&self) -> Result<Self> {
214        Ok(Box::new(Box::pin(self.as_ref().resolve_input()).await?))
215    }
216}
217
218impl<T> TaskInput for Arc<T>
219where
220    T: TaskInput,
221{
222    fn is_resolved(&self) -> bool {
223        self.as_ref().is_resolved()
224    }
225
226    fn is_transient(&self) -> bool {
227        self.as_ref().is_transient()
228    }
229
230    async fn resolve_input(&self) -> Result<Self> {
231        Ok(Arc::new(Box::pin(self.as_ref().resolve_input()).await?))
232    }
233}
234
235impl<T> TaskInput for ReadRef<T>
236where
237    T: TaskInput,
238{
239    fn is_resolved(&self) -> bool {
240        Self::as_raw_ref(self).is_resolved()
241    }
242
243    fn is_transient(&self) -> bool {
244        Self::as_raw_ref(self).is_transient()
245    }
246
247    async fn resolve_input(&self) -> Result<Self> {
248        Ok(ReadRef::new_owned(
249            Box::pin(Self::as_raw_ref(self).resolve_input()).await?,
250        ))
251    }
252}
253
254impl<T> TaskInput for Option<T>
255where
256    T: TaskInput,
257{
258    fn is_resolved(&self) -> bool {
259        match self {
260            Some(value) => value.is_resolved(),
261            None => true,
262        }
263    }
264
265    fn is_transient(&self) -> bool {
266        match self {
267            Some(value) => value.is_transient(),
268            None => false,
269        }
270    }
271
272    async fn resolve_input(&self) -> Result<Self> {
273        match self {
274            Some(value) => Ok(Some(value.resolve_input().await?)),
275            None => Ok(None),
276        }
277    }
278}
279
280impl<T> TaskInput for Vc<T>
281where
282    T: Send + Sync + ?Sized,
283{
284    fn is_resolved(&self) -> bool {
285        Vc::is_resolved(*self)
286    }
287
288    fn is_transient(&self) -> bool {
289        self.node.is_transient()
290    }
291
292    fn resolve_input(&self) -> impl Future<Output = Result<Self>> + Send + '_ {
293        // It isn't ideal to use this function but it exactly matches this usecase (resolved but
294        // still a Vc)
295        (*self).resolve()
296    }
297}
298
299// `TaskInput` isn't needed/used for a bare `ResolvedVc`, as we'll expose `ResolvedVc` arguments as
300// `Vc`, but it is useful for structs that contain `ResolvedVc` and want to derive `TaskInput`.
301impl<T> TaskInput for ResolvedVc<T>
302where
303    T: Send + Sync + ?Sized,
304{
305    fn is_resolved(&self) -> bool {
306        true
307    }
308
309    fn is_transient(&self) -> bool {
310        self.node.is_transient()
311    }
312}
313
314impl<T> TaskInput for OrdResolvedVc<T>
315where
316    T: Send + Sync + ?Sized,
317{
318    fn is_resolved(&self) -> bool {
319        true
320    }
321
322    fn is_transient(&self) -> bool {
323        self.node.is_transient()
324    }
325}
326
327impl<T> TaskInput for TransientValue<T>
328where
329    T: DynTaskInputs + Clone + Debug + Hash + Eq + TraceRawVcs + 'static,
330{
331    fn is_transient(&self) -> bool {
332        true
333    }
334}
335
336impl<T> Encode for TransientValue<T> {
337    fn encode<E: Encoder>(&self, _encoder: &mut E) -> Result<(), EncodeError> {
338        Err(EncodeError::Other("cannot encode transient task inputs"))
339    }
340}
341
342impl<Context, T> Decode<Context> for TransientValue<T> {
343    fn decode<D: Decoder<Context = Context>>(_decoder: &mut D) -> Result<Self, DecodeError> {
344        Err(DecodeError::Other("cannot decode transient task inputs"))
345    }
346}
347
348impl<T> TaskInput for TransientInstance<T>
349where
350    T: Sync + Send + TraceRawVcs + 'static,
351{
352    fn is_transient(&self) -> bool {
353        true
354    }
355}
356
357impl<T> Encode for TransientInstance<T> {
358    fn encode<E: Encoder>(&self, _encoder: &mut E) -> Result<(), EncodeError> {
359        Err(EncodeError::Other("cannot encode transient task inputs"))
360    }
361}
362
363impl<Context, T> Decode<Context> for TransientInstance<T> {
364    fn decode<D: Decoder<Context = Context>>(_decoder: &mut D) -> Result<Self, DecodeError> {
365        Err(DecodeError::Other("cannot decode transient task inputs"))
366    }
367}
368
369impl<K, V> TaskInput for BTreeMap<K, V>
370where
371    K: TaskInput + Ord,
372    V: TaskInput,
373{
374    async fn resolve_input(&self) -> Result<Self> {
375        let mut new_map = BTreeMap::new();
376        for (k, v) in self {
377            new_map.insert(
378                TaskInput::resolve_input(k).await?,
379                TaskInput::resolve_input(v).await?,
380            );
381        }
382        Ok(new_map)
383    }
384
385    fn is_resolved(&self) -> bool {
386        self.iter()
387            .all(|(k, v)| TaskInput::is_resolved(k) && TaskInput::is_resolved(v))
388    }
389
390    fn is_transient(&self) -> bool {
391        self.iter()
392            .any(|(k, v)| TaskInput::is_transient(k) || TaskInput::is_transient(v))
393    }
394}
395
396impl<T> TaskInput for BTreeSet<T>
397where
398    T: TaskInput + Ord,
399{
400    async fn resolve_input(&self) -> Result<Self> {
401        let mut new_set = BTreeSet::new();
402        for value in self {
403            new_set.insert(TaskInput::resolve_input(value).await?);
404        }
405        Ok(new_set)
406    }
407
408    fn is_resolved(&self) -> bool {
409        self.iter().all(TaskInput::is_resolved)
410    }
411
412    fn is_transient(&self) -> bool {
413        self.iter().any(TaskInput::is_transient)
414    }
415}
416
417impl<K, V> TaskInput for FrozenMap<K, V>
418where
419    K: TaskInput + Ord + 'static,
420    V: TaskInput + 'static,
421{
422    async fn resolve_input(&self) -> Result<Self> {
423        let mut new_entries = Vec::with_capacity(self.len());
424        for (k, v) in self {
425            new_entries.push((
426                TaskInput::resolve_input(k).await?,
427                TaskInput::resolve_input(v).await?,
428            ));
429        }
430        // note: resolving might deduplicate `Vc`s in keys
431        Ok(Self::from(new_entries))
432    }
433
434    fn is_resolved(&self) -> bool {
435        self.iter()
436            .all(|(k, v)| TaskInput::is_resolved(k) && TaskInput::is_resolved(v))
437    }
438
439    fn is_transient(&self) -> bool {
440        self.iter()
441            .any(|(k, v)| TaskInput::is_transient(k) || TaskInput::is_transient(v))
442    }
443}
444
445impl<T> TaskInput for FrozenSet<T>
446where
447    T: TaskInput + Ord + 'static,
448{
449    async fn resolve_input(&self) -> Result<Self> {
450        let mut new_set = Vec::with_capacity(self.len());
451        for value in self {
452            new_set.push(TaskInput::resolve_input(value).await?);
453        }
454        Ok(Self::from_iter(new_set))
455    }
456
457    fn is_resolved(&self) -> bool {
458        self.iter().all(TaskInput::is_resolved)
459    }
460
461    fn is_transient(&self) -> bool {
462        self.iter().any(TaskInput::is_transient)
463    }
464}
465
466/// A thin wrapper around [`Either`] that implements the traits required by [`TaskInput`], notably
467/// [`Encode`] and [`Decode`].
468#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs)]
469pub struct EitherTaskInput<L, R>(pub Either<L, R>);
470
471impl<L, R> Deref for EitherTaskInput<L, R> {
472    type Target = Either<L, R>;
473
474    fn deref(&self) -> &Self::Target {
475        &self.0
476    }
477}
478
479impl<L, R> DerefMut for EitherTaskInput<L, R> {
480    fn deref_mut(&mut self) -> &mut Self::Target {
481        &mut self.0
482    }
483}
484
485impl<L, R> Encode for EitherTaskInput<L, R>
486where
487    L: Encode,
488    R: Encode,
489{
490    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
491        turbo_bincode::either::encode(self, encoder)
492    }
493}
494
495impl<Context, L, R> Decode<Context> for EitherTaskInput<L, R>
496where
497    L: Decode<Context>,
498    R: Decode<Context>,
499{
500    fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
501        turbo_bincode::either::decode(decoder).map(Self)
502    }
503}
504
505impl<L, R> TaskInput for EitherTaskInput<L, R>
506where
507    L: TaskInput,
508    R: TaskInput,
509{
510    fn resolve_input(&self) -> impl Future<Output = Result<Self>> + Send + '_ {
511        self.as_ref().map_either(
512            |l| async move { anyhow::Ok(Self(Either::Left(l.resolve_input().await?))) },
513            |r| async move { anyhow::Ok(Self(Either::Right(r.resolve_input().await?))) },
514        )
515    }
516
517    fn is_resolved(&self) -> bool {
518        self.as_ref()
519            .either(TaskInput::is_resolved, TaskInput::is_resolved)
520    }
521
522    fn is_transient(&self) -> bool {
523        self.as_ref()
524            .either(TaskInput::is_transient, TaskInput::is_transient)
525    }
526}
527
528macro_rules! tuple_impls {
529    ( $( $name:ident )+ ) => {
530        impl<$($name: TaskInput),+> TaskInput for ($($name,)+)
531        where $($name: TaskInput),+
532        {
533            #[allow(non_snake_case)]
534            fn is_resolved(&self) -> bool {
535                let ($($name,)+) = self;
536                $($name.is_resolved() &&)+ true
537            }
538
539            #[allow(non_snake_case)]
540            fn is_transient(&self) -> bool {
541                let ($($name,)+) = self;
542                $($name.is_transient() ||)+ false
543            }
544
545            #[allow(non_snake_case)]
546            async fn resolve_input(&self) -> Result<Self> {
547                let ($($name,)+) = self;
548                Ok(($($name.resolve_input().await?,)+))
549            }
550        }
551    };
552}
553
554// Implement `TaskInput` for all tuples of 1 to 12 elements.
555tuple_impls! { A }
556tuple_impls! { A B }
557tuple_impls! { A B C }
558tuple_impls! { A B C D }
559tuple_impls! { A B C D E }
560tuple_impls! { A B C D E F }
561tuple_impls! { A B C D E F G }
562tuple_impls! { A B C D E F G H }
563tuple_impls! { A B C D E F G H I }
564tuple_impls! { A B C D E F G H I J }
565tuple_impls! { A B C D E F G H I J K }
566tuple_impls! { A B C D E F G H I J K L }
567
568#[cfg(test)]
569mod tests {
570    use turbo_rcstr::rcstr;
571
572    use super::*;
573
574    fn assert_task_input<T>(_: T)
575    where
576        T: TaskInput,
577    {
578    }
579
580    #[test]
581    fn test_no_fields() -> Result<()> {
582        #[turbo_tasks::task_input]
583        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
584        struct NoFields;
585
586        assert_task_input(NoFields);
587        Ok(())
588    }
589
590    #[test]
591    fn test_one_unnamed_field() -> Result<()> {
592        #[turbo_tasks::task_input]
593        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
594        struct OneUnnamedField(u32);
595
596        assert_task_input(OneUnnamedField(42));
597        Ok(())
598    }
599
600    #[test]
601    fn test_multiple_unnamed_fields() -> Result<()> {
602        #[turbo_tasks::task_input]
603        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
604        struct MultipleUnnamedFields(u32, RcStr);
605
606        assert_task_input(MultipleUnnamedFields(42, rcstr!("42")));
607        Ok(())
608    }
609
610    #[test]
611    fn test_one_named_field() -> Result<()> {
612        #[turbo_tasks::task_input]
613        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
614        struct OneNamedField {
615            named: u32,
616        }
617
618        assert_task_input(OneNamedField { named: 42 });
619        Ok(())
620    }
621
622    #[test]
623    fn test_multiple_named_fields() -> Result<()> {
624        #[turbo_tasks::task_input]
625        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
626        struct MultipleNamedFields {
627            named: u32,
628            other: RcStr,
629        }
630
631        assert_task_input(MultipleNamedFields {
632            named: 42,
633            other: rcstr!("42"),
634        });
635        Ok(())
636    }
637
638    #[test]
639    fn test_generic_field() -> Result<()> {
640        #[turbo_tasks::task_input]
641        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
642        struct GenericField<T>(T);
643
644        assert_task_input(GenericField(42));
645        assert_task_input(GenericField(rcstr!("42")));
646        Ok(())
647    }
648
649    #[turbo_tasks::task_input]
650    #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
651    enum OneVariant {
652        Variant,
653    }
654
655    #[test]
656    fn test_one_variant() -> Result<()> {
657        assert_task_input(OneVariant::Variant);
658        Ok(())
659    }
660
661    #[test]
662    fn test_multiple_variants() -> Result<()> {
663        #[turbo_tasks::task_input]
664        #[derive(Clone, PartialEq, Eq, Hash, Debug, Encode, Decode, TraceRawVcs)]
665        enum MultipleVariants {
666            Variant1,
667            Variant2,
668        }
669
670        assert_task_input(MultipleVariants::Variant2);
671        Ok(())
672    }
673
674    #[turbo_tasks::task_input]
675    #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
676    enum MultipleVariantsAndHeterogeneousFields {
677        Variant1,
678        Variant2(u32),
679        Variant3 { named: u32 },
680        Variant4(u32, RcStr),
681        Variant5 { named: u32, other: RcStr },
682    }
683
684    #[test]
685    fn test_multiple_variants_and_heterogeneous_fields() -> Result<()> {
686        assert_task_input(MultipleVariantsAndHeterogeneousFields::Variant5 {
687            named: 42,
688            other: rcstr!("42"),
689        });
690        Ok(())
691    }
692
693    #[test]
694    fn test_nested_variants() -> Result<()> {
695        #[turbo_tasks::task_input]
696        #[derive(Clone, Eq, PartialEq, Hash, Debug, Encode, Decode, TraceRawVcs)]
697        enum NestedVariants {
698            Variant1,
699            Variant2(MultipleVariantsAndHeterogeneousFields),
700            Variant3 { named: OneVariant },
701            Variant4(OneVariant, RcStr),
702            Variant5 { named: OneVariant, other: RcStr },
703        }
704
705        assert_task_input(NestedVariants::Variant5 {
706            named: OneVariant::Variant,
707            other: rcstr!("42"),
708        });
709        assert_task_input(NestedVariants::Variant2(
710            MultipleVariantsAndHeterogeneousFields::Variant5 {
711                named: 42,
712                other: rcstr!("42"),
713            },
714        ));
715        Ok(())
716    }
717}