Skip to main content

turbo_tasks/
manager.rs

1use std::{
2    cmp::Reverse,
3    fmt::{Debug, Display},
4    future::Future,
5    hash::{BuildHasher, BuildHasherDefault},
6    mem::take,
7    ops::Deref,
8    panic::AssertUnwindSafe,
9    pin::Pin,
10    process::abort,
11    sync::{
12        Arc, Mutex, RwLock, Weak,
13        atomic::{AtomicBool, AtomicUsize, Ordering},
14    },
15    time::{Duration, Instant},
16};
17
18use anyhow::{Result, anyhow};
19use auto_hash_map::AutoMap;
20use bincode::{Decode, Encode};
21use either::Either;
22use futures::FutureExt;
23use rustc_hash::{FxBuildHasher, FxHasher};
24use serde::{Deserialize, Serialize};
25use smallvec::SmallVec;
26use tokio::{select, sync::mpsc::Receiver, task_local};
27use tracing::{Instrument, Span, instrument};
28use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash128};
29
30use crate::{
31    CellId, Completion, InvalidationReason, InvalidationReasonSet, OutputContent, RawVc,
32    ReadCellOptions, ReadOutputOptions, ResolvedVc, SharedReference, TaskId, TraitMethod,
33    ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType,
34    backend::{
35        Backend, CellContent, CellHash, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType,
36        TurboTasksExecutionError, TypedCellContent, VerificationMode,
37    },
38    capture_future::CaptureFuture,
39    dyn_task_inputs::DynTaskInputsStorage,
40    event::{Event, EventListener},
41    id::{ExecutionId, LocalTaskId, TraitTypeId},
42    keyed::KeyedEq,
43    local_task_tracker::LocalTaskTracker,
44    macro_helpers::NativeFunction,
45    message_queue::{CompilationEvent, CompilationEventQueue},
46    priority_runner::{Executor, PriorityRunner},
47    registry,
48    serialization_invalidation::SerializationInvalidator,
49    task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
50    task_statistics::TaskStatisticsApi,
51    trace::TraceRawVcs,
52    util::{IdFactory, StaticOrArc},
53};
54
55/// Common base trait for [`TurboTasksApi`] and [`TurboTasks`]. Provides APIs for creating tasks
56/// from function calls.
57pub trait TurboTasksCallApi: Sync + Send {
58    /// Calls a native function with arguments. Resolves arguments when needed
59    /// with a wrapper task.
60    ///
61    /// `inputs_resolved` is `TaskInput::is_resolved(&args)` computed at the macro callsite on
62    /// the concrete tuple type — when [`InputResolution::Resolved`], the fast path skips wrapper
63    /// task creation.
64    fn dynamic_call(
65        &self,
66        native_fn: &'static NativeFunction,
67        this: Option<RawVc>,
68        arg: &mut dyn DynTaskInputsStorage,
69        inputs_resolved: InputResolution,
70        persistence: TaskPersistence,
71    ) -> RawVc;
72    /// Call a native function with arguments.
73    /// All inputs must be resolved.
74    fn native_call(
75        &self,
76        native_fn: &'static NativeFunction,
77        this: Option<RawVc>,
78        arg: &mut dyn DynTaskInputsStorage,
79        persistence: TaskPersistence,
80    ) -> RawVc;
81    /// Calls a trait method with arguments. First input is the `self` object.
82    /// Uses a wrapper task to resolve.
83    ///
84    /// `inputs_resolved` is the macro-site `InputResolution` of the *exposed* tuple; when filtering
85    /// is involved, the post-filter value is computed inside the filter functor and supersedes
86    /// this argument.
87    fn trait_call(
88        &self,
89        trait_method: &'static TraitMethod,
90        this: RawVc,
91        arg: &mut dyn DynTaskInputsStorage,
92        inputs_resolved: InputResolution,
93        persistence: TaskPersistence,
94    ) -> RawVc;
95
96    fn run(
97        &self,
98        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
99    ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>>;
100    fn run_once(
101        &self,
102        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
103    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
104    fn run_once_with_reason(
105        &self,
106        reason: StaticOrArc<dyn InvalidationReason>,
107        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
108    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
109    fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
110
111    /// Sends a compilation event to subscribers.
112    fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
113
114    /// Returns a human-readable name for the given task.
115    fn get_task_name(&self, task: TaskId) -> String;
116}
117
118/// A type-erased subset of [`TurboTasks`] stored inside a thread local when we're in a turbo task
119/// context. Returned by the [`turbo_tasks`] helper function.
120///
121/// This trait is needed because thread locals cannot contain an unresolved [`Backend`] type
122/// parameter.
123pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
124    fn invalidate(&self, task: TaskId);
125    fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>);
126
127    fn invalidate_serialization(&self, task: TaskId);
128
129    fn try_read_task_output(
130        &self,
131        task: TaskId,
132        options: ReadOutputOptions,
133    ) -> Result<Result<RawVc, EventListener>>;
134
135    fn try_read_task_cell(
136        &self,
137        task: TaskId,
138        index: CellId,
139        options: ReadCellOptions,
140    ) -> Result<Result<TypedCellContent, EventListener>>;
141
142    /// Reads a [`RawVc::LocalOutput`]. If the task has completed, returns the [`RawVc`] the local
143    /// task points to.
144    ///
145    /// The returned [`RawVc`] may also be a [`RawVc::LocalOutput`], so this may need to be called
146    /// recursively or in a loop.
147    ///
148    /// This does not accept a consistency argument, as you cannot control consistency of a read of
149    /// an operation owned by your own task. Strongly consistent reads are only allowed on
150    /// [`OperationVc`]s, which should never be local tasks.
151    ///
152    /// No dependency tracking will happen as a result of this function call, as it's a no-op for a
153    /// task to depend on itself.
154    ///
155    /// [`OperationVc`]: crate::OperationVc
156    fn try_read_local_output(
157        &self,
158        execution_id: ExecutionId,
159        local_task_id: LocalTaskId,
160    ) -> Result<Result<RawVc, EventListener>>;
161
162    fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
163
164    fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
165    fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
166    fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
167
168    /// INVALIDATION: Be careful with this, it will not track dependencies, so
169    /// using it could break cache invalidation.
170    fn try_read_own_task_cell(
171        &self,
172        current_task: TaskId,
173        index: CellId,
174    ) -> Result<TypedCellContent>;
175
176    fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
177    fn update_own_task_cell(
178        &self,
179        task: TaskId,
180        index: CellId,
181        content: CellContent,
182        updated_key_hashes: Option<SmallVec<[u64; 2]>>,
183        content_hash: Option<CellHash>,
184        verification_mode: VerificationMode,
185    );
186    fn mark_own_task_as_finished(&self, task: TaskId);
187
188    fn connect_task(&self, task: TaskId);
189
190    /// Wraps the given future in the current task.
191    ///
192    /// Beware: this method is not safe to use in production code. It is only intended for use in
193    /// tests and for debugging purposes.
194    fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
195
196    fn task_statistics(&self) -> &TaskStatisticsApi;
197
198    fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
199
200    fn subscribe_to_compilation_events(
201        &self,
202        event_types: Option<Vec<String>>,
203    ) -> Receiver<Arc<dyn CompilationEvent>>;
204
205    // Returns true if TurboTasks is configured to track dependencies.
206    fn is_tracking_dependencies(&self) -> bool;
207}
208
209/// A wrapper around a value that is unused.
210pub struct Unused<T> {
211    inner: T,
212}
213
214impl<T> Unused<T> {
215    /// Creates a new unused value.
216    ///
217    /// # Safety
218    ///
219    /// The wrapped value must not be used.
220    pub unsafe fn new_unchecked(inner: T) -> Self {
221        Self { inner }
222    }
223
224    /// Get the inner value, without consuming the `Unused` wrapper.
225    ///
226    /// # Safety
227    ///
228    /// The user need to make sure that the value stays unused.
229    pub unsafe fn get_unchecked(&self) -> &T {
230        &self.inner
231    }
232
233    /// Unwraps the value, consuming the `Unused` wrapper.
234    pub fn into(self) -> T {
235        self.inner
236    }
237}
238
239#[allow(clippy::manual_non_exhaustive)]
240pub struct UpdateInfo {
241    pub duration: Duration,
242    pub tasks: usize,
243    pub reasons: InvalidationReasonSet,
244    #[allow(dead_code)]
245    placeholder_for_future_fields: (),
246}
247
248#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
249pub enum TaskPersistence {
250    /// Tasks that may be persisted across sessions using serialization.
251    Persistent,
252
253    /// Tasks that will be persisted in memory for the life of this session, but won't persist
254    /// between sessions.
255    ///
256    /// This is used for [root tasks][TurboTasks::spawn_root_task] and tasks with an argument of
257    /// type [`TransientValue`][crate::value::TransientValue] or
258    /// [`TransientInstance`][crate::value::TransientInstance].
259    Transient,
260}
261
262impl Display for TaskPersistence {
263    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264        match self {
265            TaskPersistence::Persistent => write!(f, "persistent"),
266            TaskPersistence::Transient => write!(f, "transient"),
267        }
268    }
269}
270
271/// Whether a task call's inputs are already resolved, decided on the concrete input tuple at the
272/// call site. Travels alongside [`TaskPersistence`] through [`dynamic_call`] / [`trait_call`].
273#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
274pub enum InputResolution {
275    /// All inputs (and `this`, where applicable) are resolved — eligible for the synchronous fast
276    /// path with no async resolution task.
277    Resolved,
278    /// At least one input is unresolved and must be resolved in a local task first.
279    Unresolved,
280}
281
282impl InputResolution {
283    #[inline]
284    pub fn from_is_resolved(is_resolved: bool) -> Self {
285        if is_resolved {
286            Self::Resolved
287        } else {
288            Self::Unresolved
289        }
290    }
291
292    #[inline]
293    pub fn is_resolved(self) -> bool {
294        matches!(self, Self::Resolved)
295    }
296}
297
298#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
299pub enum ReadConsistency {
300    /// The default behavior for most APIs. Reads are faster, but may return stale values, which
301    /// may later trigger re-computation.
302    #[default]
303    Eventual,
304    /// Ensures all dependencies are fully resolved before returning the cell or output data, at
305    /// the cost of slower reads.
306    ///
307    /// Top-level code that returns data to the user should use strongly consistent reads.
308    Strong,
309}
310
311#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub enum ReadCellTracking {
313    /// Reads are tracked as dependencies of the current task.
314    Tracked {
315        /// The key used for the dependency
316        key: Option<u64>,
317    },
318    /// The read is only tracked when there is an error, otherwise it is untracked.
319    ///
320    /// INVALIDATION: Be careful with this, it will not track dependencies, so
321    /// using it could break cache invalidation.
322    TrackOnlyError,
323    /// The read is not tracked as a dependency of the current task.
324    ///
325    /// INVALIDATION: Be careful with this, it will not track dependencies, so
326    /// using it could break cache invalidation.
327    Untracked,
328}
329
330impl ReadCellTracking {
331    pub fn should_track(&self, is_err: bool) -> bool {
332        match self {
333            ReadCellTracking::Tracked { .. } => true,
334            ReadCellTracking::TrackOnlyError => is_err,
335            ReadCellTracking::Untracked => false,
336        }
337    }
338
339    pub fn key(&self) -> Option<u64> {
340        match self {
341            ReadCellTracking::Tracked { key } => *key,
342            ReadCellTracking::TrackOnlyError => None,
343            ReadCellTracking::Untracked => None,
344        }
345    }
346}
347
348impl Default for ReadCellTracking {
349    fn default() -> Self {
350        ReadCellTracking::Tracked { key: None }
351    }
352}
353
354impl Display for ReadCellTracking {
355    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356        match self {
357            ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
358            ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
359            ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
360            ReadCellTracking::Untracked => write!(f, "untracked"),
361        }
362    }
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
366pub enum ReadTracking {
367    /// Reads are tracked as dependencies of the current task.
368    #[default]
369    Tracked,
370    /// The read is only tracked when there is an error, otherwise it is untracked.
371    ///
372    /// INVALIDATION: Be careful with this, it will not track dependencies, so
373    /// using it could break cache invalidation.
374    TrackOnlyError,
375    /// The read is not tracked as a dependency of the current task.
376    ///
377    /// INVALIDATION: Be careful with this, it will not track dependencies, so
378    /// using it could break cache invalidation.
379    Untracked,
380}
381
382impl ReadTracking {
383    pub fn should_track(&self, is_err: bool) -> bool {
384        match self {
385            ReadTracking::Tracked => true,
386            ReadTracking::TrackOnlyError => is_err,
387            ReadTracking::Untracked => false,
388        }
389    }
390}
391
392impl Display for ReadTracking {
393    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394        match self {
395            ReadTracking::Tracked => write!(f, "tracked"),
396            ReadTracking::TrackOnlyError => write!(f, "track only error"),
397            ReadTracking::Untracked => write!(f, "untracked"),
398        }
399    }
400}
401
402#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
403pub enum TaskPriority {
404    #[default]
405    Initial,
406    Invalidation {
407        priority: Reverse<u32>,
408    },
409    Recomputation,
410}
411
412impl TaskPriority {
413    pub fn invalidation(priority: u32) -> Self {
414        Self::Invalidation {
415            priority: Reverse(priority),
416        }
417    }
418
419    pub fn initial() -> Self {
420        Self::Initial
421    }
422
423    pub fn leaf() -> Self {
424        Self::Invalidation {
425            priority: Reverse(0),
426        }
427    }
428
429    pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
430        match self {
431            TaskPriority::Initial => parent_priority,
432            TaskPriority::Invalidation { priority } => {
433                if let TaskPriority::Invalidation {
434                    priority: parent_priority,
435                } = parent_priority
436                    && priority.0 < parent_priority.0
437                {
438                    Self::Invalidation {
439                        priority: Reverse(parent_priority.0.saturating_add(1)),
440                    }
441                } else {
442                    *self
443                }
444            }
445            TaskPriority::Recomputation => TaskPriority::Recomputation,
446        }
447    }
448}
449
450impl Display for TaskPriority {
451    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452        match self {
453            TaskPriority::Initial => write!(f, "initial"),
454            TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
455            TaskPriority::Recomputation => write!(f, "recomputation"),
456        }
457    }
458}
459
460enum ScheduledTask {
461    Task {
462        task_id: TaskId,
463        span: Span,
464    },
465    LocalTask {
466        ty: LocalTaskSpec,
467        persistence: TaskPersistence,
468        local_task_id: LocalTaskId,
469        global_task_state: CurrentTaskStateHandle,
470        span: Span,
471    },
472}
473
474pub struct TurboTasks<B: Backend + 'static> {
475    this: Weak<Self>,
476    backend: B,
477    execution_id_factory: IdFactory<ExecutionId>,
478    stopped: AtomicBool,
479    currently_scheduled_foreground_jobs: AtomicUsize,
480    currently_scheduled_background_jobs: AtomicUsize,
481    scheduled_tasks: AtomicUsize,
482    priority_runner:
483        Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
484    start: Mutex<Option<Instant>>,
485    aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
486    /// Event that is triggered when currently_scheduled_foreground_jobs becomes non-zero
487    event_foreground_start: Event,
488    /// Event that is triggered when all foreground jobs are done
489    /// (currently_scheduled_foreground_jobs becomes zero)
490    event_foreground_done: Event,
491    /// Event that is triggered when all background jobs are done
492    event_background_done: Event,
493    compilation_events: CompilationEventQueue,
494}
495
496/// Information about a non-local task. A non-local task can contain multiple "local" tasks, which
497/// all share the same non-local task state.
498///
499/// A non-local task is one that:
500///
501/// - Has a unique task id.
502/// - Is potentially cached.
503/// - The backend is aware of.
504struct CurrentTaskState {
505    task_id: Option<TaskId>,
506    execution_id: ExecutionId,
507    priority: TaskPriority,
508
509    /// True if the current task has state in cells (interior mutability).
510    /// Only tracked when verify_determinism feature is enabled.
511    #[cfg(feature = "verify_determinism")]
512    stateful: bool,
513
514    /// True if the current task uses an external invalidator
515    has_invalidator: bool,
516
517    /// True if we're in a top-level task (e.g. `.run_once(...)` or `.run(...)`).
518    /// Eventually consistent reads are not allowed in top-level tasks.
519    in_top_level_task: bool,
520
521    /// Tracks how many cells of each type has been allocated so far during this task execution.
522    /// When a task is re-executed, the cell count may not match the existing cell vec length.
523    ///
524    /// This is taken (and becomes `None`) during teardown of a task.
525    cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
526
527    /// Tracks execution of Local tasks (and detached test futures) created during this global
528    /// task's execution.
529    local_tasks: LocalTaskTracker,
530}
531
532impl CurrentTaskState {
533    fn new(
534        task_id: TaskId,
535        execution_id: ExecutionId,
536        priority: TaskPriority,
537        in_top_level_task: bool,
538    ) -> Self {
539        Self {
540            task_id: Some(task_id),
541            execution_id,
542            priority,
543            #[cfg(feature = "verify_determinism")]
544            stateful: false,
545            has_invalidator: false,
546            in_top_level_task,
547            cell_counters: Some(AutoMap::default()),
548            local_tasks: LocalTaskTracker::new(),
549        }
550    }
551
552    fn new_temporary(
553        execution_id: ExecutionId,
554        priority: TaskPriority,
555        in_top_level_task: bool,
556    ) -> Self {
557        Self {
558            task_id: None,
559            execution_id,
560            priority,
561            #[cfg(feature = "verify_determinism")]
562            stateful: false,
563            has_invalidator: false,
564            in_top_level_task,
565            cell_counters: None,
566            local_tasks: LocalTaskTracker::new(),
567        }
568    }
569
570    fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
571        if self.execution_id != expected_execution_id {
572            panic!(
573                "Local tasks can only be scheduled/awaited within the same execution of the \
574                 parent task that created them"
575            );
576        }
577    }
578}
579
580/// A shareable current-task state handle with the immutable task ID cached
581/// outside the lock. The rest of the state is mutated by global and local
582/// tasks, but the task ID is fixed for the lifetime of an execution.
583#[derive(Clone)]
584struct CurrentTaskStateHandle {
585    inner: Arc<CurrentTaskStateInner>,
586}
587
588struct CurrentTaskStateInner {
589    current_task_id: Option<TaskId>,
590    state: RwLock<CurrentTaskState>,
591}
592
593impl CurrentTaskStateHandle {
594    fn new(state: CurrentTaskState) -> Self {
595        Self {
596            inner: Arc::new(CurrentTaskStateInner {
597                current_task_id: state.task_id,
598                state: RwLock::new(state),
599            }),
600        }
601    }
602
603    fn current_task_id(&self) -> Option<TaskId> {
604        self.inner.current_task_id
605    }
606}
607
608impl Deref for CurrentTaskStateHandle {
609    type Target = RwLock<CurrentTaskState>;
610
611    fn deref(&self) -> &Self::Target {
612        &self.inner.state
613    }
614}
615
616// TODO implement our own thread pool and make these thread locals instead
617task_local! {
618    /// The current TurboTasks instance
619    static TURBO_TASKS: Arc<dyn TurboTasksApi>;
620
621    static CURRENT_TASK_STATE: CurrentTaskStateHandle;
622
623    /// Temporarily suppresses the eventual consistency check in top-level tasks.
624    /// This is used by strongly consistent reads to allow them to succeed in top-level tasks.
625    /// This is NOT shared across local tasks (unlike CURRENT_TASK_STATE), so it's safe
626    /// to set/unset without race conditions.
627    pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
628}
629
630impl<B: Backend + 'static> TurboTasks<B> {
631    // TODO better lifetime management for turbo tasks
632    // consider using unsafe for the task_local turbo tasks
633    // that should be safe as long tasks can't outlife turbo task
634    // so we probably want to make sure that all tasks are joined
635    // when trying to drop turbo tasks
636    pub fn new(backend: B) -> Arc<Self> {
637        let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
638        let this = Arc::new_cyclic(|this| Self {
639            this: this.clone(),
640            backend,
641            execution_id_factory,
642            stopped: AtomicBool::new(false),
643            currently_scheduled_foreground_jobs: AtomicUsize::new(0),
644            currently_scheduled_background_jobs: AtomicUsize::new(0),
645            scheduled_tasks: AtomicUsize::new(0),
646            priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
647            start: Default::default(),
648            aggregated_update: Default::default(),
649            event_foreground_done: Event::new(|| {
650                || "TurboTasks::event_foreground_done".to_string()
651            }),
652            event_foreground_start: Event::new(|| {
653                || "TurboTasks::event_foreground_start".to_string()
654            }),
655            event_background_done: Event::new(|| {
656                || "TurboTasks::event_background_done".to_string()
657            }),
658            compilation_events: CompilationEventQueue::default(),
659        });
660        this.backend.startup(&*this);
661        this
662    }
663
664    pub fn pin(&self) -> Arc<Self> {
665        self.this.upgrade().unwrap()
666    }
667
668    /// Creates a new root task
669    pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
670    where
671        T: ?Sized,
672        F: Fn() -> Fut + Send + Sync + Clone + 'static,
673        Fut: Future<Output = Result<Vc<T>>> + Send,
674    {
675        let id = self.backend.create_transient_task(
676            TransientTaskType::Root(Box::new(move || {
677                let functor = functor.clone();
678                Box::pin(async move {
679                    mark_top_level_task();
680                    let raw_vc = functor().await?.node;
681                    raw_vc.to_non_local().await
682                })
683            })),
684            self,
685        );
686        self.schedule(id, TaskPriority::initial());
687        id
688    }
689
690    pub fn dispose_root_task(&self, task_id: TaskId) {
691        self.backend.dispose_root_task(task_id, self);
692    }
693
694    // TODO make sure that all dependencies settle before reading them
695    /// Creates a new root task, that is only executed once.
696    /// Dependencies will not invalidate the task.
697    #[track_caller]
698    fn spawn_once_task<T, Fut>(&self, future: Fut)
699    where
700        T: ?Sized,
701        Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
702    {
703        let id = self.backend.create_transient_task(
704            TransientTaskType::Once(Box::pin(async move {
705                mark_top_level_task();
706                let raw_vc = future.await?.node;
707                raw_vc.to_non_local().await
708            })),
709            self,
710        );
711        self.schedule(id, TaskPriority::initial());
712    }
713
714    pub async fn run_once<T: TraceRawVcs + Send + 'static>(
715        &self,
716        future: impl Future<Output = Result<T>> + Send + 'static,
717    ) -> Result<T> {
718        let (tx, rx) = tokio::sync::oneshot::channel();
719        self.spawn_once_task(async move {
720            mark_top_level_task();
721            let result = future.await;
722            tx.send(result)
723                .map_err(|_| anyhow!("unable to send result"))?;
724            Ok(Completion::new())
725        });
726
727        rx.await?
728    }
729
730    #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
731    pub async fn run<T: TraceRawVcs + Send + 'static>(
732        &self,
733        future: impl Future<Output = Result<T>> + Send + 'static,
734    ) -> Result<T, TurboTasksExecutionError> {
735        self.begin_foreground_job();
736        // it's okay for execution ids to overflow and wrap, they're just used for an assert
737        let execution_id = self.execution_id_factory.wrapping_get();
738        let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
739            execution_id,
740            TaskPriority::initial(),
741            true, // in_top_level_task
742        ));
743
744        let result = TURBO_TASKS
745            .scope(
746                self.pin(),
747                CURRENT_TASK_STATE.scope(current_task_state, async {
748                    let result = CaptureFuture::new(future).await;
749
750                    // wait for all spawned local tasks using `local` to finish
751                    wait_for_local_tasks().await;
752
753                    match result {
754                        Ok(Ok(value)) => Ok(value),
755                        Ok(Err(err)) => Err(err.into()),
756                        Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
757                    }
758                }),
759            )
760            .await;
761        self.finish_foreground_job();
762        result
763    }
764
765    pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
766        let this = self.pin();
767        tokio::spawn(async move {
768            this.pin()
769                .run_once(async move {
770                    this.finish_foreground_job();
771                    future.await;
772                    this.begin_foreground_job();
773                    Ok(())
774                })
775                .await
776                .unwrap()
777        });
778    }
779
780    pub(crate) fn native_call(
781        &self,
782        native_fn: &'static NativeFunction,
783        this: Option<RawVc>,
784        arg: &mut dyn DynTaskInputsStorage,
785        persistence: TaskPersistence,
786    ) -> RawVc {
787        RawVc::task_output(self.backend.get_or_create_task(
788            native_fn,
789            this,
790            arg,
791            current_task_if_available("turbo_function calls"),
792            persistence,
793            self,
794        ))
795    }
796
797    pub fn dynamic_call(
798        &self,
799        native_fn: &'static NativeFunction,
800        this: Option<RawVc>,
801        arg: &mut dyn DynTaskInputsStorage,
802        inputs_resolved: InputResolution,
803        persistence: TaskPersistence,
804    ) -> RawVc {
805        if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
806            return self.native_call(native_fn, this, arg, persistence);
807        }
808        // Need async resolution — must move the arg to the heap now
809        let arg = arg.take_box();
810        let task_type = LocalTaskSpec {
811            task_type: LocalTaskType::ResolveNative { native_fn },
812            this,
813            arg,
814        };
815        self.schedule_local_task(task_type, persistence)
816    }
817
818    pub fn trait_call(
819        &self,
820        trait_method: &'static TraitMethod,
821        this: RawVc,
822        arg: &mut dyn DynTaskInputsStorage,
823        inputs_resolved: InputResolution,
824        persistence: TaskPersistence,
825    ) -> RawVc {
826        // avoid creating a wrapper task if self is already resolved
827        // for resolved cells we already know the value type so we can lookup the
828        // function
829        if let Some((_, cell_id)) = this.as_task_cell() {
830            match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
831                Some(native_fn) => {
832                    if let Some(filter) = native_fn.arg_meta.filter_owned {
833                        let (resolved, mut arg) = (filter)(arg);
834                        return self.dynamic_call(
835                            native_fn,
836                            Some(this),
837                            &mut arg,
838                            resolved,
839                            persistence,
840                        );
841                    } else {
842                        return self.dynamic_call(
843                            native_fn,
844                            Some(this),
845                            arg,
846                            inputs_resolved,
847                            persistence,
848                        );
849                    }
850                }
851                None => {
852                    // We are destined to fail at this point, but we just retry resolution in the
853                    // local task since we cannot report an error from here.
854                    // TODO: A panic seems appropriate since the immediate caller is to blame
855                }
856            }
857        }
858
859        // create a wrapper task to resolve all inputs
860        let task_type = LocalTaskSpec {
861            task_type: LocalTaskType::ResolveTrait { trait_method },
862            this: Some(this),
863            arg: arg.take_box(),
864        };
865
866        self.schedule_local_task(task_type, persistence)
867    }
868
869    #[track_caller]
870    pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
871        self.begin_foreground_job();
872        self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
873
874        self.priority_runner.schedule(
875            &self.pin(),
876            ScheduledTask::Task {
877                task_id,
878                span: Span::current(),
879            },
880            priority,
881        );
882    }
883
884    fn schedule_local_task(
885        &self,
886        ty: LocalTaskSpec,
887        // if this is a `LocalTaskType::Resolve*`, we may spawn another task with this persistence,
888        persistence: TaskPersistence,
889    ) -> RawVc {
890        let task_type = ty.task_type;
891        let (global_task_state, execution_id, priority, local_task_id) =
892            CURRENT_TASK_STATE.with(|gts| {
893                let mut gts_write = gts.write().unwrap();
894                let local_task_id = gts_write.local_tasks.create(task_type);
895                (
896                    gts.clone(),
897                    gts_write.execution_id,
898                    gts_write.priority,
899                    local_task_id,
900                )
901            });
902
903        self.priority_runner.schedule(
904            &self.pin(),
905            ScheduledTask::LocalTask {
906                ty,
907                persistence,
908                local_task_id,
909                global_task_state,
910                span: Span::current(),
911            },
912            priority,
913        );
914
915        RawVc::local_output(execution_id, local_task_id, persistence)
916    }
917
918    fn begin_foreground_job(&self) {
919        if self
920            .currently_scheduled_foreground_jobs
921            .fetch_add(1, Ordering::AcqRel)
922            == 0
923        {
924            *self.start.lock().unwrap() = Some(Instant::now());
925            self.event_foreground_start.notify(usize::MAX);
926            self.backend.idle_end(self);
927        }
928    }
929
930    fn finish_foreground_job(&self) {
931        if self
932            .currently_scheduled_foreground_jobs
933            .fetch_sub(1, Ordering::AcqRel)
934            == 1
935        {
936            self.backend.idle_start(self);
937            // That's not super race-condition-safe, but it's only for
938            // statistical reasons
939            let total = self.scheduled_tasks.load(Ordering::Acquire);
940            self.scheduled_tasks.store(0, Ordering::Release);
941            if let Some(start) = *self.start.lock().unwrap() {
942                let (update, _) = &mut *self.aggregated_update.lock().unwrap();
943                if let Some(update) = update.as_mut() {
944                    update.0 += start.elapsed();
945                    update.1 += total;
946                } else {
947                    *update = Some((start.elapsed(), total));
948                }
949            }
950            self.event_foreground_done.notify(usize::MAX);
951        }
952    }
953
954    fn begin_background_job(&self) {
955        self.currently_scheduled_background_jobs
956            .fetch_add(1, Ordering::Relaxed);
957    }
958
959    fn finish_background_job(&self) {
960        if self
961            .currently_scheduled_background_jobs
962            .fetch_sub(1, Ordering::Relaxed)
963            == 1
964        {
965            self.event_background_done.notify(usize::MAX);
966        }
967    }
968
969    pub fn get_in_progress_count(&self) -> usize {
970        self.currently_scheduled_foreground_jobs
971            .load(Ordering::Acquire)
972    }
973
974    /// Waits for the given task to finish executing. This works by performing an untracked read,
975    /// and discarding the value of the task output.
976    ///
977    /// [`ReadConsistency::Eventual`] means that this will return after the task executes, but
978    /// before all dependencies have completely settled.
979    ///
980    /// [`ReadConsistency::Strong`] means that this will also wait for the task and all dependencies
981    /// to fully settle before returning.
982    ///
983    /// As this function is typically called in top-level code that waits for results to be ready
984    /// for the user to access, most callers should use [`ReadConsistency::Strong`].
985    pub async fn wait_task_completion(
986        &self,
987        id: TaskId,
988        consistency: ReadConsistency,
989    ) -> Result<()> {
990        read_task_output(
991            self,
992            id,
993            ReadOutputOptions {
994                // INVALIDATION: This doesn't return a value, only waits for it to be ready.
995                tracking: ReadTracking::Untracked,
996                consistency,
997            },
998        )
999        .await?;
1000        Ok(())
1001    }
1002
1003    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1004    /// (`aggregation`). Will wait until an update happens.
1005    pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1006        self.aggregated_update_info(aggregation, Duration::MAX)
1007            .await
1008            .unwrap()
1009    }
1010
1011    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1012    /// (`aggregation`). Will only return None when the timeout is reached while
1013    /// waiting for the first update.
1014    pub async fn aggregated_update_info(
1015        &self,
1016        aggregation: Duration,
1017        timeout: Duration,
1018    ) -> Option<UpdateInfo> {
1019        let listener = self
1020            .event_foreground_done
1021            .listen_with_note(|| || "wait for update info".to_string());
1022        let wait_for_finish = {
1023            let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1024            if aggregation.is_zero() {
1025                if let Some((duration, tasks)) = update.take() {
1026                    return Some(UpdateInfo {
1027                        duration,
1028                        tasks,
1029                        reasons: take(reason_set),
1030                        placeholder_for_future_fields: (),
1031                    });
1032                } else {
1033                    true
1034                }
1035            } else {
1036                update.is_none()
1037            }
1038        };
1039        if wait_for_finish {
1040            if timeout == Duration::MAX {
1041                // wait for finish
1042                listener.await;
1043            } else {
1044                // wait for start, then wait for finish or timeout
1045                let start_listener = self
1046                    .event_foreground_start
1047                    .listen_with_note(|| || "wait for update info".to_string());
1048                if self
1049                    .currently_scheduled_foreground_jobs
1050                    .load(Ordering::Acquire)
1051                    == 0
1052                {
1053                    start_listener.await;
1054                } else {
1055                    drop(start_listener);
1056                }
1057                if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1058                    // Timeout
1059                    return None;
1060                }
1061            }
1062        }
1063        if !aggregation.is_zero() {
1064            loop {
1065                select! {
1066                    () = tokio::time::sleep(aggregation) => {
1067                        break;
1068                    }
1069                    () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1070                        // Resets the sleep
1071                    }
1072                }
1073            }
1074        }
1075        let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1076        if let Some((duration, tasks)) = update.take() {
1077            Some(UpdateInfo {
1078                duration,
1079                tasks,
1080                reasons: take(reason_set),
1081                placeholder_for_future_fields: (),
1082            })
1083        } else {
1084            panic!("aggregated_update_info must not called concurrently")
1085        }
1086    }
1087
1088    pub async fn wait_background_done(&self) {
1089        let listener = self.event_background_done.listen();
1090        if self
1091            .currently_scheduled_background_jobs
1092            .load(Ordering::Acquire)
1093            != 0
1094        {
1095            listener.await;
1096        }
1097    }
1098
1099    pub async fn stop_and_wait(&self) {
1100        turbo_tasks_future_scope(self.pin(), async move {
1101            self.backend.stopping(self);
1102            self.stopped.store(true, Ordering::Release);
1103            {
1104                let listener = self
1105                    .event_foreground_done
1106                    .listen_with_note(|| || "wait for stop".to_string());
1107                if self
1108                    .currently_scheduled_foreground_jobs
1109                    .load(Ordering::Acquire)
1110                    != 0
1111                {
1112                    listener.await;
1113                }
1114            }
1115            {
1116                let listener = self.event_background_done.listen();
1117                if self
1118                    .currently_scheduled_background_jobs
1119                    .load(Ordering::Acquire)
1120                    != 0
1121                {
1122                    listener.await;
1123                }
1124            }
1125            self.backend.stop(self);
1126        })
1127        .await;
1128    }
1129
1130    #[track_caller]
1131    pub(crate) fn schedule_background_job<T>(&self, func: T)
1132    where
1133        T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1134        T::CallOnceFuture: Send,
1135    {
1136        let mut this = self.pin();
1137        self.begin_background_job();
1138        tokio::spawn(
1139            TURBO_TASKS
1140                .scope(this.clone(), async move {
1141                    if !this.stopped.load(Ordering::Acquire) {
1142                        this = func(this).await;
1143                    }
1144                    this.finish_background_job();
1145                })
1146                .in_current_span(),
1147        );
1148    }
1149
1150    fn finish_current_task_state(&self) -> FinishedTaskState {
1151        CURRENT_TASK_STATE.with(|cell| {
1152            let current_task_state = &*cell.write().unwrap();
1153            FinishedTaskState {
1154                #[cfg(feature = "verify_determinism")]
1155                stateful: current_task_state.stateful,
1156                has_invalidator: current_task_state.has_invalidator,
1157            }
1158        })
1159    }
1160
1161    pub fn backend(&self) -> &B {
1162        &self.backend
1163    }
1164
1165    pub fn get_current_task_priority(&self) -> TaskPriority {
1166        CURRENT_TASK_STATE
1167            .try_with(|task_state| task_state.read().unwrap().priority)
1168            .unwrap_or(TaskPriority::initial())
1169    }
1170
1171    pub fn is_idle(&self) -> bool {
1172        self.currently_scheduled_foreground_jobs
1173            .load(Ordering::Acquire)
1174            == 0
1175    }
1176
1177    #[track_caller]
1178    pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1179        self.schedule_background_job(async move |this| {
1180            this.backend.run_backend_job(job, &*this).await;
1181            this
1182        })
1183    }
1184}
1185
1186struct TurboTasksExecutor;
1187
1188/// Run a future and abort the process if a panic is reported
1189///
1190/// Turbtasks catches panics from user code and propagates throught the task tree, but if it happens
1191/// as part of state management we have to abort
1192async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1193    match AssertUnwindSafe(f).catch_unwind().await {
1194        Ok(r) => r,
1195        Err(_) => {
1196            eprintln!(
1197                "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1198                 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1199                 https://github.com/vercel/next.js/discussions and include the panic message \
1200                 and stack trace above.\n\nAborting."
1201            );
1202            abort();
1203        }
1204    }
1205}
1206
1207impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1208    type Future = impl Future<Output = ()> + Send + 'static;
1209
1210    fn execute(
1211        &self,
1212        this: &Arc<TurboTasks<B>>,
1213        scheduled_task: ScheduledTask,
1214        priority: TaskPriority,
1215    ) -> Self::Future {
1216        match scheduled_task {
1217            ScheduledTask::Task { task_id, span } => {
1218                let this2 = this.clone();
1219                let this = this.clone();
1220                let future = async move {
1221                    abort_on_panic(async {
1222                        // it's okay for execution ids to overflow and wrap, they're just used
1223                        // for an assert
1224                        let execution_id = this.execution_id_factory.wrapping_get();
1225                        let current_task_state =
1226                            CurrentTaskStateHandle::new(CurrentTaskState::new(
1227                                task_id,
1228                                execution_id,
1229                                priority,
1230                                false, // in_top_level_task
1231                            ));
1232                        let single_execution_future = async {
1233                            if this.stopped.load(Ordering::Acquire) {
1234                                this.backend.task_execution_canceled(task_id, &*this);
1235                                return None;
1236                            }
1237
1238                            let TaskExecutionSpec { future, span } = this
1239                                .backend
1240                                .try_start_task_execution(task_id, priority, &*this)?;
1241
1242                            async {
1243                                let result = CaptureFuture::new(future).await;
1244
1245                                // wait for all spawned local tasks using `local` to finish
1246                                wait_for_local_tasks().await;
1247
1248                                let result = match result {
1249                                    Ok(Ok(raw_vc)) => {
1250                                        // This is safe because we waited for all local tasks to
1251                                        // complete above
1252                                        raw_vc
1253                                            .to_non_local_unchecked_sync(&*this)
1254                                            .map_err(|err| err.into())
1255                                    }
1256                                    Ok(Err(err)) => Err(err.into()),
1257                                    Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1258                                };
1259
1260                                let finished_state = this.finish_current_task_state();
1261                                let cell_counters = CURRENT_TASK_STATE
1262                                    .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1263                                this.backend.task_execution_completed(
1264                                    task_id,
1265                                    result,
1266                                    &cell_counters,
1267                                    #[cfg(feature = "verify_determinism")]
1268                                    finished_state.stateful,
1269                                    finished_state.has_invalidator,
1270                                    &*this,
1271                                )
1272                            }
1273                            .instrument(span)
1274                            .await
1275                        };
1276                        if let Some(stale_priority) = CURRENT_TASK_STATE
1277                            .scope(current_task_state, single_execution_future)
1278                            .await
1279                        {
1280                            // Task was stale; re-schedule at the correct invalidation priority so
1281                            // other tasks can run in the right priority order.
1282                            this.schedule(task_id, stale_priority);
1283                        }
1284                        this.finish_foreground_job();
1285                    })
1286                    .await
1287                };
1288
1289                Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1290            }
1291            ScheduledTask::LocalTask {
1292                ty,
1293                persistence,
1294                local_task_id,
1295                global_task_state,
1296                span,
1297            } => {
1298                let this2 = this.clone();
1299                let this = this.clone();
1300                let task_type = ty.task_type;
1301                let future = async move {
1302                    let span = match &ty.task_type {
1303                        LocalTaskType::ResolveNative { native_fn } => {
1304                            native_fn.resolve_span(priority)
1305                        }
1306                        LocalTaskType::ResolveTrait { trait_method } => {
1307                            trait_method.resolve_span(priority)
1308                        }
1309                    };
1310                    abort_on_panic(
1311                        async move {
1312                            let result = match ty.task_type {
1313                                LocalTaskType::ResolveNative { native_fn } => {
1314                                    LocalTaskType::run_resolve_native(
1315                                        native_fn,
1316                                        ty.this,
1317                                        &*ty.arg,
1318                                        persistence,
1319                                        this,
1320                                    )
1321                                    .await
1322                                }
1323                                LocalTaskType::ResolveTrait { trait_method } => {
1324                                    LocalTaskType::run_resolve_trait(
1325                                        trait_method,
1326                                        ty.this.unwrap(),
1327                                        &*ty.arg,
1328                                        persistence,
1329                                        this,
1330                                    )
1331                                    .await
1332                                }
1333                            };
1334
1335                            let output = match result {
1336                                Ok(raw_vc) => OutputContent::Link(raw_vc),
1337                                Err(err) => OutputContent::Error(
1338                                    TurboTasksExecutionError::from(err)
1339                                        .with_local_task_context(task_type.to_string()),
1340                                ),
1341                            };
1342
1343                            CURRENT_TASK_STATE.with(move |gts| {
1344                                gts.write()
1345                                    .unwrap()
1346                                    .local_tasks
1347                                    .complete(local_task_id, output);
1348                            });
1349                        }
1350                        .instrument(span),
1351                    )
1352                    .await
1353                };
1354                let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1355
1356                Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1357            }
1358        }
1359    }
1360}
1361
1362struct FinishedTaskState {
1363    /// True if the task has state in cells (interior mutability).
1364    /// Only tracked when verify_determinism feature is enabled.
1365    #[cfg(feature = "verify_determinism")]
1366    stateful: bool,
1367
1368    /// True if the task uses an external invalidator
1369    has_invalidator: bool,
1370}
1371
1372impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1373    fn dynamic_call(
1374        &self,
1375        native_fn: &'static NativeFunction,
1376        this: Option<RawVc>,
1377        arg: &mut dyn DynTaskInputsStorage,
1378        inputs_resolved: InputResolution,
1379        persistence: TaskPersistence,
1380    ) -> RawVc {
1381        self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1382    }
1383    fn native_call(
1384        &self,
1385        native_fn: &'static NativeFunction,
1386        this: Option<RawVc>,
1387        arg: &mut dyn DynTaskInputsStorage,
1388        persistence: TaskPersistence,
1389    ) -> RawVc {
1390        self.native_call(native_fn, this, arg, persistence)
1391    }
1392    fn trait_call(
1393        &self,
1394        trait_method: &'static TraitMethod,
1395        this: RawVc,
1396        arg: &mut dyn DynTaskInputsStorage,
1397        inputs_resolved: InputResolution,
1398        persistence: TaskPersistence,
1399    ) -> RawVc {
1400        self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1401    }
1402
1403    #[track_caller]
1404    fn run(
1405        &self,
1406        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1407    ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1408        let this = self.pin();
1409        Box::pin(async move { this.run(future).await })
1410    }
1411
1412    #[track_caller]
1413    fn run_once(
1414        &self,
1415        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1416    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1417        let this = self.pin();
1418        Box::pin(async move { this.run_once(future).await })
1419    }
1420
1421    #[track_caller]
1422    fn run_once_with_reason(
1423        &self,
1424        reason: StaticOrArc<dyn InvalidationReason>,
1425        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1426    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1427        {
1428            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1429            reason_set.insert(reason);
1430        }
1431        let this = self.pin();
1432        Box::pin(async move { this.run_once(future).await })
1433    }
1434
1435    #[track_caller]
1436    fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1437        self.start_once_process(future)
1438    }
1439
1440    fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1441        if let Err(e) = self.compilation_events.send(event) {
1442            tracing::warn!("Failed to send compilation event: {e}");
1443        }
1444    }
1445
1446    fn get_task_name(&self, task: TaskId) -> String {
1447        self.backend.get_task_name(task, self)
1448    }
1449}
1450
1451impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1452    #[instrument(level = "info", skip_all, name = "invalidate")]
1453    fn invalidate(&self, task: TaskId) {
1454        self.backend.invalidate_task(task, self);
1455    }
1456
1457    #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1458    fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1459        {
1460            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1461            reason_set.insert(reason);
1462        }
1463        self.backend.invalidate_task(task, self);
1464    }
1465
1466    fn invalidate_serialization(&self, task: TaskId) {
1467        self.backend.invalidate_serialization(task, self);
1468    }
1469
1470    #[track_caller]
1471    fn try_read_task_output(
1472        &self,
1473        task: TaskId,
1474        options: ReadOutputOptions,
1475    ) -> Result<Result<RawVc, EventListener>> {
1476        if options.consistency == ReadConsistency::Eventual {
1477            debug_assert_not_in_top_level_task("read_task_output");
1478        }
1479        self.backend.try_read_task_output(
1480            task,
1481            current_task_if_available("reading Vcs"),
1482            options,
1483            self,
1484        )
1485    }
1486
1487    #[track_caller]
1488    fn try_read_task_cell(
1489        &self,
1490        task: TaskId,
1491        index: CellId,
1492        options: ReadCellOptions,
1493    ) -> Result<Result<TypedCellContent, EventListener>> {
1494        let reader = current_task_if_available("reading Vcs");
1495        self.backend
1496            .try_read_task_cell(task, index, reader, options, self)
1497    }
1498
1499    fn try_read_own_task_cell(
1500        &self,
1501        current_task: TaskId,
1502        index: CellId,
1503    ) -> Result<TypedCellContent> {
1504        self.backend
1505            .try_read_own_task_cell(current_task, index, self)
1506    }
1507
1508    #[track_caller]
1509    fn try_read_local_output(
1510        &self,
1511        execution_id: ExecutionId,
1512        local_task_id: LocalTaskId,
1513    ) -> Result<Result<RawVc, EventListener>> {
1514        debug_assert_not_in_top_level_task("read_local_output");
1515        CURRENT_TASK_STATE.with(|gts| {
1516            let gts_read = gts.read().unwrap();
1517
1518            // Local Vcs are local to their parent task's current execution, and do not exist
1519            // outside of it. This is weakly enforced at compile time using the `NonLocalValue`
1520            // marker trait. This assertion exists to handle any potential escapes that the
1521            // compile-time checks cannot capture.
1522            gts_read.assert_execution_id(execution_id);
1523
1524            match gts_read.local_tasks.get(local_task_id) {
1525                LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1526                LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1527            }
1528        })
1529    }
1530
1531    fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1532        // TODO: Add assert_not_in_top_level_task("read_task_collectibles") check here.
1533        // Collectible reads are eventually consistent.
1534        self.backend.read_task_collectibles(
1535            task,
1536            trait_id,
1537            current_task_if_available("reading collectibles"),
1538            self,
1539        )
1540    }
1541
1542    fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1543        self.backend.emit_collectible(
1544            trait_type,
1545            collectible,
1546            current_task("emitting collectible"),
1547            self,
1548        );
1549    }
1550
1551    fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1552        self.backend.unemit_collectible(
1553            trait_type,
1554            collectible,
1555            count,
1556            current_task("emitting collectible"),
1557            self,
1558        );
1559    }
1560
1561    fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1562        for (&collectible, &count) in collectibles {
1563            if count > 0 {
1564                self.backend.unemit_collectible(
1565                    trait_type,
1566                    collectible,
1567                    count as u32,
1568                    current_task("emitting collectible"),
1569                    self,
1570                );
1571            }
1572        }
1573    }
1574
1575    fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1576        self.try_read_own_task_cell(task, index)
1577    }
1578
1579    fn update_own_task_cell(
1580        &self,
1581        task: TaskId,
1582        index: CellId,
1583        content: CellContent,
1584        updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1585        content_hash: Option<CellHash>,
1586        verification_mode: VerificationMode,
1587    ) {
1588        self.backend.update_task_cell(
1589            task,
1590            index,
1591            content,
1592            updated_key_hashes,
1593            content_hash,
1594            verification_mode,
1595            self,
1596        );
1597    }
1598
1599    fn connect_task(&self, task: TaskId) {
1600        self.backend
1601            .connect_task(task, current_task_if_available("connecting task"), self);
1602    }
1603
1604    fn mark_own_task_as_finished(&self, task: TaskId) {
1605        self.backend.mark_own_task_as_finished(task, self);
1606    }
1607
1608    /// Creates a future that inherits the current task id and task state. The current global task
1609    /// will wait for this future to be dropped before exiting.
1610    fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1611        // this is similar to what happens for a local task, except that we keep the local task's
1612        // state as well.
1613        let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1614        global_task_state
1615            .write()
1616            .unwrap()
1617            .local_tasks
1618            .register_detached();
1619        let wrapped = async move {
1620            // use a drop guard for panic safety
1621            struct DropGuard;
1622            impl Drop for DropGuard {
1623                fn drop(&mut self) {
1624                    CURRENT_TASK_STATE
1625                        .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1626                }
1627            }
1628            let _guard = DropGuard;
1629            fut.await;
1630        };
1631        tokio::spawn(TURBO_TASKS.scope(
1632            turbo_tasks(),
1633            CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1634        ));
1635    }
1636
1637    fn task_statistics(&self) -> &TaskStatisticsApi {
1638        self.backend.task_statistics()
1639    }
1640
1641    fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1642        let this = self.pin();
1643        Box::pin(async move {
1644            this.stop_and_wait().await;
1645        })
1646    }
1647
1648    fn subscribe_to_compilation_events(
1649        &self,
1650        event_types: Option<Vec<String>>,
1651    ) -> Receiver<Arc<dyn CompilationEvent>> {
1652        self.compilation_events.subscribe(event_types)
1653    }
1654
1655    fn is_tracking_dependencies(&self) -> bool {
1656        self.backend.is_tracking_dependencies()
1657    }
1658}
1659
1660async fn wait_for_local_tasks() {
1661    let listener =
1662        CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
1663    let Some(listener) = listener else {
1664        return;
1665    };
1666    listener.await;
1667}
1668
1669pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
1670    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1671        Ok(id) => id,
1672        Err(_) => panic!(
1673            "{from} can only be used in the context of a turbo_tasks task execution or \
1674             turbo_tasks run"
1675        ),
1676    }
1677}
1678
1679pub(crate) fn current_task(from: &str) -> TaskId {
1680    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1681        Ok(Some(id)) => id,
1682        Ok(None) | Err(_) => {
1683            panic!("{from} can only be used in the context of a turbo_tasks task execution")
1684        }
1685    }
1686}
1687
1688/// Panics if we're not in a top-level task (e.g. [`run_once`]). Some function calls should only
1689/// happen in a top-level task (e.g. [`Effects::apply`][crate::Effects::apply]).
1690#[track_caller]
1691pub(crate) fn debug_assert_in_top_level_task(message: &str) {
1692    if !cfg!(debug_assertions) {
1693        return;
1694    }
1695
1696    let in_top_level = CURRENT_TASK_STATE
1697        .try_with(|ts| ts.read().unwrap().in_top_level_task)
1698        .unwrap_or(true);
1699    if !in_top_level {
1700        panic!("{message}");
1701    }
1702}
1703
1704#[track_caller]
1705pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
1706    if !cfg!(debug_assertions) {
1707        return;
1708    }
1709
1710    // HACK: We set this inside of `ReadRawVcFuture` to suppress warnings about an internal
1711    // consistency bug
1712    let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
1713        .try_with(|&suppressed| suppressed)
1714        .unwrap_or(false);
1715    if suppressed {
1716        return;
1717    }
1718
1719    let in_top_level = CURRENT_TASK_STATE
1720        .try_with(|ts| ts.read().unwrap().in_top_level_task)
1721        .unwrap_or(false);
1722    if in_top_level {
1723        panic!(
1724            "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
1725             Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
1726             reads to avoid leaking inconsistent return values."
1727        );
1728    }
1729}
1730
1731pub async fn run<T: Send + 'static>(
1732    tt: Arc<dyn TurboTasksApi>,
1733    future: impl Future<Output = Result<T>> + Send + 'static,
1734) -> Result<T> {
1735    let (tx, rx) = tokio::sync::oneshot::channel();
1736
1737    tt.run(Box::pin(async move {
1738        let result = future.await?;
1739        tx.send(result)
1740            .map_err(|_| anyhow!("unable to send result"))?;
1741        Ok(())
1742    }))
1743    .await?;
1744
1745    Ok(rx.await?)
1746}
1747
1748pub async fn run_once<T: Send + 'static>(
1749    tt: Arc<dyn TurboTasksApi>,
1750    future: impl Future<Output = Result<T>> + Send + 'static,
1751) -> Result<T> {
1752    let (tx, rx) = tokio::sync::oneshot::channel();
1753
1754    tt.run_once(Box::pin(async move {
1755        let result = future.await?;
1756        tx.send(result)
1757            .map_err(|_| anyhow!("unable to send result"))?;
1758        Ok(())
1759    }))
1760    .await?;
1761
1762    Ok(rx.await?)
1763}
1764
1765pub async fn run_once_with_reason<T: Send + 'static>(
1766    tt: Arc<dyn TurboTasksApi>,
1767    reason: impl InvalidationReason,
1768    future: impl Future<Output = Result<T>> + Send + 'static,
1769) -> Result<T> {
1770    let (tx, rx) = tokio::sync::oneshot::channel();
1771
1772    tt.run_once_with_reason(
1773        (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
1774        Box::pin(async move {
1775            let result = future.await?;
1776            tx.send(result)
1777                .map_err(|_| anyhow!("unable to send result"))?;
1778            Ok(())
1779        }),
1780    )
1781    .await?;
1782
1783    Ok(rx.await?)
1784}
1785
1786/// Calls [`TurboTasks::dynamic_call`] for the current turbo tasks instance.
1787pub fn dynamic_call(
1788    func: &'static NativeFunction,
1789    this: Option<RawVc>,
1790    arg: &mut dyn DynTaskInputsStorage,
1791    inputs_resolved: InputResolution,
1792    persistence: TaskPersistence,
1793) -> RawVc {
1794    with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
1795}
1796
1797/// Calls [`TurboTasks::trait_call`] for the current turbo tasks instance.
1798pub fn trait_call(
1799    trait_method: &'static TraitMethod,
1800    this: RawVc,
1801    arg: &mut dyn DynTaskInputsStorage,
1802    inputs_resolved: InputResolution,
1803    persistence: TaskPersistence,
1804) -> RawVc {
1805    with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
1806}
1807
1808pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
1809    TURBO_TASKS.with(|arc| arc.clone())
1810}
1811
1812pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
1813    TURBO_TASKS.with(Arc::downgrade)
1814}
1815
1816pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
1817    TURBO_TASKS.try_with(|arc| arc.clone()).ok()
1818}
1819
1820pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
1821    TURBO_TASKS.with(|arc| func(arc))
1822}
1823
1824pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
1825    TURBO_TASKS.sync_scope(tt, f)
1826}
1827
1828pub fn turbo_tasks_future_scope<T>(
1829    tt: Arc<dyn TurboTasksApi>,
1830    f: impl Future<Output = T>,
1831) -> impl Future<Output = T> {
1832    TURBO_TASKS.scope(tt, f)
1833}
1834
1835/// Spawns the given future within the context of the current task.
1836///
1837/// Beware: this method is not safe to use in production code. It is only
1838/// intended for use in tests and for debugging purposes.
1839pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
1840    turbo_tasks().spawn_detached_for_testing(Box::pin(f));
1841}
1842
1843/// Marks the current task as finished. This excludes it from waiting for
1844/// strongly consistency.
1845pub fn mark_finished() {
1846    with_turbo_tasks(|tt| {
1847        tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
1848    });
1849}
1850
1851/// Returns a [`SerializationInvalidator`] that can be used to invalidate the
1852/// serialization of the current task cells.
1853///
1854/// Also marks the current task as stateful when the `verify_determinism` feature is enabled,
1855/// since State allocation implies interior mutability.
1856pub fn get_serialization_invalidator() -> SerializationInvalidator {
1857    CURRENT_TASK_STATE.with(|cell| {
1858        let CurrentTaskState {
1859            task_id,
1860            #[cfg(feature = "verify_determinism")]
1861            stateful,
1862            ..
1863        } = &mut *cell.write().unwrap();
1864        #[cfg(feature = "verify_determinism")]
1865        {
1866            *stateful = true;
1867        }
1868        let Some(task_id) = *task_id else {
1869            panic!(
1870                "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
1871                 task execution"
1872            );
1873        };
1874        SerializationInvalidator::new(task_id)
1875    })
1876}
1877
1878pub fn mark_invalidator() {
1879    CURRENT_TASK_STATE.with(|cell| {
1880        let CurrentTaskState {
1881            has_invalidator, ..
1882        } = &mut *cell.write().unwrap();
1883        *has_invalidator = true;
1884    })
1885}
1886
1887/// Marks the current task as stateful. This is used to indicate that the task
1888/// has interior mutability (e.g., via [`State`][crate::State]), which means
1889/// the task may produce different outputs even with the same inputs.
1890///
1891/// Only has an effect when the `verify_determinism` feature is enabled.
1892pub fn mark_stateful() {
1893    #[cfg(feature = "verify_determinism")]
1894    {
1895        CURRENT_TASK_STATE.with(|cell| {
1896            let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
1897            *stateful = true;
1898        })
1899    }
1900    // No-op when verify_determinism is not enabled
1901}
1902
1903/// Marks the current task context as being in a top-level task. When in a top-level task,
1904/// eventually consistent reads will panic. It is almost always a mistake to perform an eventually
1905/// consistent read at the top-level of the application.
1906pub fn mark_top_level_task() {
1907    if cfg!(debug_assertions) {
1908        CURRENT_TASK_STATE.with(|cell| {
1909            cell.write().unwrap().in_top_level_task = true;
1910        })
1911    }
1912}
1913
1914/// Unmarks the current task context as being in a top-level task. The opposite of
1915/// [`mark_top_level_task`].
1916///
1917/// This utility can be okay in unit tests, where we're observing the internal behavior of
1918/// turbo-tasks, but otherwise, it is probably a mistake to call this function.
1919///
1920/// Calling this will allow eventually-consistent reads at the top-level, potentially exposing
1921/// incomplete computations and internal errors caused by eventual consistency that would've been
1922/// caught when the function was re-run. A strongly-consistent read re-runs parts of a task until
1923/// all of the dependencies have settled.
1924pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
1925    if cfg!(debug_assertions) {
1926        CURRENT_TASK_STATE.with(|cell| {
1927            cell.write().unwrap().in_top_level_task = false;
1928        })
1929    }
1930}
1931
1932pub fn prevent_gc() {
1933    // TODO implement garbage collection
1934}
1935
1936pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
1937    with_turbo_tasks(|tt| {
1938        let raw_vc = collectible.node.node;
1939        tt.emit_collectible(T::get_trait_type_id(), raw_vc)
1940    })
1941}
1942
1943pub(crate) async fn read_task_output(
1944    this: &dyn TurboTasksApi,
1945    id: TaskId,
1946    options: ReadOutputOptions,
1947) -> Result<RawVc> {
1948    loop {
1949        match this.try_read_task_output(id, options)? {
1950            Ok(result) => return Ok(result),
1951            Err(listener) => listener.await,
1952        }
1953    }
1954}
1955
1956/// A reference to a task's cell with methods that allow updating the contents
1957/// of the cell.
1958///
1959/// Mutations should not outside of the task that that owns this cell. Doing so
1960/// is a logic error, and may lead to incorrect caching behavior.
1961#[derive(Clone, Copy)]
1962pub struct CurrentCellRef {
1963    current_task: TaskId,
1964    index: CellId,
1965}
1966
1967type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
1968
1969impl CurrentCellRef {
1970    /// Updates the cell if the given `functor` returns a value.
1971    fn conditional_update<T>(
1972        &self,
1973        functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
1974    ) where
1975        T: VcValueType,
1976    {
1977        self.conditional_update_with_shared_reference(|old_shared_reference| {
1978            let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
1979            let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
1980            Some((
1981                SharedReference::new(triomphe::Arc::new(new_value)),
1982                updated_key_hashes,
1983                content_hash,
1984            ))
1985        })
1986    }
1987
1988    /// Updates the cell if the given `functor` returns a `SharedReference`.
1989    fn conditional_update_with_shared_reference(
1990        &self,
1991        functor: impl FnOnce(
1992            Option<&SharedReference>,
1993        ) -> Option<(
1994            SharedReference,
1995            Option<SmallVec<[u64; 2]>>,
1996            Option<CellHash>,
1997        )>,
1998    ) {
1999        let tt = turbo_tasks();
2000        let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2001        let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2002        if let Some((update, updated_key_hashes, content_hash)) = update {
2003            tt.update_own_task_cell(
2004                self.current_task,
2005                self.index,
2006                CellContent(Some(update)),
2007                updated_key_hashes,
2008                content_hash,
2009                VerificationMode::EqualityCheck,
2010            )
2011        }
2012    }
2013
2014    /// Replace the current cell's content with `new_value` if the current content is not equal by
2015    /// value with the existing content.
2016    ///
2017    /// The comparison happens using the value itself, not the [`VcRead::Target`] of that value.
2018    ///
2019    /// Take this example of a custom equality implementation on a transparent wrapper type:
2020    ///
2021    /// ```
2022    /// #[turbo_tasks::value(transparent, eq = "manual")]
2023    /// #[derive(Clone)]
2024    /// struct Wrapper(Vec<u32>);
2025    ///
2026    /// impl PartialEq for Wrapper {
2027    ///     fn eq(&self, other: &Wrapper) -> bool {
2028    ///         // Example: order doesn't matter for equality
2029    ///         let (mut this, mut other) = (self.0.clone(), other.0.clone());
2030    ///         this.sort_unstable();
2031    ///         other.sort_unstable();
2032    ///         this == other
2033    ///     }
2034    /// }
2035    ///
2036    /// impl Eq for Wrapper {}
2037    /// ```
2038    ///
2039    /// Comparisons of [`Vc<Wrapper>`] used when updating the cell will use `Wrapper`'s custom
2040    /// equality implementation, rather than the one provided by the target ([`Vec<u32>`]) type.
2041    ///
2042    /// However, in most cases, the default derived implementation of [`PartialEq`] is used which
2043    /// just forwards to the inner value's [`PartialEq`].
2044    ///
2045    /// If you already have a `SharedReference`, consider calling
2046    /// [`Self::compare_and_update_with_shared_reference`] which can re-use the [`SharedReference`]
2047    /// object.
2048    pub fn compare_and_update<T>(&self, new_value: T)
2049    where
2050        T: PartialEq + VcValueType,
2051    {
2052        self.conditional_update(|old_value| {
2053            if let Some(old_value) = old_value
2054                && old_value == &new_value
2055            {
2056                return None;
2057            }
2058            Some((new_value, None, None))
2059        });
2060    }
2061
2062    /// Replace the current cell's content with `new_shared_reference` if the current content is not
2063    /// equal by value with the existing content.
2064    ///
2065    /// If you already have a `SharedReference`, this is a faster version of
2066    /// [`CurrentCellRef::compare_and_update`].
2067    ///
2068    /// The value should be stored in [`SharedReference`] using the type `T`.
2069    pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2070    where
2071        T: VcValueType + PartialEq,
2072    {
2073        self.conditional_update_with_shared_reference(|old_sr| {
2074            if let Some(old_sr) = old_sr {
2075                let old_value = extract_sr_value::<T>(old_sr);
2076                let new_value = extract_sr_value::<T>(&new_shared_reference);
2077                if old_value == new_value {
2078                    return None;
2079                }
2080            }
2081            Some((new_shared_reference, None, None))
2082        });
2083    }
2084
2085    /// Replace the current cell's content if the new value is different.
2086    ///
2087    /// Like [`Self::compare_and_update`], but also computes and stores a hash of the value.
2088    /// When the cell's transient data is evicted, the stored hash enables the backend to detect
2089    /// whether the value actually changed without re-comparing values—avoiding unnecessary
2090    /// downstream invalidation.
2091    ///
2092    /// Requires `T: DeterministicHash` in addition to `T: PartialEq`.
2093    pub fn hashed_compare_and_update<T>(&self, new_value: T)
2094    where
2095        T: PartialEq + DeterministicHash + VcValueType,
2096    {
2097        self.conditional_update(|old_value| {
2098            if let Some(old_value) = old_value
2099                && old_value == &new_value
2100            {
2101                return None;
2102            }
2103            let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2104
2105            Some((new_value, None, Some(content_hash)))
2106        });
2107    }
2108
2109    /// Replace the current cell's content if the new value (from a pre-existing
2110    /// [`SharedReference`]) is different.
2111    ///
2112    /// Like [`Self::compare_and_update_with_shared_reference`], but also passes a hash
2113    /// for hash-based change detection when transient data has been evicted.
2114    pub fn hashed_compare_and_update_with_shared_reference<T>(
2115        &self,
2116        new_shared_reference: SharedReference,
2117    ) where
2118        T: VcValueType + PartialEq + DeterministicHash,
2119    {
2120        self.conditional_update_with_shared_reference(move |old_sr| {
2121            if let Some(old_sr) = old_sr {
2122                let old_value = extract_sr_value::<T>(old_sr);
2123                let new_value = extract_sr_value::<T>(&new_shared_reference);
2124                if old_value == new_value {
2125                    return None;
2126                }
2127            }
2128            let content_hash =
2129                hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2130            Some((new_shared_reference, None, Some(content_hash)))
2131        });
2132    }
2133
2134    /// See [`Self::compare_and_update`], but selectively update individual keys.
2135    pub fn keyed_compare_and_update<T>(&self, new_value: T)
2136    where
2137        T: PartialEq + VcValueType,
2138        VcReadTarget<T>: KeyedEq,
2139        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2140    {
2141        self.conditional_update(|old_value| {
2142            let Some(old_value) = old_value else {
2143                return Some((new_value, None, None));
2144            };
2145            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2146            let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2147            let updated_keys = old_value.different_keys(new_value_ref);
2148            if updated_keys.is_empty() {
2149                return None;
2150            }
2151            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2152            let updated_key_hashes = updated_keys
2153                .into_iter()
2154                .map(|key| FxBuildHasher.hash_one(key))
2155                .collect();
2156            Some((new_value, Some(updated_key_hashes), None))
2157        });
2158    }
2159
2160    /// See [`Self::compare_and_update_with_shared_reference`], but selectively update individual
2161    /// keys.
2162    pub fn keyed_compare_and_update_with_shared_reference<T>(
2163        &self,
2164        new_shared_reference: SharedReference,
2165    ) where
2166        T: VcValueType + PartialEq,
2167        VcReadTarget<T>: KeyedEq,
2168        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2169    {
2170        self.conditional_update_with_shared_reference(|old_sr| {
2171            let Some(old_sr) = old_sr else {
2172                return Some((new_shared_reference, None, None));
2173            };
2174            let old_value = extract_sr_value::<T>(old_sr);
2175            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2176            let new_value = extract_sr_value::<T>(&new_shared_reference);
2177            let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2178            let updated_keys = old_value.different_keys(new_value);
2179            if updated_keys.is_empty() {
2180                return None;
2181            }
2182            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2183            let updated_key_hashes = updated_keys
2184                .into_iter()
2185                .map(|key| FxBuildHasher.hash_one(key))
2186                .collect();
2187            Some((new_shared_reference, Some(updated_key_hashes), None))
2188        });
2189    }
2190
2191    /// Unconditionally updates the content of the cell.
2192    pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2193    where
2194        T: VcValueType,
2195    {
2196        let tt = turbo_tasks();
2197        tt.update_own_task_cell(
2198            self.current_task,
2199            self.index,
2200            CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2201            None,
2202            None,
2203            verification_mode,
2204        )
2205    }
2206
2207    /// A faster version of [`Self::update`] if you already have a
2208    /// [`SharedReference`].
2209    ///
2210    /// If the passed-in [`SharedReference`] is the same as the existing cell's
2211    /// by identity, no update is performed.
2212    ///
2213    /// The value should be stored in [`SharedReference`] using the type `T`.
2214    pub fn update_with_shared_reference(
2215        &self,
2216        shared_ref: SharedReference,
2217        verification_mode: VerificationMode,
2218    ) {
2219        let tt = turbo_tasks();
2220        let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2221            let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2222            if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2223                // pointer equality (not value equality)
2224                shared_ref_exp != shared_ref
2225            } else {
2226                true
2227            }
2228        } else {
2229            true
2230        };
2231        if update {
2232            tt.update_own_task_cell(
2233                self.current_task,
2234                self.index,
2235                CellContent(Some(shared_ref)),
2236                None,
2237                None,
2238                verification_mode,
2239            )
2240        }
2241    }
2242}
2243
2244impl From<CurrentCellRef> for RawVc {
2245    fn from(cell: CurrentCellRef) -> Self {
2246        RawVc::task_cell(cell.current_task, cell.index)
2247    }
2248}
2249
2250fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2251    sr.0.downcast_ref::<T>()
2252        .expect("cannot update SharedReference of different type")
2253}
2254
2255pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2256    find_cell_by_id(T::get_value_type_id())
2257}
2258
2259pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2260    CURRENT_TASK_STATE.with(|ts| {
2261        let current_task = current_task("celling turbo_tasks values");
2262        let mut ts = ts.write().unwrap();
2263        let map = ts.cell_counters.as_mut().unwrap();
2264        let current_index = map.entry(ty).or_default();
2265        let index = *current_index;
2266        assert!(
2267            index <= CellId::MAX_CELL_INDEX,
2268            "task allocated more than {} cells of a single type",
2269            CellId::MAX_CELL_INDEX as u64 + 1,
2270        );
2271        *current_index += 1;
2272        CurrentCellRef {
2273            current_task,
2274            index: CellId::new(ty, index),
2275        }
2276    })
2277}
2278
2279pub(crate) async fn read_local_output(
2280    this: &dyn TurboTasksApi,
2281    execution_id: ExecutionId,
2282    local_task_id: LocalTaskId,
2283) -> Result<RawVc> {
2284    loop {
2285        match this.try_read_local_output(execution_id, local_task_id)? {
2286            Ok(raw_vc) => return Ok(raw_vc),
2287            Err(event_listener) => event_listener.await,
2288        }
2289    }
2290}