Skip to main content

turbo_tasks/
manager.rs

1use std::{
2    borrow::Borrow,
3    cell::Cell,
4    cmp::Reverse,
5    fmt::{Debug, Display},
6    future::Future,
7    hash::{BuildHasher, BuildHasherDefault, Hash},
8    mem::take,
9    ops::Deref,
10    panic::AssertUnwindSafe,
11    pin::Pin,
12    process::abort,
13    sync::{
14        Arc, Mutex, RwLock, Weak,
15        atomic::{AtomicBool, AtomicUsize, Ordering},
16    },
17    task::{Context, Poll, Waker},
18    time::{Duration, Instant},
19};
20
21use anyhow::{Result, anyhow};
22use auto_hash_map::AutoMap;
23use bincode::{Decode, Encode};
24use either::Either;
25use futures::FutureExt;
26use rustc_hash::{FxBuildHasher, FxHasher};
27use serde::{Deserialize, Serialize};
28use smallvec::SmallVec;
29use tokio::{select, sync::mpsc::Receiver, task_local};
30use tracing::{Instrument, Span, instrument};
31use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash128};
32
33use crate::{
34    CellId, Completion, InvalidationReason, InvalidationReasonSet, NonLocalValue, OperationValue,
35    OperationVc, OutputContent, RawVc, ReadCellOptions, ReadOutcome, ReadOutputOptions, ResolvedVc,
36    SharedReference, TaskId, TraitMethod, ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType,
37    backend::{
38        Backend, CellContent, CellHash, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType,
39        TurboTasksExecutionError, TypedCellContent, VerificationMode,
40    },
41    capture_future::CaptureFuture,
42    dyn_task_inputs::DynTaskInputsStorage,
43    event::{Event, EventListener},
44    id::{ExecutionId, LocalTaskId, TraitTypeId},
45    keyed::KeyedEq,
46    local_task_tracker::LocalTaskTracker,
47    macro_helpers::NativeFunction,
48    message_queue::{CompilationEvent, CompilationEventQueue},
49    priority_runner::{Claimable, Executor, PriorityRunner},
50    registry,
51    serialization_invalidation::SerializationInvalidator,
52    task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
53    task_statistics::TaskStatisticsApi,
54    util::{IdFactory, StaticOrArc},
55};
56
57/// Common base trait for [`TurboTasksApi`] and [`TurboTasks`]. Provides APIs for creating tasks
58/// from function calls.
59pub trait TurboTasksCallApi: Sync + Send {
60    /// Calls a native function with arguments. Resolves arguments when needed
61    /// with a wrapper task.
62    ///
63    /// `inputs_resolved` is `TaskInput::is_resolved(&args)` computed at the macro callsite on
64    /// the concrete tuple type — when [`InputResolution::Resolved`], the fast path skips wrapper
65    /// task creation.
66    fn dynamic_call(
67        &self,
68        native_fn: &'static NativeFunction,
69        this: Option<RawVc>,
70        arg: &mut dyn DynTaskInputsStorage,
71        inputs_resolved: InputResolution,
72        persistence: TaskPersistence,
73    ) -> RawVc;
74    /// Call a native function with arguments.
75    /// All inputs must be resolved.
76    fn native_call(
77        &self,
78        native_fn: &'static NativeFunction,
79        this: Option<RawVc>,
80        arg: &mut dyn DynTaskInputsStorage,
81        persistence: TaskPersistence,
82    ) -> RawVc;
83    /// Calls a trait method with arguments. First input is the `self` object.
84    /// Uses a wrapper task to resolve.
85    ///
86    /// `inputs_resolved` is the macro-site `InputResolution` of the *exposed* tuple; when filtering
87    /// is involved, the post-filter value is computed inside the filter functor and supersedes
88    /// this argument.
89    fn trait_call(
90        &self,
91        trait_method: &'static TraitMethod,
92        this: RawVc,
93        arg: &mut dyn DynTaskInputsStorage,
94        inputs_resolved: InputResolution,
95        persistence: TaskPersistence,
96    ) -> RawVc;
97
98    fn run(
99        &self,
100        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
101    ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>>;
102    fn run_once(
103        &self,
104        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
105    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
106    fn run_once_with_reason(
107        &self,
108        reason: StaticOrArc<dyn InvalidationReason>,
109        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
110    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
111    fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
112
113    /// Sends a compilation event to subscribers.
114    fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
115
116    /// Returns a human-readable name for the given task.
117    fn get_task_name(&self, task: TaskId) -> String;
118}
119
120/// A type-erased subset of [`TurboTasks`] stored inside a thread local when we're in a turbo task
121/// context. Returned by the [`turbo_tasks`] helper function.
122///
123/// This trait is needed because thread locals cannot contain an unresolved [`Backend`] type
124/// parameter.
125pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
126    fn invalidate(&self, task: TaskId);
127    fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>);
128
129    fn invalidate_serialization(&self, task: TaskId);
130
131    fn try_read_task_output(
132        &self,
133        task: TaskId,
134        options: ReadOutputOptions,
135    ) -> Result<ReadOutcome<RawVc>>;
136
137    fn try_read_task_cell(
138        &self,
139        task: TaskId,
140        index: CellId,
141        options: ReadCellOptions,
142    ) -> Result<ReadOutcome<TypedCellContent>>;
143
144    /// Reads a [`RawVcUnpacked::LocalOutput`][crate::RawVcUnpacked::LocalOutput]. If the task has
145    /// completed, returns the [`RawVc`] the local task points to.
146    ///
147    /// The returned [`RawVc`] may also unpack to a
148    /// [`RawVcUnpacked::LocalOutput`][crate::RawVcUnpacked::LocalOutput], so this may need to be
149    /// called recursively or in a loop.
150    ///
151    /// This does not accept a consistency argument, as you cannot control consistency of a read of
152    /// an operation owned by your own task. Strongly consistent reads are only allowed on
153    /// [`OperationVc`]s, which should never be local tasks.
154    ///
155    /// No dependency tracking will happen as a result of this function call, as it's a no-op for a
156    /// task to depend on itself.
157    ///
158    /// [`OperationVc`]: crate::OperationVc
159    fn try_read_local_output(
160        &self,
161        execution_id: ExecutionId,
162        local_task_id: LocalTaskId,
163    ) -> Result<Result<RawVc, EventListener>>;
164
165    fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
166
167    /// Executes a task that is scheduled but not started yet inline on the current thread, so that
168    /// a read doesn't have to wait for a worker to pick the task up. Returns whether the task's
169    /// execution completed.
170    ///
171    /// Used by the read paths; see `TurboTasks::try_execute_scheduled_task_inline` for the details.
172    fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
173
174    /// Records that a read waited for a task that a worker was already executing, so it did not try
175    /// to claim it. Diagnostics only, see `TurboTasks::inline_execution_stats`.
176    #[cfg(feature = "inline_execution_stats")]
177    fn note_waited_for_in_progress_task(&self);
178
179    fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
180    fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
181    fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
182
183    /// INVALIDATION: Be careful with this, it will not track dependencies, so
184    /// using it could break cache invalidation.
185    fn try_read_own_task_cell(
186        &self,
187        current_task: TaskId,
188        index: CellId,
189    ) -> Result<TypedCellContent>;
190
191    fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
192    fn update_own_task_cell(
193        &self,
194        task: TaskId,
195        index: CellId,
196        content: CellContent,
197        updated_key_hashes: Option<SmallVec<[u64; 2]>>,
198        content_hash: Option<CellHash>,
199        verification_mode: VerificationMode,
200    );
201    fn mark_own_task_as_finished(&self, task: TaskId);
202
203    /// Pin a task against garbage collection. Delegates to
204    /// [`Backend::pin_task_for_gc`](crate::backend::Backend::pin_task_for_gc).
205    fn pin_task_for_gc(&self, task: TaskId);
206
207    /// Removes a pin added by [`pin_task_for_gc`](TurboTasksApi::pin_task_for_gc).
208    fn unpin_task_for_gc(&self, task: TaskId);
209
210    fn connect_task(&self, task: TaskId);
211
212    /// Wraps the given future in the current task.
213    ///
214    /// Beware: this method is not safe to use in production code. It is only intended for use in
215    /// tests and for debugging purposes.
216    fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
217
218    fn task_statistics(&self) -> &TaskStatisticsApi;
219
220    fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
221
222    fn subscribe_to_compilation_events(
223        &self,
224        event_types: Option<Vec<String>>,
225    ) -> Receiver<Arc<dyn CompilationEvent>>;
226
227    // Returns true if TurboTasks is configured to track dependencies.
228    fn is_tracking_dependencies(&self) -> bool;
229}
230
231/// A wrapper around a value that is unused.
232pub struct Unused<T> {
233    inner: T,
234}
235
236impl<T> Unused<T> {
237    /// Creates a new unused value.
238    ///
239    /// # Safety
240    ///
241    /// The wrapped value must not be used.
242    pub unsafe fn new_unchecked(inner: T) -> Self {
243        Self { inner }
244    }
245
246    /// Get the inner value, without consuming the `Unused` wrapper.
247    ///
248    /// # Safety
249    ///
250    /// The user need to make sure that the value stays unused.
251    pub unsafe fn get_unchecked(&self) -> &T {
252        &self.inner
253    }
254
255    /// Unwraps the value, consuming the `Unused` wrapper.
256    pub fn into(self) -> T {
257        self.inner
258    }
259}
260
261#[allow(clippy::manual_non_exhaustive)]
262pub struct UpdateInfo {
263    pub duration: Duration,
264    pub tasks: usize,
265    pub reasons: InvalidationReasonSet,
266    #[allow(dead_code)]
267    placeholder_for_future_fields: (),
268}
269
270#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
271pub enum TaskPersistence {
272    /// Tasks that may be persisted across sessions using serialization.
273    Persistent,
274
275    /// Tasks that will be persisted in memory for the life of this session, but won't persist
276    /// between sessions.
277    ///
278    /// This is used for [root tasks][TurboTasks::spawn_root_task] and tasks with an argument of
279    /// type [`TransientValue`][crate::value::TransientValue] or
280    /// [`TransientInstance`][crate::value::TransientInstance].
281    Transient,
282}
283
284impl Display for TaskPersistence {
285    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286        match self {
287            TaskPersistence::Persistent => write!(f, "persistent"),
288            TaskPersistence::Transient => write!(f, "transient"),
289        }
290    }
291}
292
293/// Whether a task call's inputs are already resolved, decided on the concrete input tuple at the
294/// call site. Travels alongside [`TaskPersistence`] through [`dynamic_call`] / [`trait_call`].
295#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
296pub enum InputResolution {
297    /// All inputs (and `this`, where applicable) are resolved — eligible for the synchronous fast
298    /// path with no async resolution task.
299    Resolved,
300    /// At least one input is unresolved and must be resolved in a local task first.
301    Unresolved,
302}
303
304impl InputResolution {
305    #[inline]
306    pub fn from_is_resolved(is_resolved: bool) -> Self {
307        if is_resolved {
308            Self::Resolved
309        } else {
310            Self::Unresolved
311        }
312    }
313
314    #[inline]
315    pub fn is_resolved(self) -> bool {
316        matches!(self, Self::Resolved)
317    }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
321pub enum ReadConsistency {
322    /// The default behavior for most APIs. Reads are faster, but may return stale values, which
323    /// may later trigger re-computation.
324    #[default]
325    Eventual,
326    /// Ensures all dependencies are fully resolved before returning the cell or output data, at
327    /// the cost of slower reads.
328    ///
329    /// Top-level code that returns data to the user should use strongly consistent reads.
330    Strong,
331}
332
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334pub enum ReadCellTracking {
335    /// Reads are tracked as dependencies of the current task.
336    Tracked {
337        /// The key used for the dependency
338        key: Option<u64>,
339    },
340    /// The read is only tracked when there is an error, otherwise it is untracked.
341    ///
342    /// INVALIDATION: Be careful with this, it will not track dependencies, so
343    /// using it could break cache invalidation.
344    TrackOnlyError,
345    /// The read is not tracked as a dependency of the current task.
346    ///
347    /// INVALIDATION: Be careful with this, it will not track dependencies, so
348    /// using it could break cache invalidation.
349    Untracked,
350}
351
352impl ReadCellTracking {
353    pub fn should_track(&self, is_err: bool) -> bool {
354        match self {
355            ReadCellTracking::Tracked { .. } => true,
356            ReadCellTracking::TrackOnlyError => is_err,
357            ReadCellTracking::Untracked => false,
358        }
359    }
360
361    pub fn key(&self) -> Option<u64> {
362        match self {
363            ReadCellTracking::Tracked { key } => *key,
364            ReadCellTracking::TrackOnlyError => None,
365            ReadCellTracking::Untracked => None,
366        }
367    }
368}
369
370impl Default for ReadCellTracking {
371    fn default() -> Self {
372        ReadCellTracking::Tracked { key: None }
373    }
374}
375
376impl Display for ReadCellTracking {
377    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378        match self {
379            ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
380            ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
381            ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
382            ReadCellTracking::Untracked => write!(f, "untracked"),
383        }
384    }
385}
386
387#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
388pub enum ReadTracking {
389    /// Reads are tracked as dependencies of the current task.
390    #[default]
391    Tracked,
392    /// The read is only tracked when there is an error, otherwise it is untracked.
393    ///
394    /// INVALIDATION: Be careful with this, it will not track dependencies, so
395    /// using it could break cache invalidation.
396    TrackOnlyError,
397    /// The read is not tracked as a dependency of the current task.
398    ///
399    /// INVALIDATION: Be careful with this, it will not track dependencies, so
400    /// using it could break cache invalidation.
401    Untracked,
402}
403
404impl ReadTracking {
405    pub fn should_track(&self, is_err: bool) -> bool {
406        match self {
407            ReadTracking::Tracked => true,
408            ReadTracking::TrackOnlyError => is_err,
409            ReadTracking::Untracked => false,
410        }
411    }
412}
413
414impl Display for ReadTracking {
415    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416        match self {
417            ReadTracking::Tracked => write!(f, "tracked"),
418            ReadTracking::TrackOnlyError => write!(f, "track only error"),
419            ReadTracking::Untracked => write!(f, "untracked"),
420        }
421    }
422}
423
424#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
425pub enum TaskPriority {
426    #[default]
427    Initial,
428    Invalidation {
429        priority: Reverse<u32>,
430    },
431    Recomputation,
432}
433
434impl TaskPriority {
435    pub fn invalidation(priority: u32) -> Self {
436        Self::Invalidation {
437            priority: Reverse(priority),
438        }
439    }
440
441    pub fn initial() -> Self {
442        Self::Initial
443    }
444
445    pub fn leaf() -> Self {
446        Self::Invalidation {
447            priority: Reverse(0),
448        }
449    }
450
451    pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
452        match self {
453            TaskPriority::Initial => parent_priority,
454            TaskPriority::Invalidation { priority } => {
455                if let TaskPriority::Invalidation {
456                    priority: parent_priority,
457                } = parent_priority
458                    && priority.0 < parent_priority.0
459                {
460                    Self::Invalidation {
461                        priority: Reverse(parent_priority.0.saturating_add(1)),
462                    }
463                } else {
464                    *self
465                }
466            }
467            TaskPriority::Recomputation => TaskPriority::Recomputation,
468        }
469    }
470}
471
472impl Display for TaskPriority {
473    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474        match self {
475            TaskPriority::Initial => write!(f, "initial"),
476            TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
477            TaskPriority::Recomputation => write!(f, "recomputation"),
478        }
479    }
480}
481
482enum ScheduledTask {
483    Task {
484        task_id: TaskId,
485        span: Span,
486    },
487    LocalTask {
488        ty: LocalTaskSpec,
489        persistence: TaskPersistence,
490        /// The non-local task whose execution created this local task (`None` for a top-level
491        /// execution), see [`ScheduleKey::LocalTask`].
492        parent_task: Option<TaskId>,
493        execution_id: ExecutionId,
494        local_task_id: LocalTaskId,
495        global_task_state: CurrentTaskStateHandle,
496        span: Span,
497    },
498}
499
500/// Identifies a scheduled task, so that a read which is about to wait for it can take it out of the
501/// scheduler queue and execute it inline instead (see `PriorityRunner::claim` and
502/// `execute_read_target_inline`).
503#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
504pub enum ScheduleKey {
505    /// A cached (non-local) task.
506    Task(TaskId),
507    /// A local task, which is only known within the execution that created it: the non-local task
508    /// of that execution (`None` for a top-level execution, e.g. [`TurboTasks::run`]), the
509    /// execution and the local task id within it.
510    ///
511    /// `ExecutionId` alone isn't unique across concurrent executions (it wraps), but a non-local
512    /// task only has one execution at a time, and that execution waits for its local tasks. So the
513    /// key is unique, except for two concurrent top-level executions that share a wrapped
514    /// `ExecutionId`.
515    LocalTask(Option<TaskId>, ExecutionId, LocalTaskId),
516}
517
518impl ScheduleKey {
519    /// The key of a local task created by the current execution.
520    pub(crate) fn current_local_task(
521        execution_id: ExecutionId,
522        local_task_id: LocalTaskId,
523    ) -> Self {
524        let parent_task = CURRENT_TASK_STATE.with(|ts| ts.current_task_id());
525        Self::LocalTask(parent_task, execution_id, local_task_id)
526    }
527}
528
529impl Claimable for ScheduledTask {
530    type Key = ScheduleKey;
531
532    fn claim_key(&self) -> ScheduleKey {
533        match self {
534            ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
535            ScheduledTask::LocalTask {
536                parent_task,
537                execution_id,
538                local_task_id,
539                ..
540            } => ScheduleKey::LocalTask(*parent_task, *execution_id, *local_task_id),
541        }
542    }
543}
544
545#[cfg(feature = "inline_execution_stats")]
546use std::sync::atomic::AtomicU64;
547
548/// Counters describing how reads and inline execution interacted, see
549/// [`TurboTasks::inline_execution_stats`]. Diagnostics only.
550#[cfg(feature = "inline_execution_stats")]
551#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
552pub struct InlineExecutionStats {
553    /// Tasks that were put into the scheduler queue.
554    pub queued: u64,
555    /// Reads that tried to take a queued task out of the queue.
556    pub claim_attempted: u64,
557    /// Claims that succeeded and whose execution finished on the reading thread.
558    pub claim_completed: u64,
559    /// Claims that succeeded but whose execution yielded, so it was handed to the runtime.
560    pub claim_yielded: u64,
561    /// Claims that found nothing to take, because a worker had already picked the task up.
562    pub claim_failed: u64,
563    /// Reads that waited without attempting a claim, because the task was already being executed.
564    pub waited_in_progress: u64,
565}
566
567/// The counters behind [`InlineExecutionStats`].
568///
569/// Without the `inline_execution_stats` feature this is zero-sized and every method is an empty
570/// `#[inline]` no-op, so the counting compiles away: the counters sit on the read-miss path, and a
571/// build that doesn't want the numbers shouldn't pay for them.
572#[derive(Default)]
573struct InlineExecutionCounters {
574    #[cfg(feature = "inline_execution_stats")]
575    claim_attempted: AtomicU64,
576    #[cfg(feature = "inline_execution_stats")]
577    claim_completed: AtomicU64,
578    #[cfg(feature = "inline_execution_stats")]
579    claim_yielded: AtomicU64,
580    #[cfg(feature = "inline_execution_stats")]
581    claim_failed: AtomicU64,
582    #[cfg(feature = "inline_execution_stats")]
583    waited_in_progress: AtomicU64,
584}
585
586impl InlineExecutionCounters {
587    /// A read found its task queued and tried to take it over.
588    #[inline]
589    fn claim_attempted(&self) {
590        #[cfg(feature = "inline_execution_stats")]
591        self.claim_attempted.fetch_add(1, Ordering::Relaxed);
592    }
593
594    /// A claim succeeded and the execution finished on the reading thread.
595    #[inline]
596    fn claim_completed(&self) {
597        #[cfg(feature = "inline_execution_stats")]
598        self.claim_completed.fetch_add(1, Ordering::Relaxed);
599    }
600
601    /// A claim succeeded but the execution yielded, so it was handed to the runtime.
602    #[inline]
603    fn claim_yielded(&self) {
604        #[cfg(feature = "inline_execution_stats")]
605        self.claim_yielded.fetch_add(1, Ordering::Relaxed);
606    }
607
608    /// A claim found nothing to take, because a worker had already picked the task up.
609    #[inline]
610    fn claim_failed(&self) {
611        #[cfg(feature = "inline_execution_stats")]
612        self.claim_failed.fetch_add(1, Ordering::Relaxed);
613    }
614
615    /// A read waited without attempting a claim, because the task was already being executed.
616    #[cfg(feature = "inline_execution_stats")]
617    #[inline]
618    fn waited_in_progress(&self) {
619        self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
620    }
621}
622
623/// Whether a dump of [`InlineExecutionStats`] was requested via `TURBO_ENGINE_INLINE_STATS=1`.
624#[cfg(feature = "inline_execution_stats")]
625pub(crate) fn inline_stats_requested() -> bool {
626    static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
627        std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
628    });
629    *REQUESTED
630}
631
632/// Maximum number of task executions that may be nested inline on a single thread, to conserve
633/// stack space. (The alternative would be growing the stack on demand, the way SWC does.)
634const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
635
636thread_local! {
637    /// How many task executions are currently nested inline on this thread.
638    static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
639}
640
641/// Whether the current thread may execute another task inline, see [`MAX_INLINE_EXECUTION_DEPTH`].
642fn inline_execution_allowed() -> bool {
643    INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
644}
645
646/// Counts one level of inline task execution on this thread, see [`MAX_INLINE_EXECUTION_DEPTH`].
647struct InlineExecutionDepthGuard;
648
649impl InlineExecutionDepthGuard {
650    fn enter() -> Self {
651        INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
652        Self
653    }
654}
655
656impl Drop for InlineExecutionDepthGuard {
657    fn drop(&mut self) {
658        INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
659    }
660}
661
662/// Polls `future` once inline and then spawns it if it doesn't complete so tokio drives it. Returns
663/// whether it completed.
664fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
665    let _depth_guard = InlineExecutionDepthGuard::enter();
666    let span_slot = InlineExecutionSpanSlot::default();
667    let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
668    // A waker that never wakes anything is fine here: if this poll doesn't complete the future we
669    // spawn it, and a spawned task is always polled at least once, which is the poll that registers
670    // the real waker.
671    match future
672        .as_mut()
673        .poll(&mut Context::from_waker(Waker::noop()))
674    {
675        Poll::Ready(()) => {
676            span_slot.record("complete");
677            true
678        }
679        Poll::Pending => {
680            span_slot.record("partial");
681            tokio::task::spawn(future);
682            false
683        }
684    }
685}
686
687/// Executes the task inline if possible, returns true if it executed to completion.
688pub(crate) fn execute_read_target_inline(
689    turbo_tasks: &dyn TurboTasksApi,
690    key: ScheduleKey,
691) -> bool {
692    if !inline_execution_allowed() {
693        // Nested too deeply; a worker will pick the task up, as it always did.
694        return false;
695    }
696    turbo_tasks.try_execute_scheduled_task_inline(key)
697}
698
699pub struct TurboTasks<B: Backend + 'static> {
700    this: Weak<Self>,
701    backend: B,
702    execution_id_factory: IdFactory<ExecutionId>,
703    stopped: AtomicBool,
704    currently_scheduled_foreground_jobs: AtomicUsize,
705    currently_scheduled_background_jobs: AtomicUsize,
706    scheduled_tasks: AtomicUsize,
707    /// Diagnostics for reads and inline execution, see `TurboTasks::inline_execution_stats`.
708    /// Zero-sized without the `inline_execution_stats` feature.
709    inline_counters: InlineExecutionCounters,
710    priority_runner:
711        Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
712    start: Mutex<Option<Instant>>,
713    aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
714    /// Event that is triggered when currently_scheduled_foreground_jobs becomes non-zero
715    event_foreground_start: Event,
716    /// Event that is triggered when all foreground jobs are done
717    /// (currently_scheduled_foreground_jobs becomes zero)
718    event_foreground_done: Event,
719    /// Event that is triggered when all background jobs are done
720    event_background_done: Event,
721    compilation_events: CompilationEventQueue,
722}
723
724/// Information about a non-local task. A non-local task can contain multiple "local" tasks, which
725/// all share the same non-local task state.
726///
727/// A non-local task is one that:
728///
729/// - Has a unique task id.
730/// - Is potentially cached.
731/// - The backend is aware of.
732struct CurrentTaskState {
733    task_id: Option<TaskId>,
734    execution_id: ExecutionId,
735    priority: TaskPriority,
736
737    /// True if the current task has state in cells (interior mutability).
738    /// Only tracked when verify_determinism feature is enabled.
739    #[cfg(feature = "verify_determinism")]
740    stateful: bool,
741
742    /// True if the current task uses an external invalidator
743    has_invalidator: bool,
744
745    /// True if we're in a top-level task (e.g. `.run_once(...)` or `.run(...)`).
746    /// Eventually consistent reads are not allowed in top-level tasks.
747    in_top_level_task: bool,
748
749    /// Tracks how many cells of each type has been allocated so far during this task execution.
750    /// When a task is re-executed, the cell count may not match the existing cell vec length.
751    ///
752    /// This is taken (and becomes `None`) during teardown of a task.
753    cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
754
755    /// Tracks execution of Local tasks (and detached test futures) created during this global
756    /// task's execution.
757    local_tasks: LocalTaskTracker,
758}
759
760impl CurrentTaskState {
761    fn new(
762        task_id: TaskId,
763        execution_id: ExecutionId,
764        priority: TaskPriority,
765        in_top_level_task: bool,
766    ) -> Self {
767        Self {
768            task_id: Some(task_id),
769            execution_id,
770            priority,
771            #[cfg(feature = "verify_determinism")]
772            stateful: false,
773            has_invalidator: false,
774            in_top_level_task,
775            cell_counters: Some(AutoMap::default()),
776            local_tasks: LocalTaskTracker::new(),
777        }
778    }
779
780    fn new_temporary(
781        execution_id: ExecutionId,
782        priority: TaskPriority,
783        in_top_level_task: bool,
784    ) -> Self {
785        Self {
786            task_id: None,
787            execution_id,
788            priority,
789            #[cfg(feature = "verify_determinism")]
790            stateful: false,
791            has_invalidator: false,
792            in_top_level_task,
793            cell_counters: None,
794            local_tasks: LocalTaskTracker::new(),
795        }
796    }
797
798    fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
799        if self.execution_id != expected_execution_id {
800            panic!(
801                "Local tasks can only be scheduled/awaited within the same execution of the \
802                 parent task that created them"
803            );
804        }
805    }
806}
807
808/// A shareable current-task state handle with the immutable task ID cached
809/// outside the lock. The rest of the state is mutated by global and local
810/// tasks, but the task ID is fixed for the lifetime of an execution.
811#[derive(Clone)]
812struct CurrentTaskStateHandle {
813    inner: Arc<CurrentTaskStateInner>,
814}
815
816struct CurrentTaskStateInner {
817    current_task_id: Option<TaskId>,
818    state: RwLock<CurrentTaskState>,
819}
820
821impl CurrentTaskStateHandle {
822    fn new(state: CurrentTaskState) -> Self {
823        Self {
824            inner: Arc::new(CurrentTaskStateInner {
825                current_task_id: state.task_id,
826                state: RwLock::new(state),
827            }),
828        }
829    }
830
831    fn current_task_id(&self) -> Option<TaskId> {
832        self.inner.current_task_id
833    }
834}
835
836impl Deref for CurrentTaskStateHandle {
837    type Target = RwLock<CurrentTaskState>;
838
839    fn deref(&self) -> &Self::Target {
840        &self.inner.state
841    }
842}
843
844// TODO implement our own thread pool and make these thread locals instead
845task_local! {
846    /// The current TurboTasks instance
847    static TURBO_TASKS: Arc<dyn TurboTasksApi>;
848
849    static CURRENT_TASK_STATE: CurrentTaskStateHandle;
850
851    /// Temporarily suppresses the eventual consistency check in top-level tasks.
852    /// This is used by strongly consistent reads to allow them to succeed in top-level tasks.
853    /// This is NOT shared across local tasks (unlike CURRENT_TASK_STATE), so it's safe
854    /// to set/unset without race conditions.
855    pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
856
857    /// Set only while a reader polls a task it claimed, so that the outcome of that poll can be
858    /// recorded on the *task's* span rather than the reader's, see [`InlineExecutionSpanSlot`].
859    static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
860}
861
862/// Lets a reader that executes a claimed task inline record the outcome on the span of the task it
863/// executed.
864///
865/// The reader only sees the outer execution future, whose instrumented span has already been exited
866/// by the time its `poll` returns — `Span::current()` there is the reader's own span. So the
867/// executor puts the span it is about to instrument the task body with in here, and the reader
868/// records the outcome on it afterwards.
869///
870/// Only present while a claimed task is being polled inline: a task started by a worker doesn't
871/// have this task-local set, and leaves the field unset.
872#[derive(Clone, Default)]
873struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
874
875impl InlineExecutionSpanSlot {
876    /// Called by the executor with the span it instruments the task body with.
877    fn set(span: &Span) {
878        let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
879            *slot.0.lock().unwrap() = Some(span.clone());
880        });
881    }
882
883    /// Records the outcome of the inline poll on the executed task's span, if the executor got far
884    /// enough to register one (it doesn't when the task was already claimed by someone else and the
885    /// execution turns into a no-op).
886    fn record(&self, outcome: &'static str) {
887        if let Some(span) = self.0.lock().unwrap().as_ref() {
888            span.record("inline_execution", outcome);
889        }
890    }
891}
892
893impl<B: Backend + 'static> TurboTasks<B> {
894    // TODO better lifetime management for turbo tasks
895    // consider using unsafe for the task_local turbo tasks
896    // that should be safe as long tasks can't outlife turbo task
897    // so we probably want to make sure that all tasks are joined
898    // when trying to drop turbo tasks
899    pub fn new(backend: B) -> Arc<Self> {
900        let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
901        let this = Arc::new_cyclic(|this| Self {
902            this: this.clone(),
903            backend,
904            execution_id_factory,
905            stopped: AtomicBool::new(false),
906            currently_scheduled_foreground_jobs: AtomicUsize::new(0),
907            currently_scheduled_background_jobs: AtomicUsize::new(0),
908            scheduled_tasks: AtomicUsize::new(0),
909            inline_counters: InlineExecutionCounters::default(),
910            priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
911            start: Default::default(),
912            aggregated_update: Default::default(),
913            event_foreground_done: Event::new(|| {
914                || "TurboTasks::event_foreground_done".to_string()
915            }),
916            event_foreground_start: Event::new(|| {
917                || "TurboTasks::event_foreground_start".to_string()
918            }),
919            event_background_done: Event::new(|| {
920                || "TurboTasks::event_background_done".to_string()
921            }),
922            compilation_events: CompilationEventQueue::default(),
923        });
924        this.backend.startup(&*this);
925        this
926    }
927
928    pub fn pin(&self) -> Arc<Self> {
929        self.this.upgrade().unwrap()
930    }
931
932    /// Creates a new root task
933    pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
934    where
935        T: ?Sized,
936        F: Fn() -> Fut + Send + Sync + Clone + 'static,
937        Fut: Future<Output = Result<Vc<T>>> + Send,
938    {
939        let id = self.backend.create_transient_task(
940            TransientTaskType::Root(Box::new(move || {
941                let functor = functor.clone();
942                Box::pin(async move {
943                    mark_top_level_task();
944                    let raw_vc = functor().await?.node;
945                    raw_vc.to_non_local().await
946                })
947            })),
948            self,
949        );
950        self.schedule(id, TaskPriority::initial());
951        id
952    }
953
954    pub fn dispose_root_task(&self, task_id: TaskId) {
955        self.backend.dispose_root_task(task_id, self);
956    }
957
958    /// Pins a task against garbage collection (a transient, session-only reference). Balanced by
959    /// [`unpin_task_for_gc`](Self::unpin_task_for_gc). Used for references that escape the tracked
960    /// task graph — e.g. a `DetachedVc` holding an `OperationVc` across the NAPI boundary.
961    pub fn pin_task_for_gc(&self, task_id: TaskId) {
962        self.backend.pin_task_for_gc(task_id, self);
963    }
964
965    /// Releases a pin added by [`pin_task_for_gc`](Self::pin_task_for_gc).
966    pub fn unpin_task_for_gc(&self, task_id: TaskId) {
967        self.backend.unpin_task_for_gc(task_id, self);
968    }
969
970    // TODO make sure that all dependencies settle before reading them
971    /// Creates a new root task, that is only executed once.
972    /// Dependencies will not invalidate the task.
973    #[track_caller]
974    fn spawn_once_task<T, Fut>(&self, future: Fut)
975    where
976        T: ?Sized,
977        Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
978    {
979        let id = self.backend.create_transient_task(
980            TransientTaskType::Once(Box::pin(async move {
981                mark_top_level_task();
982                let raw_vc = future.await?.node;
983                raw_vc.to_non_local().await
984            })),
985            self,
986        );
987        self.schedule(id, TaskPriority::initial());
988    }
989
990    /// Runs `future` as a top-level task and returns its result.
991    ///
992    /// # Returned values must not contain `Vc` or `ResolvedVc` or if they do they should be covered by a pin
993    ///
994    /// `T` is deliberately unconstrained for ergonomics, but a `Vc`/`ResolvedVc` that escapes this
995    /// call is **not** protected against garbage collection. It can be collected, and
996    /// dereferencing the stale handle then fails.
997    ///    ///
998    /// To hand a computation out of a top-level task, return the [`OperationVc`] and pin it:
999    ///
1000    /// ```ignore
1001    /// let (resolved, op) = tt.run_once(async move {
1002    ///     let op = my_operation(args);
1003    ///     let resolved = op.resolve().strongly_consistent().await?;
1004    ///     // Return the operation too, so it can be pinned below.
1005    ///     Ok((resolved, op))
1006    /// }).await?;
1007    /// let _gc_root = GcRoot::pin(tt.clone(), op);  // unpins on drop
1008    /// ```
1009    ///
1010    /// Returning owned data -- [`ReadRef`](crate::ReadRef), `RcStr`, plain values -- is always
1011    /// fine.
1012    pub async fn run_once<T: Send + 'static>(
1013        &self,
1014        future: impl Future<Output = Result<T>> + Send + 'static,
1015    ) -> Result<T> {
1016        let (tx, rx) = tokio::sync::oneshot::channel();
1017        self.spawn_once_task(async move {
1018            mark_top_level_task();
1019            let result = future.await;
1020            tx.send(result)
1021                .map_err(|_| anyhow!("unable to send result"))?;
1022            Ok(Completion::new())
1023        });
1024
1025        rx.await?
1026    }
1027
1028    /// Runs `future` as a top-level task and returns its result.
1029    ///
1030    /// The same constraint as [`run_once`](Self::run_once) applies to `T`: a `Vc`/`ResolvedVc`
1031    /// returned from here is not anchored against garbage collection. See that method's docs for
1032    /// why, and for the pin-the-`OperationVc` idiom.
1033    #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
1034    pub async fn run<T: Send + 'static>(
1035        &self,
1036        future: impl Future<Output = Result<T>> + Send + 'static,
1037    ) -> Result<T, TurboTasksExecutionError> {
1038        self.begin_foreground_job();
1039        // it's okay for execution ids to overflow and wrap, they're just used for an assert
1040        let execution_id = self.execution_id_factory.wrapping_get();
1041        let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
1042            execution_id,
1043            TaskPriority::initial(),
1044            true, // in_top_level_task
1045        ));
1046
1047        let result = TURBO_TASKS
1048            .scope(
1049                self.pin(),
1050                CURRENT_TASK_STATE.scope(current_task_state, async {
1051                    let result = CaptureFuture::new(future).await;
1052
1053                    // wait for all spawned local tasks using `local` to finish
1054                    wait_for_local_tasks().await;
1055
1056                    match result {
1057                        Ok(Ok(value)) => Ok(value),
1058                        Ok(Err(err)) => Err(err.into()),
1059                        Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1060                    }
1061                }),
1062            )
1063            .await;
1064        self.finish_foreground_job();
1065        result
1066    }
1067
1068    pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1069        let this = self.pin();
1070        tokio::spawn(async move {
1071            this.pin()
1072                .run_once(async move {
1073                    this.finish_foreground_job();
1074                    future.await;
1075                    this.begin_foreground_job();
1076                    Ok(())
1077                })
1078                .await
1079                .unwrap()
1080        });
1081    }
1082
1083    pub(crate) fn native_call(
1084        &self,
1085        native_fn: &'static NativeFunction,
1086        this: Option<RawVc>,
1087        arg: &mut dyn DynTaskInputsStorage,
1088        persistence: TaskPersistence,
1089    ) -> RawVc {
1090        RawVc::task_output(self.backend.get_or_create_task(
1091            native_fn,
1092            this,
1093            arg,
1094            current_task_if_available("turbo_function calls"),
1095            persistence,
1096            self,
1097        ))
1098    }
1099
1100    pub fn dynamic_call(
1101        &self,
1102        native_fn: &'static NativeFunction,
1103        this: Option<RawVc>,
1104        arg: &mut dyn DynTaskInputsStorage,
1105        inputs_resolved: InputResolution,
1106        persistence: TaskPersistence,
1107    ) -> RawVc {
1108        if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1109            return self.native_call(native_fn, this, arg, persistence);
1110        }
1111        // Need async resolution — must move the arg to the heap now
1112        let arg = arg.take_box();
1113        let task_type = LocalTaskSpec {
1114            task_type: LocalTaskType::ResolveNative { native_fn },
1115            this,
1116            arg,
1117        };
1118        self.schedule_local_task(task_type, persistence)
1119    }
1120
1121    pub fn trait_call(
1122        &self,
1123        trait_method: &'static TraitMethod,
1124        this: RawVc,
1125        arg: &mut dyn DynTaskInputsStorage,
1126        inputs_resolved: InputResolution,
1127        persistence: TaskPersistence,
1128    ) -> RawVc {
1129        // avoid creating a wrapper task if self is already resolved
1130        // for resolved cells we already know the value type so we can lookup the
1131        // function
1132        if let Some((_, cell_id)) = this.as_task_cell() {
1133            match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1134                Some(native_fn) => {
1135                    if let Some(filter) = native_fn.arg_meta.filter_owned {
1136                        let (resolved, mut arg) = (filter)(arg);
1137                        return self.dynamic_call(
1138                            native_fn,
1139                            Some(this),
1140                            &mut arg,
1141                            resolved,
1142                            persistence,
1143                        );
1144                    } else {
1145                        return self.dynamic_call(
1146                            native_fn,
1147                            Some(this),
1148                            arg,
1149                            inputs_resolved,
1150                            persistence,
1151                        );
1152                    }
1153                }
1154                None => {
1155                    // We are destined to fail at this point, but we just retry resolution in the
1156                    // local task since we cannot report an error from here.
1157                    // TODO: A panic seems appropriate since the immediate caller is to blame
1158                }
1159            }
1160        }
1161
1162        // create a wrapper task to resolve all inputs
1163        let task_type = LocalTaskSpec {
1164            task_type: LocalTaskType::ResolveTrait { trait_method },
1165            this: Some(this),
1166            arg: arg.take_box(),
1167        };
1168
1169        self.schedule_local_task(task_type, persistence)
1170    }
1171
1172    #[track_caller]
1173    pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1174        self.begin_foreground_job();
1175        self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1176
1177        let task = ScheduledTask::Task {
1178            task_id,
1179            span: Span::current(),
1180        };
1181        self.priority_runner.schedule(&self.pin(), task, priority);
1182    }
1183
1184    /// Schedules all `tasks`, like calling [`TurboTasks::schedule`] for each of them, but hands
1185    /// them to the scheduler as one batch, so its queue is locked once instead of once per task.
1186    #[track_caller]
1187    pub fn schedule_many(&self, tasks: &[(TaskId, TaskPriority)]) {
1188        if tasks.is_empty() {
1189            return;
1190        }
1191        self.begin_foreground_jobs(tasks.len());
1192        self.scheduled_tasks
1193            .fetch_add(tasks.len(), Ordering::AcqRel);
1194
1195        let span = Span::current();
1196        self.priority_runner.schedule_many(
1197            &self.pin(),
1198            tasks.iter().map(|&(task_id, priority)| {
1199                (
1200                    ScheduledTask::Task {
1201                        task_id,
1202                        span: span.clone(),
1203                    },
1204                    priority,
1205                )
1206            }),
1207        );
1208    }
1209
1210    fn schedule_local_task(
1211        &self,
1212        ty: LocalTaskSpec,
1213        // if this is a `LocalTaskType::Resolve*`, we may spawn another task with this persistence,
1214        persistence: TaskPersistence,
1215    ) -> RawVc {
1216        let task_type = ty.task_type;
1217        let (global_task_state, parent_task, execution_id, priority, local_task_id) =
1218            CURRENT_TASK_STATE.with(|gts| {
1219                let mut gts_write = gts.write().unwrap();
1220                let local_task_id = gts_write.local_tasks.create(task_type);
1221                (
1222                    gts.clone(),
1223                    gts_write.task_id,
1224                    gts_write.execution_id,
1225                    gts_write.priority,
1226                    local_task_id,
1227                )
1228            });
1229
1230        let task = ScheduledTask::LocalTask {
1231            ty,
1232            persistence,
1233            parent_task,
1234            execution_id,
1235            local_task_id,
1236            global_task_state,
1237            span: Span::current(),
1238        };
1239        self.priority_runner.schedule(&self.pin(), task, priority);
1240
1241        RawVc::local_output(execution_id, local_task_id, persistence)
1242    }
1243
1244    /// Executes the task inline if possible, returns true if it executed to completion.
1245    fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1246        let this = self.pin();
1247        self.inline_counters.claim_attempted();
1248        if let Some(future) = self.priority_runner.claim(&this, &key) {
1249            let completed = poll_once_or_spawn(future);
1250            if completed {
1251                self.inline_counters.claim_completed();
1252            } else {
1253                self.inline_counters.claim_yielded();
1254            }
1255            return completed;
1256        }
1257        self.inline_counters.claim_failed();
1258        false
1259    }
1260
1261    #[cfg(feature = "inline_execution_stats")]
1262    fn note_waited_for_in_progress_task(&self) {
1263        self.inline_counters.waited_in_progress();
1264    }
1265
1266    fn begin_foreground_job(&self) {
1267        self.begin_foreground_jobs(1);
1268    }
1269
1270    /// Begins `count` foreground jobs at once. `count` must not be zero.
1271    fn begin_foreground_jobs(&self, count: usize) {
1272        debug_assert!(count > 0);
1273        if self
1274            .currently_scheduled_foreground_jobs
1275            .fetch_add(count, Ordering::AcqRel)
1276            == 0
1277        {
1278            *self.start.lock().unwrap() = Some(Instant::now());
1279            self.event_foreground_start.notify(usize::MAX);
1280            self.backend.idle_end(self);
1281        }
1282    }
1283
1284    fn finish_foreground_job(&self) {
1285        if self
1286            .currently_scheduled_foreground_jobs
1287            .fetch_sub(1, Ordering::AcqRel)
1288            == 1
1289        {
1290            self.backend.idle_start(self);
1291            // That's not super race-condition-safe, but it's only for
1292            // statistical reasons
1293            let total = self.scheduled_tasks.load(Ordering::Acquire);
1294            self.scheduled_tasks.store(0, Ordering::Release);
1295            if let Some(start) = *self.start.lock().unwrap() {
1296                let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1297                if let Some(update) = update.as_mut() {
1298                    update.0 += start.elapsed();
1299                    update.1 += total;
1300                } else {
1301                    *update = Some((start.elapsed(), total));
1302                }
1303            }
1304            self.event_foreground_done.notify(usize::MAX);
1305        }
1306    }
1307
1308    fn begin_background_job(&self) {
1309        self.currently_scheduled_background_jobs
1310            .fetch_add(1, Ordering::Relaxed);
1311    }
1312
1313    fn finish_background_job(&self) {
1314        if self
1315            .currently_scheduled_background_jobs
1316            .fetch_sub(1, Ordering::Relaxed)
1317            == 1
1318        {
1319            self.event_background_done.notify(usize::MAX);
1320        }
1321    }
1322
1323    pub fn get_in_progress_count(&self) -> usize {
1324        self.currently_scheduled_foreground_jobs
1325            .load(Ordering::Acquire)
1326    }
1327
1328    /// Counters describing how reads and inline execution interacted. Diagnostics only; a dump of
1329    /// these can be requested with `TURBO_ENGINE_INLINE_STATS=1`.
1330    #[cfg(feature = "inline_execution_stats")]
1331    #[doc(hidden)]
1332    pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1333        let counters = &self.inline_counters;
1334        InlineExecutionStats {
1335            queued: self.priority_runner.total_queued(),
1336            claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1337            claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1338            claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1339            claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1340            waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1341        }
1342    }
1343
1344    /// Waits for the given task to finish executing. This works by performing an untracked read,
1345    /// and discarding the value of the task output.
1346    ///
1347    /// [`ReadConsistency::Eventual`] means that this will return after the task executes, but
1348    /// before all dependencies have completely settled.
1349    ///
1350    /// [`ReadConsistency::Strong`] means that this will also wait for the task and all dependencies
1351    /// to fully settle before returning.
1352    ///
1353    /// As this function is typically called in top-level code that waits for results to be ready
1354    /// for the user to access, most callers should use [`ReadConsistency::Strong`].
1355    pub async fn wait_task_completion(
1356        &self,
1357        id: TaskId,
1358        consistency: ReadConsistency,
1359    ) -> Result<()> {
1360        read_task_output(
1361            self,
1362            id,
1363            ReadOutputOptions {
1364                // INVALIDATION: This doesn't return a value, only waits for it to be ready.
1365                tracking: ReadTracking::Untracked,
1366                consistency,
1367            },
1368        )
1369        .await?;
1370        Ok(())
1371    }
1372
1373    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1374    /// (`aggregation`). Will wait until an update happens.
1375    pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1376        self.aggregated_update_info(aggregation, Duration::MAX)
1377            .await
1378            .unwrap()
1379    }
1380
1381    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1382    /// (`aggregation`). Will only return None when the timeout is reached while
1383    /// waiting for the first update.
1384    pub async fn aggregated_update_info(
1385        &self,
1386        aggregation: Duration,
1387        timeout: Duration,
1388    ) -> Option<UpdateInfo> {
1389        let listener = self
1390            .event_foreground_done
1391            .listen_with_note(|| || "wait for update info".to_string());
1392        let wait_for_finish = {
1393            let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1394            if aggregation.is_zero() {
1395                if let Some((duration, tasks)) = update.take() {
1396                    return Some(UpdateInfo {
1397                        duration,
1398                        tasks,
1399                        reasons: take(reason_set),
1400                        placeholder_for_future_fields: (),
1401                    });
1402                } else {
1403                    true
1404                }
1405            } else {
1406                update.is_none()
1407            }
1408        };
1409        if wait_for_finish {
1410            if timeout == Duration::MAX {
1411                // wait for finish
1412                listener.await;
1413            } else {
1414                // wait for start, then wait for finish or timeout
1415                let start_listener = self
1416                    .event_foreground_start
1417                    .listen_with_note(|| || "wait for update info".to_string());
1418                if self
1419                    .currently_scheduled_foreground_jobs
1420                    .load(Ordering::Acquire)
1421                    == 0
1422                {
1423                    start_listener.await;
1424                } else {
1425                    drop(start_listener);
1426                }
1427                if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1428                    // Timeout
1429                    return None;
1430                }
1431            }
1432        }
1433        if !aggregation.is_zero() {
1434            loop {
1435                select! {
1436                    () = tokio::time::sleep(aggregation) => {
1437                        break;
1438                    }
1439                    () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1440                        // Resets the sleep
1441                    }
1442                }
1443            }
1444        }
1445        let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1446        if let Some((duration, tasks)) = update.take() {
1447            Some(UpdateInfo {
1448                duration,
1449                tasks,
1450                reasons: take(reason_set),
1451                placeholder_for_future_fields: (),
1452            })
1453        } else {
1454            panic!("aggregated_update_info must not called concurrently")
1455        }
1456    }
1457
1458    pub async fn wait_background_done(&self) {
1459        let listener = self.event_background_done.listen();
1460        if self
1461            .currently_scheduled_background_jobs
1462            .load(Ordering::Acquire)
1463            != 0
1464        {
1465            listener.await;
1466        }
1467    }
1468
1469    pub async fn stop_and_wait(&self) {
1470        #[cfg(feature = "inline_execution_stats")]
1471        if inline_stats_requested() {
1472            // Requested with `TURBO_ENGINE_INLINE_STATS=1`; printed rather than traced so it shows
1473            // up without a tracing subscriber configured.
1474            eprintln!(
1475                "turbo-tasks inline execution stats: {:#?}",
1476                self.inline_execution_stats()
1477            );
1478        }
1479        turbo_tasks_future_scope(self.pin(), async move {
1480            self.backend.stopping(self);
1481            self.stopped.store(true, Ordering::Release);
1482            {
1483                let listener = self
1484                    .event_foreground_done
1485                    .listen_with_note(|| || "wait for stop".to_string());
1486                if self
1487                    .currently_scheduled_foreground_jobs
1488                    .load(Ordering::Acquire)
1489                    != 0
1490                {
1491                    listener.await;
1492                }
1493            }
1494            {
1495                let listener = self.event_background_done.listen();
1496                if self
1497                    .currently_scheduled_background_jobs
1498                    .load(Ordering::Acquire)
1499                    != 0
1500                {
1501                    listener.await;
1502                }
1503            }
1504            self.backend.stop(self);
1505            // Deliver compilation events sent during shutdown (e.g. the persistence trace span)
1506            // to subscribers before returning, then close the queue so subscriptions end after
1507            // draining.
1508            self.compilation_events.flush_and_close().await;
1509        })
1510        .await;
1511    }
1512
1513    #[track_caller]
1514    pub(crate) fn schedule_background_job<T>(&self, func: T)
1515    where
1516        T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1517        T::CallOnceFuture: Send,
1518    {
1519        let mut this = self.pin();
1520        self.begin_background_job();
1521        tokio::spawn(
1522            TURBO_TASKS
1523                .scope(this.clone(), async move {
1524                    if !this.stopped.load(Ordering::Acquire) {
1525                        this = func(this).await;
1526                    }
1527                    this.finish_background_job();
1528                })
1529                .in_current_span(),
1530        );
1531    }
1532
1533    fn finish_current_task_state(&self) -> FinishedTaskState {
1534        CURRENT_TASK_STATE.with(|cell| {
1535            let current_task_state = &*cell.write().unwrap();
1536            FinishedTaskState {
1537                #[cfg(feature = "verify_determinism")]
1538                stateful: current_task_state.stateful,
1539                has_invalidator: current_task_state.has_invalidator,
1540            }
1541        })
1542    }
1543
1544    pub fn backend(&self) -> &B {
1545        &self.backend
1546    }
1547
1548    pub fn get_current_task_priority(&self) -> TaskPriority {
1549        CURRENT_TASK_STATE
1550            .try_with(|task_state| task_state.read().unwrap().priority)
1551            .unwrap_or(TaskPriority::initial())
1552    }
1553
1554    pub fn is_idle(&self) -> bool {
1555        self.currently_scheduled_foreground_jobs
1556            .load(Ordering::Acquire)
1557            == 0
1558    }
1559
1560    #[track_caller]
1561    pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1562        self.schedule_background_job(async move |this| {
1563            this.backend.run_backend_job(job, &*this).await;
1564            this
1565        })
1566    }
1567}
1568
1569struct TurboTasksExecutor;
1570
1571/// Run a future and abort the process if a panic is reported
1572///
1573/// Turbtasks catches panics from user code and propagates throught the task tree, but if it happens
1574/// as part of state management we have to abort
1575async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1576    match AssertUnwindSafe(f).catch_unwind().await {
1577        Ok(r) => r,
1578        Err(_) => {
1579            eprintln!(
1580                "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1581                 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1582                 https://github.com/vercel/next.js/discussions and include the panic message \
1583                 and stack trace above.\n\nAborting."
1584            );
1585            abort();
1586        }
1587    }
1588}
1589
1590impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1591    const LOWEST_PRIORITY: TaskPriority = TaskPriority::Initial;
1592
1593    type Future = impl Future<Output = ()> + Send + 'static;
1594
1595    fn execute(
1596        &self,
1597        this: &Arc<TurboTasks<B>>,
1598        scheduled_task: ScheduledTask,
1599        priority: TaskPriority,
1600    ) -> Self::Future {
1601        match scheduled_task {
1602            ScheduledTask::Task { task_id, span } => {
1603                let this2 = this.clone();
1604                let this = this.clone();
1605                let future = async move {
1606                    abort_on_panic(async {
1607                        // it's okay for execution ids to overflow and wrap, they're just used
1608                        // for an assert
1609                        let execution_id = this.execution_id_factory.wrapping_get();
1610                        let current_task_state =
1611                            CurrentTaskStateHandle::new(CurrentTaskState::new(
1612                                task_id,
1613                                execution_id,
1614                                priority,
1615                                false, // in_top_level_task
1616                            ));
1617                        let single_execution_future = async {
1618                            if this.stopped.load(Ordering::Acquire) {
1619                                this.backend.task_execution_canceled(task_id, &*this);
1620                                return None;
1621                            }
1622
1623                            let TaskExecutionSpec { future, span } = this
1624                                .backend
1625                                .try_start_task_execution(task_id, priority, &*this)?;
1626
1627                            // When a reader claimed this task and is polling it inline, let it
1628                            // record the outcome on this span rather than its own.
1629                            InlineExecutionSpanSlot::set(&span);
1630
1631                            async {
1632                                let result = CaptureFuture::new(future).await;
1633
1634                                // wait for all spawned local tasks using `local` to finish
1635                                wait_for_local_tasks().await;
1636
1637                                let result = match result {
1638                                    Ok(Ok(raw_vc)) => {
1639                                        // This is safe because we waited for all local tasks to
1640                                        // complete above
1641                                        raw_vc
1642                                            .to_non_local_unchecked_sync(&*this)
1643                                            .map_err(|err| err.into())
1644                                    }
1645                                    Ok(Err(err)) => Err(err.into()),
1646                                    Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1647                                };
1648
1649                                let finished_state = this.finish_current_task_state();
1650                                let cell_counters = CURRENT_TASK_STATE
1651                                    .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1652                                this.backend.task_execution_completed(
1653                                    task_id,
1654                                    result,
1655                                    &cell_counters,
1656                                    #[cfg(feature = "verify_determinism")]
1657                                    finished_state.stateful,
1658                                    finished_state.has_invalidator,
1659                                    &*this,
1660                                )
1661                            }
1662                            .instrument(span)
1663                            .await
1664                        };
1665                        if let Some(stale_priority) = CURRENT_TASK_STATE
1666                            .scope(current_task_state, single_execution_future)
1667                            .await
1668                        {
1669                            // Task was stale; re-schedule at the correct invalidation priority so
1670                            // other tasks can run in the right priority order.
1671                            this.schedule(task_id, stale_priority);
1672                        }
1673                        this.finish_foreground_job();
1674                    })
1675                    .await
1676                };
1677
1678                Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1679            }
1680            ScheduledTask::LocalTask {
1681                ty,
1682                persistence,
1683                parent_task: _,
1684                execution_id: _,
1685                local_task_id,
1686                global_task_state,
1687                span,
1688            } => {
1689                let this2 = this.clone();
1690                let this = this.clone();
1691                let task_type = ty.task_type;
1692                let future = async move {
1693                    let span = match &ty.task_type {
1694                        LocalTaskType::ResolveNative { native_fn } => {
1695                            native_fn.resolve_span(priority)
1696                        }
1697                        LocalTaskType::ResolveTrait { trait_method } => {
1698                            trait_method.resolve_span(priority)
1699                        }
1700                    };
1701                    // See the cached-task arm: lets a reader that claimed this local task record
1702                    // the outcome of its inline poll on this span.
1703                    InlineExecutionSpanSlot::set(&span);
1704                    abort_on_panic(
1705                        async move {
1706                            let result = match ty.task_type {
1707                                LocalTaskType::ResolveNative { native_fn } => {
1708                                    LocalTaskType::run_resolve_native(
1709                                        native_fn,
1710                                        ty.this,
1711                                        &*ty.arg,
1712                                        persistence,
1713                                        this,
1714                                    )
1715                                    .await
1716                                }
1717                                LocalTaskType::ResolveTrait { trait_method } => {
1718                                    LocalTaskType::run_resolve_trait(
1719                                        trait_method,
1720                                        ty.this.unwrap(),
1721                                        &*ty.arg,
1722                                        persistence,
1723                                        this,
1724                                    )
1725                                    .await
1726                                }
1727                            };
1728
1729                            let output = match result {
1730                                Ok(raw_vc) => OutputContent::Link(raw_vc),
1731                                Err(err) => OutputContent::Error(
1732                                    TurboTasksExecutionError::from(err)
1733                                        .with_local_task_context(task_type.to_string()),
1734                                ),
1735                            };
1736
1737                            CURRENT_TASK_STATE.with(move |gts| {
1738                                gts.write()
1739                                    .unwrap()
1740                                    .local_tasks
1741                                    .complete(local_task_id, output);
1742                            });
1743                        }
1744                        .instrument(span),
1745                    )
1746                    .await
1747                };
1748                let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1749
1750                Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1751            }
1752        }
1753    }
1754
1755    fn discard(
1756        &self,
1757        this: &Arc<TurboTasks<B>>,
1758        scheduled_task: ScheduledTask,
1759        _priority: TaskPriority,
1760    ) {
1761        match scheduled_task {
1762            // A newer schedule of the same task replaced this one in the queue, so only that one
1763            // executes. Balance the foreground job `TurboTasks::schedule` began for this one.
1764            ScheduledTask::Task { .. } => this.finish_foreground_job(),
1765            // Local tasks have unique keys and are never replaced, and they don't begin a
1766            // foreground job either.
1767            ScheduledTask::LocalTask { .. } => {}
1768        }
1769    }
1770}
1771
1772struct FinishedTaskState {
1773    /// True if the task has state in cells (interior mutability).
1774    /// Only tracked when verify_determinism feature is enabled.
1775    #[cfg(feature = "verify_determinism")]
1776    stateful: bool,
1777
1778    /// True if the task uses an external invalidator
1779    has_invalidator: bool,
1780}
1781
1782impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1783    fn dynamic_call(
1784        &self,
1785        native_fn: &'static NativeFunction,
1786        this: Option<RawVc>,
1787        arg: &mut dyn DynTaskInputsStorage,
1788        inputs_resolved: InputResolution,
1789        persistence: TaskPersistence,
1790    ) -> RawVc {
1791        self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1792    }
1793    fn native_call(
1794        &self,
1795        native_fn: &'static NativeFunction,
1796        this: Option<RawVc>,
1797        arg: &mut dyn DynTaskInputsStorage,
1798        persistence: TaskPersistence,
1799    ) -> RawVc {
1800        self.native_call(native_fn, this, arg, persistence)
1801    }
1802    fn trait_call(
1803        &self,
1804        trait_method: &'static TraitMethod,
1805        this: RawVc,
1806        arg: &mut dyn DynTaskInputsStorage,
1807        inputs_resolved: InputResolution,
1808        persistence: TaskPersistence,
1809    ) -> RawVc {
1810        self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1811    }
1812
1813    #[track_caller]
1814    fn run(
1815        &self,
1816        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1817    ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1818        let this = self.pin();
1819        Box::pin(async move { this.run(future).await })
1820    }
1821
1822    #[track_caller]
1823    fn run_once(
1824        &self,
1825        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1826    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1827        let this = self.pin();
1828        Box::pin(async move { this.run_once(future).await })
1829    }
1830
1831    #[track_caller]
1832    fn run_once_with_reason(
1833        &self,
1834        reason: StaticOrArc<dyn InvalidationReason>,
1835        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1836    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1837        {
1838            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1839            reason_set.insert(reason);
1840        }
1841        let this = self.pin();
1842        Box::pin(async move { this.run_once(future).await })
1843    }
1844
1845    #[track_caller]
1846    fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1847        self.start_once_process(future)
1848    }
1849
1850    fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1851        if let Err(e) = self.compilation_events.send(event) {
1852            tracing::warn!("Failed to send compilation event: {e}");
1853        }
1854    }
1855
1856    fn get_task_name(&self, task: TaskId) -> String {
1857        self.backend.get_task_name(task, self)
1858    }
1859}
1860
1861impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1862    #[instrument(level = "info", skip_all, name = "invalidate")]
1863    fn invalidate(&self, task: TaskId) {
1864        self.backend.invalidate_task(task, self);
1865    }
1866
1867    #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1868    fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1869        {
1870            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1871            reason_set.insert(reason);
1872        }
1873        self.backend.invalidate_task(task, self);
1874    }
1875
1876    fn invalidate_serialization(&self, task: TaskId) {
1877        self.backend.invalidate_serialization(task, self);
1878    }
1879
1880    #[track_caller]
1881    fn try_read_task_output(
1882        &self,
1883        task: TaskId,
1884        options: ReadOutputOptions,
1885    ) -> Result<ReadOutcome<RawVc>> {
1886        if options.consistency == ReadConsistency::Eventual {
1887            debug_assert_not_in_top_level_task("read_task_output");
1888        }
1889        self.backend.try_read_task_output(
1890            task,
1891            current_task_if_available("reading Vcs"),
1892            options,
1893            self,
1894        )
1895    }
1896
1897    #[track_caller]
1898    fn try_read_task_cell(
1899        &self,
1900        task: TaskId,
1901        index: CellId,
1902        options: ReadCellOptions,
1903    ) -> Result<ReadOutcome<TypedCellContent>> {
1904        let reader = current_task_if_available("reading Vcs");
1905        self.backend
1906            .try_read_task_cell(task, index, reader, options, self)
1907    }
1908
1909    fn try_read_own_task_cell(
1910        &self,
1911        current_task: TaskId,
1912        index: CellId,
1913    ) -> Result<TypedCellContent> {
1914        self.backend
1915            .try_read_own_task_cell(current_task, index, self)
1916    }
1917
1918    #[track_caller]
1919    fn try_read_local_output(
1920        &self,
1921        execution_id: ExecutionId,
1922        local_task_id: LocalTaskId,
1923    ) -> Result<Result<RawVc, EventListener>> {
1924        debug_assert_not_in_top_level_task("read_local_output");
1925        CURRENT_TASK_STATE.with(|gts| {
1926            let gts_read = gts.read().unwrap();
1927
1928            // Local Vcs are local to their parent task's current execution, and do not exist
1929            // outside of it. This is weakly enforced at compile time using the `NonLocalValue`
1930            // marker trait. This assertion exists to handle any potential escapes that the
1931            // compile-time checks cannot capture.
1932            gts_read.assert_execution_id(execution_id);
1933
1934            match gts_read.local_tasks.get(local_task_id) {
1935                LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1936                LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1937            }
1938        })
1939    }
1940
1941    fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1942        // TODO: Add assert_not_in_top_level_task("read_task_collectibles") check here.
1943        // Collectible reads are eventually consistent.
1944        self.backend.read_task_collectibles(
1945            task,
1946            trait_id,
1947            current_task_if_available("reading collectibles"),
1948            self,
1949        )
1950    }
1951
1952    fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1953        self.try_execute_scheduled_task_inline(key)
1954    }
1955
1956    #[cfg(feature = "inline_execution_stats")]
1957    fn note_waited_for_in_progress_task(&self) {
1958        self.note_waited_for_in_progress_task()
1959    }
1960
1961    fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1962        self.backend.emit_collectible(
1963            trait_type,
1964            collectible,
1965            current_task("emitting collectible"),
1966            self,
1967        );
1968    }
1969
1970    fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1971        self.backend.unemit_collectible(
1972            trait_type,
1973            collectible,
1974            count,
1975            current_task("emitting collectible"),
1976            self,
1977        );
1978    }
1979
1980    fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1981        for (&collectible, &count) in collectibles {
1982            if count > 0 {
1983                self.backend.unemit_collectible(
1984                    trait_type,
1985                    collectible,
1986                    count as u32,
1987                    current_task("emitting collectible"),
1988                    self,
1989                );
1990            }
1991        }
1992    }
1993
1994    fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1995        self.try_read_own_task_cell(task, index)
1996    }
1997
1998    fn update_own_task_cell(
1999        &self,
2000        task: TaskId,
2001        index: CellId,
2002        content: CellContent,
2003        updated_key_hashes: Option<SmallVec<[u64; 2]>>,
2004        content_hash: Option<CellHash>,
2005        verification_mode: VerificationMode,
2006    ) {
2007        self.backend.update_task_cell(
2008            task,
2009            index,
2010            content,
2011            updated_key_hashes,
2012            content_hash,
2013            verification_mode,
2014            self,
2015        );
2016    }
2017
2018    fn connect_task(&self, task: TaskId) {
2019        self.backend
2020            .connect_task(task, current_task_if_available("connecting task"), self);
2021    }
2022
2023    fn mark_own_task_as_finished(&self, task: TaskId) {
2024        self.backend.mark_own_task_as_finished(task, self);
2025    }
2026
2027    fn pin_task_for_gc(&self, task: TaskId) {
2028        self.backend.pin_task_for_gc(task, self);
2029    }
2030
2031    fn unpin_task_for_gc(&self, task: TaskId) {
2032        self.backend.unpin_task_for_gc(task, self);
2033    }
2034
2035    /// Creates a future that inherits the current task id and task state. The current global task
2036    /// will wait for this future to be dropped before exiting.
2037    fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
2038        // this is similar to what happens for a local task, except that we keep the local task's
2039        // state as well.
2040        let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
2041        global_task_state
2042            .write()
2043            .unwrap()
2044            .local_tasks
2045            .register_detached();
2046        let wrapped = async move {
2047            // use a drop guard for panic safety
2048            struct DropGuard;
2049            impl Drop for DropGuard {
2050                fn drop(&mut self) {
2051                    CURRENT_TASK_STATE
2052                        .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
2053                }
2054            }
2055            let _guard = DropGuard;
2056            fut.await;
2057        };
2058        tokio::spawn(TURBO_TASKS.scope(
2059            turbo_tasks(),
2060            CURRENT_TASK_STATE.scope(global_task_state, wrapped),
2061        ));
2062    }
2063
2064    fn task_statistics(&self) -> &TaskStatisticsApi {
2065        self.backend.task_statistics()
2066    }
2067
2068    fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
2069        let this = self.pin();
2070        Box::pin(async move {
2071            this.stop_and_wait().await;
2072        })
2073    }
2074
2075    fn subscribe_to_compilation_events(
2076        &self,
2077        event_types: Option<Vec<String>>,
2078    ) -> Receiver<Arc<dyn CompilationEvent>> {
2079        self.compilation_events.subscribe(event_types)
2080    }
2081
2082    fn is_tracking_dependencies(&self) -> bool {
2083        self.backend.is_tracking_dependencies()
2084    }
2085}
2086
2087async fn wait_for_local_tasks() {
2088    let listener =
2089        CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
2090    let Some(listener) = listener else {
2091        return;
2092    };
2093    listener.await;
2094}
2095
2096pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
2097    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2098        Ok(id) => id,
2099        Err(_) => panic!(
2100            "{from} can only be used in the context of a turbo_tasks task execution or \
2101             turbo_tasks run"
2102        ),
2103    }
2104}
2105
2106pub(crate) fn current_task(from: &str) -> TaskId {
2107    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2108        Ok(Some(id)) => id,
2109        Ok(None) | Err(_) => {
2110            panic!("{from} can only be used in the context of a turbo_tasks task execution")
2111        }
2112    }
2113}
2114
2115/// Panics if we're not in a top-level task (e.g. [`run_once`]). Some function calls should only
2116/// happen in a top-level task (e.g. [`Effects::apply`][crate::Effects::apply]).
2117#[track_caller]
2118pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2119    if !cfg!(debug_assertions) {
2120        return;
2121    }
2122
2123    let in_top_level = CURRENT_TASK_STATE
2124        .try_with(|ts| ts.read().unwrap().in_top_level_task)
2125        .unwrap_or(true);
2126    if !in_top_level {
2127        panic!("{message}");
2128    }
2129}
2130
2131#[track_caller]
2132pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2133    if !cfg!(debug_assertions) {
2134        return;
2135    }
2136
2137    // HACK: We set this inside of `ReadRawVcFuture` to suppress warnings about an internal
2138    // consistency bug
2139    let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2140        .try_with(|&suppressed| suppressed)
2141        .unwrap_or(false);
2142    if suppressed {
2143        return;
2144    }
2145
2146    let in_top_level = CURRENT_TASK_STATE
2147        .try_with(|ts| ts.read().unwrap().in_top_level_task)
2148        .unwrap_or(false);
2149    if in_top_level {
2150        panic!(
2151            "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2152             Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2153             reads to avoid leaking inconsistent return values."
2154        );
2155    }
2156}
2157
2158pub async fn run<T: Send + 'static>(
2159    tt: Arc<dyn TurboTasksApi>,
2160    future: impl Future<Output = Result<T>> + Send + 'static,
2161) -> Result<T> {
2162    let (tx, rx) = tokio::sync::oneshot::channel();
2163
2164    tt.run(Box::pin(async move {
2165        let result = future.await?;
2166        tx.send(result)
2167            .map_err(|_| anyhow!("unable to send result"))?;
2168        Ok(())
2169    }))
2170    .await?;
2171
2172    Ok(rx.await?)
2173}
2174
2175pub async fn run_once<T: Send + 'static>(
2176    tt: Arc<dyn TurboTasksApi>,
2177    future: impl Future<Output = Result<T>> + Send + 'static,
2178) -> Result<T> {
2179    let (tx, rx) = tokio::sync::oneshot::channel();
2180
2181    tt.run_once(Box::pin(async move {
2182        let result = future.await?;
2183        tx.send(result)
2184            .map_err(|_| anyhow!("unable to send result"))?;
2185        Ok(())
2186    }))
2187    .await?;
2188
2189    Ok(rx.await?)
2190}
2191
2192pub async fn run_once_with_reason<T: Send + 'static>(
2193    tt: Arc<dyn TurboTasksApi>,
2194    reason: impl InvalidationReason,
2195    future: impl Future<Output = Result<T>> + Send + 'static,
2196) -> Result<T> {
2197    let (tx, rx) = tokio::sync::oneshot::channel();
2198
2199    tt.run_once_with_reason(
2200        (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2201        Box::pin(async move {
2202            let result = future.await?;
2203            tx.send(result)
2204                .map_err(|_| anyhow!("unable to send result"))?;
2205            Ok(())
2206        }),
2207    )
2208    .await?;
2209
2210    Ok(rx.await?)
2211}
2212
2213/// Calls [`TurboTasks::dynamic_call`] for the current turbo tasks instance.
2214pub fn dynamic_call(
2215    func: &'static NativeFunction,
2216    this: Option<RawVc>,
2217    arg: &mut dyn DynTaskInputsStorage,
2218    inputs_resolved: InputResolution,
2219    persistence: TaskPersistence,
2220) -> RawVc {
2221    with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2222}
2223
2224/// Calls [`TurboTasks::trait_call`] for the current turbo tasks instance.
2225pub fn trait_call(
2226    trait_method: &'static TraitMethod,
2227    this: RawVc,
2228    arg: &mut dyn DynTaskInputsStorage,
2229    inputs_resolved: InputResolution,
2230    persistence: TaskPersistence,
2231) -> RawVc {
2232    with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2233}
2234
2235pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2236    TURBO_TASKS.with(|arc| arc.clone())
2237}
2238
2239pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2240    TURBO_TASKS.with(Arc::downgrade)
2241}
2242
2243pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2244    TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2245}
2246
2247pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2248    TURBO_TASKS.with(|arc| func(arc))
2249}
2250
2251pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2252    TURBO_TASKS.sync_scope(tt, f)
2253}
2254
2255pub fn turbo_tasks_future_scope<T>(
2256    tt: Arc<dyn TurboTasksApi>,
2257    f: impl Future<Output = T>,
2258) -> impl Future<Output = T> {
2259    TURBO_TASKS.scope(tt, f)
2260}
2261
2262/// Spawns the given future within the context of the current task.
2263///
2264/// Beware: this method is not safe to use in production code. It is only
2265/// intended for use in tests and for debugging purposes.
2266pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2267    turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2268}
2269
2270/// Marks the current task as finished. This excludes it from waiting for
2271/// strongly consistency.
2272pub fn mark_finished() {
2273    with_turbo_tasks(|tt| {
2274        tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2275    });
2276}
2277
2278/// Returns a [`SerializationInvalidator`] that can be used to invalidate the
2279/// serialization of the current task cells.
2280///
2281/// Also marks the current task as stateful when the `verify_determinism` feature is enabled,
2282/// since State allocation implies interior mutability.
2283pub fn get_serialization_invalidator() -> SerializationInvalidator {
2284    CURRENT_TASK_STATE.with(|cell| {
2285        let CurrentTaskState {
2286            task_id,
2287            #[cfg(feature = "verify_determinism")]
2288            stateful,
2289            ..
2290        } = &mut *cell.write().unwrap();
2291        #[cfg(feature = "verify_determinism")]
2292        {
2293            *stateful = true;
2294        }
2295        let Some(task_id) = *task_id else {
2296            panic!(
2297                "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2298                 task execution"
2299            );
2300        };
2301        SerializationInvalidator::new(task_id)
2302    })
2303}
2304
2305pub fn mark_invalidator() {
2306    CURRENT_TASK_STATE.with(|cell| {
2307        let CurrentTaskState {
2308            has_invalidator, ..
2309        } = &mut *cell.write().unwrap();
2310        *has_invalidator = true;
2311    })
2312}
2313
2314/// Marks the current task as stateful. This is used to indicate that the task
2315/// has interior mutability (e.g., via [`State`][crate::State]), which means
2316/// the task may produce different outputs even with the same inputs.
2317///
2318/// Only has an effect when the `verify_determinism` feature is enabled.
2319pub fn mark_stateful() {
2320    #[cfg(feature = "verify_determinism")]
2321    {
2322        CURRENT_TASK_STATE.with(|cell| {
2323            let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2324            *stateful = true;
2325        })
2326    }
2327    // No-op when verify_determinism is not enabled
2328}
2329
2330/// Marks the current task context as being in a top-level task. When in a top-level task,
2331/// eventually consistent reads will panic. It is almost always a mistake to perform an eventually
2332/// consistent read at the top-level of the application.
2333pub fn mark_top_level_task() {
2334    if cfg!(debug_assertions) {
2335        CURRENT_TASK_STATE.with(|cell| {
2336            cell.write().unwrap().in_top_level_task = true;
2337        })
2338    }
2339}
2340
2341/// Unmarks the current task context as being in a top-level task. The opposite of
2342/// [`mark_top_level_task`].
2343///
2344/// This utility can be okay in unit tests, where we're observing the internal behavior of
2345/// turbo-tasks, but otherwise, it is probably a mistake to call this function.
2346///
2347/// Calling this will allow eventually-consistent reads at the top-level, potentially exposing
2348/// incomplete computations and internal errors caused by eventual consistency that would've been
2349/// caught when the function was re-run. A strongly-consistent read re-runs parts of a task until
2350/// all of the dependencies have settled.
2351pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2352    if cfg!(debug_assertions) {
2353        CURRENT_TASK_STATE.with(|cell| {
2354            cell.write().unwrap().in_top_level_task = false;
2355        })
2356    }
2357}
2358
2359/// Pins the current task against garbage collection for the rest of the session, keeping it (and,
2360/// via the reachability it anchors, the values it produced) alive even if it becomes disconnected
2361/// from the live task graph. Use this when a value escapes the tracked graph — e.g. a `Vc` sent out
2362/// of a `spawn_detached` future across a channel, or handed across the NAPI boundary — so no
2363/// persistent parent lists it as a child and it would otherwise be collected.
2364///
2365/// No-op outside a task context, and on backends without garbage collection.
2366pub fn prevent_gc() {
2367    if let Some(task) = current_task_if_available("prevent_gc") {
2368        with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2369    }
2370}
2371
2372/// An RAII guard that pins an [`OperationVc`]'s task against garbage collection.
2373pub struct GcRoot<T: ?Sized> {
2374    tt: Arc<dyn TurboTasksApi>,
2375    vc: OperationVc<T>,
2376}
2377
2378impl<T: ?Sized> GcRoot<T> {
2379    /// Pins `vc`'s task, returning a guard that unpins it on drop.
2380    pub fn pin(tt: Arc<dyn TurboTasksApi>, vc: OperationVc<T>) -> Self {
2381        tt.pin_task_for_gc(vc.task_id());
2382        Self { tt, vc }
2383    }
2384}
2385
2386/// A guard derefs to the operation it pins, so [`OperationVc`]'s own methods can be called on it
2387/// directly and `*guard` recovers the operation itself.
2388impl<T: ?Sized> Deref for GcRoot<T> {
2389    type Target = OperationVc<T>;
2390
2391    fn deref(&self) -> &Self::Target {
2392        &self.vc
2393    }
2394}
2395
2396impl<T: ?Sized> Clone for GcRoot<T> {
2397    fn clone(&self) -> Self {
2398        Self::pin(self.tt.clone(), self.vc)
2399    }
2400}
2401
2402impl<T: ?Sized> Drop for GcRoot<T> {
2403    fn drop(&mut self) {
2404        self.tt.unpin_task_for_gc(self.vc.task_id());
2405    }
2406}
2407
2408impl<T: ?Sized> Debug for GcRoot<T> {
2409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2410        f.debug_struct("GcRoot").field("vc", &self.vc).finish()
2411    }
2412}
2413
2414impl<T: ?Sized> PartialEq for GcRoot<T> {
2415    /// Compares the pinned operation only. Two guards for the same operation are interchangeable
2416    /// as far as reachability is concerned, even though each holds its own pin.
2417    fn eq(&self, other: &Self) -> bool {
2418        self.vc == other.vc
2419    }
2420}
2421
2422impl<T: ?Sized> Eq for GcRoot<T> {}
2423
2424impl<T: ?Sized> Hash for GcRoot<T> {
2425    /// Hashes the pinned operation, consistently with [`PartialEq`], so a guard can be looked up
2426    /// in a set by the [`OperationVc`] it pins (see the [`Borrow`] impl).
2427    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2428        self.vc.hash(state);
2429    }
2430}
2431
2432/// Lets a collection keyed on guards be queried with the bare operation: `Borrow` plus the
2433/// matching [`Hash`]/[`Eq`] impls give `OperationVc<T>: Equivalent<GcRoot<T>>`, so e.g.
2434/// `IndexSet<GcRoot<T>>::swap_remove` accepts an `&OperationVc<T>`.
2435impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2436    fn borrow(&self) -> &OperationVc<T> {
2437        &self.vc
2438    }
2439}
2440
2441/// Safety: a `GcRoot` contains exactly one [`OperationVc`] and no [`Vc`] or [`ResolvedVc`], which
2442/// is what [`OperationValue`] asserts.
2443unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2444
2445/// Safety: mirrors the [`OperationVc`] impl — a `GcRoot` holds no task-local data beyond the
2446/// operation it pins.
2447unsafe impl<T: NonLocalValue + ?Sized> NonLocalValue for GcRoot<T> {}
2448
2449pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2450    with_turbo_tasks(|tt| {
2451        let raw_vc = collectible.node.node;
2452        tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2453    })
2454}
2455
2456pub(crate) async fn read_task_output(
2457    this: &dyn TurboTasksApi,
2458    id: TaskId,
2459    options: ReadOutputOptions,
2460) -> Result<RawVc> {
2461    loop {
2462        match this.try_read_task_output(id, options)? {
2463            ReadOutcome::Value(result) => return Ok(result),
2464            ReadOutcome::Scheduled(listener) => {
2465                // Nobody has started it yet, so take it over instead of waiting for a worker.
2466                if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2467                    continue;
2468                }
2469                listener.await
2470            }
2471            ReadOutcome::InProgress(listener) => {
2472                // A worker is on it — there is nothing to take over, so don't touch the queue.
2473                #[cfg(feature = "inline_execution_stats")]
2474                this.note_waited_for_in_progress_task();
2475                listener.await
2476            }
2477        }
2478    }
2479}
2480
2481/// A reference to a task's cell with methods that allow updating the contents
2482/// of the cell.
2483///
2484/// Mutations should not outside of the task that that owns this cell. Doing so
2485/// is a logic error, and may lead to incorrect caching behavior.
2486#[derive(Clone, Copy)]
2487pub struct CurrentCellRef {
2488    current_task: TaskId,
2489    index: CellId,
2490}
2491
2492type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2493
2494/// What a conditional cell update returns: the new content, the key hashes that changed, and an
2495/// optional hash of the value.
2496type CellUpdate = (
2497    SharedReference,
2498    Option<SmallVec<[u64; 2]>>,
2499    Option<CellHash>,
2500);
2501
2502/// The callback [`CurrentCellRef::conditional_update_with_shared_reference`] takes. It is a `dyn`
2503/// trait object so that the function's body is compiled once rather than once per cell type.
2504type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2505
2506impl CurrentCellRef {
2507    /// Updates the cell if the given `functor` returns a value.
2508    fn conditional_update<T>(
2509        &self,
2510        functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2511    ) where
2512        T: VcValueType,
2513    {
2514        // `FnMut` cannot move out of its captures, and the callee calls this at most once, so
2515        // the `FnOnce` is handed over through an `Option`.
2516        let mut functor = Some(functor);
2517        self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2518            let functor = functor.take().expect("functor is called at most once");
2519            let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2520            let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2521            Some((
2522                SharedReference::new(triomphe::Arc::new(new_value)),
2523                updated_key_hashes,
2524                content_hash,
2525            ))
2526        })
2527    }
2528
2529    /// Updates the cell if the given `functor` returns a `SharedReference`.
2530    ///
2531    /// `functor` is a `dyn` trait object rather than a generic parameter on purpose. This body is
2532    /// identical for every cell type, so making it generic monomorphized it once per
2533    /// `VcValueType` in the dependency graph — over a thousand copies of the same code. The
2534    /// indirect call it costs instead is negligible next to the cell read and update it wraps.
2535    fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2536        let tt = turbo_tasks();
2537        let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2538        let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2539        if let Some((update, updated_key_hashes, content_hash)) = update {
2540            tt.update_own_task_cell(
2541                self.current_task,
2542                self.index,
2543                CellContent(Some(update)),
2544                updated_key_hashes,
2545                content_hash,
2546                VerificationMode::EqualityCheck,
2547            )
2548        }
2549    }
2550
2551    /// Replace the current cell's content with `new_value` if the current content is not equal by
2552    /// value with the existing content.
2553    ///
2554    /// The comparison happens using the value itself, not the [`VcRead::Target`] of that value.
2555    ///
2556    /// Take this example of a custom equality implementation on a transparent wrapper type:
2557    ///
2558    /// ```
2559    /// #[turbo_tasks::value(transparent, eq = "manual")]
2560    /// #[derive(Clone)]
2561    /// struct Wrapper(Vec<u32>);
2562    ///
2563    /// impl PartialEq for Wrapper {
2564    ///     fn eq(&self, other: &Wrapper) -> bool {
2565    ///         // Example: order doesn't matter for equality
2566    ///         let (mut this, mut other) = (self.0.clone(), other.0.clone());
2567    ///         this.sort_unstable();
2568    ///         other.sort_unstable();
2569    ///         this == other
2570    ///     }
2571    /// }
2572    ///
2573    /// impl Eq for Wrapper {}
2574    /// ```
2575    ///
2576    /// Comparisons of [`Vc<Wrapper>`] used when updating the cell will use `Wrapper`'s custom
2577    /// equality implementation, rather than the one provided by the target ([`Vec<u32>`]) type.
2578    ///
2579    /// However, in most cases, the default derived implementation of [`PartialEq`] is used which
2580    /// just forwards to the inner value's [`PartialEq`].
2581    ///
2582    /// If you already have a `SharedReference`, consider calling
2583    /// [`Self::compare_and_update_with_shared_reference`] which can re-use the [`SharedReference`]
2584    /// object.
2585    pub fn compare_and_update<T>(&self, new_value: T)
2586    where
2587        T: PartialEq + VcValueType,
2588    {
2589        self.conditional_update(|old_value| {
2590            if let Some(old_value) = old_value
2591                && old_value == &new_value
2592            {
2593                return None;
2594            }
2595            Some((new_value, None, None))
2596        });
2597    }
2598
2599    /// Replace the current cell's content with `new_shared_reference` if the current content is not
2600    /// equal by value with the existing content.
2601    ///
2602    /// If you already have a `SharedReference`, this is a faster version of
2603    /// [`CurrentCellRef::compare_and_update`].
2604    ///
2605    /// The value should be stored in [`SharedReference`] using the type `T`.
2606    pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2607    where
2608        T: VcValueType + PartialEq,
2609    {
2610        let mut new_shared_reference = Some(new_shared_reference);
2611        self.conditional_update_with_shared_reference(&mut |old_sr| {
2612            let new_shared_reference = new_shared_reference
2613                .take()
2614                .expect("functor is called at most once");
2615            if let Some(old_sr) = old_sr {
2616                let old_value = extract_sr_value::<T>(old_sr);
2617                let new_value = extract_sr_value::<T>(&new_shared_reference);
2618                if old_value == new_value {
2619                    return None;
2620                }
2621            }
2622            Some((new_shared_reference, None, None))
2623        });
2624    }
2625
2626    /// Replace the current cell's content if the new value is different.
2627    ///
2628    /// Like [`Self::compare_and_update`], but also computes and stores a hash of the value.
2629    /// When the cell's transient data is evicted, the stored hash enables the backend to detect
2630    /// whether the value actually changed without re-comparing values—avoiding unnecessary
2631    /// downstream invalidation.
2632    ///
2633    /// Requires `T: DeterministicHash` in addition to `T: PartialEq`.
2634    pub fn hashed_compare_and_update<T>(&self, new_value: T)
2635    where
2636        T: PartialEq + DeterministicHash + VcValueType,
2637    {
2638        self.conditional_update(|old_value| {
2639            if let Some(old_value) = old_value
2640                && old_value == &new_value
2641            {
2642                return None;
2643            }
2644            let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2645
2646            Some((new_value, None, Some(content_hash)))
2647        });
2648    }
2649
2650    /// Replace the current cell's content if the new value (from a pre-existing
2651    /// [`SharedReference`]) is different.
2652    ///
2653    /// Like [`Self::compare_and_update_with_shared_reference`], but also passes a hash
2654    /// for hash-based change detection when transient data has been evicted.
2655    pub fn hashed_compare_and_update_with_shared_reference<T>(
2656        &self,
2657        new_shared_reference: SharedReference,
2658    ) where
2659        T: VcValueType + PartialEq + DeterministicHash,
2660    {
2661        let mut new_shared_reference = Some(new_shared_reference);
2662        self.conditional_update_with_shared_reference(&mut move |old_sr| {
2663            let new_shared_reference = new_shared_reference
2664                .take()
2665                .expect("functor is called at most once");
2666            if let Some(old_sr) = old_sr {
2667                let old_value = extract_sr_value::<T>(old_sr);
2668                let new_value = extract_sr_value::<T>(&new_shared_reference);
2669                if old_value == new_value {
2670                    return None;
2671                }
2672            }
2673            let content_hash =
2674                hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2675            Some((new_shared_reference, None, Some(content_hash)))
2676        });
2677    }
2678
2679    /// See [`Self::compare_and_update`], but selectively update individual keys.
2680    pub fn keyed_compare_and_update<T>(&self, new_value: T)
2681    where
2682        T: PartialEq + VcValueType,
2683        VcReadTarget<T>: KeyedEq,
2684        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2685    {
2686        self.conditional_update(|old_value| {
2687            let Some(old_value) = old_value else {
2688                return Some((new_value, None, None));
2689            };
2690            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2691            let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2692            let updated_keys = old_value.different_keys(new_value_ref);
2693            if updated_keys.is_empty() {
2694                return None;
2695            }
2696            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2697            let updated_key_hashes = updated_keys
2698                .into_iter()
2699                .map(|key| FxBuildHasher.hash_one(key))
2700                .collect();
2701            Some((new_value, Some(updated_key_hashes), None))
2702        });
2703    }
2704
2705    /// See [`Self::compare_and_update_with_shared_reference`], but selectively update individual
2706    /// keys.
2707    pub fn keyed_compare_and_update_with_shared_reference<T>(
2708        &self,
2709        new_shared_reference: SharedReference,
2710    ) where
2711        T: VcValueType + PartialEq,
2712        VcReadTarget<T>: KeyedEq,
2713        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2714    {
2715        let mut new_shared_reference = Some(new_shared_reference);
2716        self.conditional_update_with_shared_reference(&mut |old_sr| {
2717            let new_shared_reference = new_shared_reference
2718                .take()
2719                .expect("functor is called at most once");
2720            let Some(old_sr) = old_sr else {
2721                return Some((new_shared_reference, None, None));
2722            };
2723            let old_value = extract_sr_value::<T>(old_sr);
2724            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2725            let new_value = extract_sr_value::<T>(&new_shared_reference);
2726            let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2727            let updated_keys = old_value.different_keys(new_value);
2728            if updated_keys.is_empty() {
2729                return None;
2730            }
2731            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2732            let updated_key_hashes = updated_keys
2733                .into_iter()
2734                .map(|key| FxBuildHasher.hash_one(key))
2735                .collect();
2736            Some((new_shared_reference, Some(updated_key_hashes), None))
2737        });
2738    }
2739
2740    /// Unconditionally updates the content of the cell.
2741    pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2742    where
2743        T: VcValueType,
2744    {
2745        let tt = turbo_tasks();
2746        tt.update_own_task_cell(
2747            self.current_task,
2748            self.index,
2749            CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2750            None,
2751            None,
2752            verification_mode,
2753        )
2754    }
2755
2756    /// A faster version of [`Self::update`] if you already have a
2757    /// [`SharedReference`].
2758    ///
2759    /// If the passed-in [`SharedReference`] is the same as the existing cell's
2760    /// by identity, no update is performed.
2761    ///
2762    /// The value should be stored in [`SharedReference`] using the type `T`.
2763    pub fn update_with_shared_reference(
2764        &self,
2765        shared_ref: SharedReference,
2766        verification_mode: VerificationMode,
2767    ) {
2768        let tt = turbo_tasks();
2769        let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2770            let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2771            if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2772                // pointer equality (not value equality)
2773                shared_ref_exp != shared_ref
2774            } else {
2775                true
2776            }
2777        } else {
2778            true
2779        };
2780        if update {
2781            tt.update_own_task_cell(
2782                self.current_task,
2783                self.index,
2784                CellContent(Some(shared_ref)),
2785                None,
2786                None,
2787                verification_mode,
2788            )
2789        }
2790    }
2791}
2792
2793impl From<CurrentCellRef> for RawVc {
2794    fn from(cell: CurrentCellRef) -> Self {
2795        RawVc::task_cell(cell.current_task, cell.index)
2796    }
2797}
2798
2799fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2800    sr.0.downcast_ref::<T>()
2801        .expect("cannot update SharedReference of different type")
2802}
2803
2804pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2805    find_cell_by_id(T::get_value_type_id())
2806}
2807
2808pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2809    CURRENT_TASK_STATE.with(|ts| {
2810        let current_task = current_task("celling turbo_tasks values");
2811        let mut ts = ts.write().unwrap();
2812        let map = ts.cell_counters.as_mut().unwrap();
2813        let current_index = map.entry(ty).or_default();
2814        let index = *current_index;
2815        assert!(
2816            index <= CellId::MAX_CELL_INDEX,
2817            "task allocated more than {} cells of a single type",
2818            CellId::MAX_CELL_INDEX as u64 + 1,
2819        );
2820        *current_index += 1;
2821        CurrentCellRef {
2822            current_task,
2823            index: CellId::new(ty, index),
2824        }
2825    })
2826}
2827
2828pub(crate) async fn read_local_output(
2829    this: &dyn TurboTasksApi,
2830    execution_id: ExecutionId,
2831    local_task_id: LocalTaskId,
2832) -> Result<RawVc> {
2833    loop {
2834        match this.try_read_local_output(execution_id, local_task_id)? {
2835            Ok(raw_vc) => return Ok(raw_vc),
2836            Err(event_listener) => {
2837                // The local task is not done yet. If it is only scheduled, execute it right here
2838                // instead of waiting for a worker to pick it up.
2839                if execute_read_target_inline(
2840                    this,
2841                    ScheduleKey::current_local_task(execution_id, local_task_id),
2842                ) {
2843                    continue;
2844                }
2845                event_listener.await
2846            }
2847        }
2848    }
2849}
2850
2851#[cfg(test)]
2852mod tests {
2853    use super::*;
2854
2855    #[test]
2856    fn test_inline_execution_depth_guard_restores_depth() {
2857        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2858        {
2859            let _outer = InlineExecutionDepthGuard::enter();
2860            {
2861                let _inner = InlineExecutionDepthGuard::enter();
2862                assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2863            }
2864            assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2865        }
2866        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2867    }
2868
2869    #[test]
2870    fn test_inline_depth_cap() {
2871        assert!(inline_execution_allowed(), "nothing is nested yet");
2872        let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2873            .map(|_| InlineExecutionDepthGuard::enter())
2874            .collect::<Vec<_>>();
2875        assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2876        assert!(
2877            !inline_execution_allowed(),
2878            "at the nesting cap reads wait for a worker instead of executing inline"
2879        );
2880
2881        // One level below the cap inline execution is allowed again.
2882        guards.pop();
2883        assert!(inline_execution_allowed());
2884    }
2885
2886    #[tokio::test]
2887    async fn test_poll_once_or_spawn_completed_execution() {
2888        assert!(
2889            poll_once_or_spawn(async {}),
2890            "a future that completes on the first poll is executed inline"
2891        );
2892        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2893    }
2894
2895    #[tokio::test]
2896    async fn test_poll_once_or_spawn_pending_execution() {
2897        let (tx, rx) = tokio::sync::oneshot::channel();
2898        let done = Arc::new(AtomicBool::new(false));
2899        let done_in_task = done.clone();
2900        assert!(
2901            !poll_once_or_spawn(async move {
2902                // Yields on the first poll, so it cannot be executed inline.
2903                tokio::task::yield_now().await;
2904                done_in_task.store(true, Ordering::SeqCst);
2905                let _ = tx.send(());
2906            }),
2907            "a future that yields is not completed inline"
2908        );
2909        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2910
2911        // ...but it was spawned, so it still runs to completion.
2912        rx.await.unwrap();
2913        assert!(done.load(Ordering::SeqCst));
2914    }
2915}