turbo_tasks/
completion.rs

1use anyhow::Result;
2
3use crate::{self as turbo_tasks, 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
35#[turbo_tasks::value(transparent)]
36pub struct Completions(Vec<ResolvedVc<Completion>>);
37
38#[turbo_tasks::value_impl]
39impl Completions {
40    /// Merges the list of completions into one.
41    #[turbo_tasks::function]
42    pub async fn completed(&self) -> anyhow::Result<Vc<Completion>> {
43        if self.0.len() > 100 {
44            let mid = self.0.len() / 2;
45            let (left, right) = self.0.split_at(mid);
46            let left = Vc::<Completions>::cell(left.to_vec());
47            let right = Vc::<Completions>::cell(right.to_vec());
48            let left = left.completed();
49            let right = right.completed();
50            left.await?;
51            right.await?;
52            Ok(Completion::new())
53        } else {
54            self.0
55                .iter()
56                .map(|&c| async move {
57                    // Wraps the completion in a new completion. This makes it cheaper to restore
58                    // since it doesn't need to restore the original task resp task chain.
59                    wrap(*c).await?;
60                    Ok(())
61                })
62                .try_join()
63                .await?;
64            Ok(Completion::new())
65        }
66    }
67}
68
69#[turbo_tasks::function]
70async fn wrap(completion: Vc<Completion>) -> Result<Vc<Completion>> {
71    completion.await?;
72    Ok(Completion::new())
73}