Skip to main content

turbo_tasks/
manager.rs

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