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