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