turbo_tasks/completion.rs
1use anyhow::Result;
2
3use crate::{self as turbo_tasks, RawVc, ResolvedVc, TryJoinIterExt, Vc};
4
5/// Just an empty type, but it's never equal to itself.
6///
7/// [`Vc<Completion>`] can be used as return value instead of `()` to have a concrete reference that
8/// can be awaited. It will invalidate the awaiting task everytime the referenced task has been
9/// executed.
10///
11/// Note: [`PartialEq`] is not implemented since it doesn't make sense to compare `Completion` this
12/// way. You probably want to use [`ReadRef::ptr_eq`][crate::ReadRef::ptr_eq] instead.
13#[turbo_tasks::value(cell = "new", eq = "manual")]
14#[derive(Debug)]
15pub struct Completion;
16
17#[turbo_tasks::value_impl]
18impl Completion {
19 /// This will always be the same and never invalidates the reading task.
20 #[turbo_tasks::function]
21 pub fn immutable() -> Vc<Self> {
22 Completion::cell(Completion)
23 }
24}
25
26// no #[turbo_tasks::value_impl] to inline new into the caller task
27// this ensures it's re-created on each execution
28impl Completion {
29 /// This will always be a new completion and invalidates the reading task.
30 pub fn new() -> Vc<Self> {
31 Completion::cell(Completion)
32 }
33
34 /// Uses the previous completion. Can be used to cancel without triggering a
35 /// new invalidation.
36 pub fn unchanged() -> Vc<Self> {
37 // This is the same code that Completion::cell uses except that it
38 // only updates the cell when it is empty (Completion::cell opted-out of
39 // that via `#[turbo_tasks::value(cell = "new")]`)
40 let cell = turbo_tasks::macro_helpers::find_cell_by_type(
41 <Completion as crate::VcValueType>::get_value_type_id(),
42 );
43 cell.conditional_update(|old| old.is_none().then_some(Completion));
44 let raw: RawVc = cell.into();
45 raw.into()
46 }
47}
48
49#[turbo_tasks::value(transparent)]
50pub struct Completions(Vec<ResolvedVc<Completion>>);
51
52#[turbo_tasks::value_impl]
53impl Completions {
54 /// Merges multiple completions into one. The passed list will be part of
55 /// the cache key, so this function should not be used with varying lists.
56 ///
57 /// Varying lists should use `Vc::cell(list).completed()`
58 /// instead.
59 #[turbo_tasks::function]
60 pub fn all(completions: Vec<ResolvedVc<Completion>>) -> Vc<Completion> {
61 Vc::<Completions>::cell(completions).completed()
62 }
63
64 /// Merges the list of completions into one.
65 #[turbo_tasks::function]
66 pub async fn completed(&self) -> anyhow::Result<Vc<Completion>> {
67 if self.0.len() > 100 {
68 let mid = self.0.len() / 2;
69 let (left, right) = self.0.split_at(mid);
70 let left = Vc::<Completions>::cell(left.to_vec());
71 let right = Vc::<Completions>::cell(right.to_vec());
72 let left = left.completed();
73 let right = right.completed();
74 left.await?;
75 right.await?;
76 Ok(Completion::new())
77 } else {
78 self.0
79 .iter()
80 .map(|&c| async move {
81 // Wraps the completion in a new completion. This makes it cheaper to restore
82 // since it doesn't need to restore the original task resp task chain.
83 wrap(*c).await?;
84 Ok(())
85 })
86 .try_join()
87 .await?;
88 Ok(Completion::new())
89 }
90 }
91}
92
93#[turbo_tasks::function]
94pub async fn wrap(completion: Vc<Completion>) -> Result<Vc<Completion>> {
95 completion.await?;
96 Ok(Completion::new())
97}