Skip to main content

turbo_tasks/
manager.rs

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