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    /// Runs `future` as a top-level task and returns its result.
968    ///
969    /// # Returned values must not contain `Vc` or `ResolvedVc` or if they do they should be covered by a pin
970    ///
971    /// `T` is deliberately unconstrained for ergonomics, but a `Vc`/`ResolvedVc` that escapes this
972    /// call is **not** protected against garbage collection. It can be collected, and
973    /// dereferencing the stale handle then fails.
974    ///    ///
975    /// To hand a computation out of a top-level task, return the [`OperationVc`] and pin it:
976    ///
977    /// ```ignore
978    /// let (resolved, op) = tt.run_once(async move {
979    ///     let op = my_operation(args);
980    ///     let resolved = op.resolve().strongly_consistent().await?;
981    ///     // Return the operation too, so it can be pinned below.
982    ///     Ok((resolved, op))
983    /// }).await?;
984    /// let _gc_root = GcRoot::pin(tt.clone(), op);  // unpins on drop
985    /// ```
986    ///
987    /// Returning owned data -- [`ReadRef`](crate::ReadRef), `RcStr`, plain values -- is always
988    /// fine.
989    pub async fn run_once<T: Send + 'static>(
990        &self,
991        future: impl Future<Output = Result<T>> + Send + 'static,
992    ) -> Result<T> {
993        let (tx, rx) = tokio::sync::oneshot::channel();
994        self.spawn_once_task(async move {
995            mark_top_level_task();
996            let result = future.await;
997            tx.send(result)
998                .map_err(|_| anyhow!("unable to send result"))?;
999            Ok(Completion::new())
1000        });
1001
1002        rx.await?
1003    }
1004
1005    /// Runs `future` as a top-level task and returns its result.
1006    ///
1007    /// The same constraint as [`run_once`](Self::run_once) applies to `T`: a `Vc`/`ResolvedVc`
1008    /// returned from here is not anchored against garbage collection. See that method's docs for
1009    /// why, and for the pin-the-`OperationVc` idiom.
1010    #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
1011    pub async fn run<T: Send + 'static>(
1012        &self,
1013        future: impl Future<Output = Result<T>> + Send + 'static,
1014    ) -> Result<T, TurboTasksExecutionError> {
1015        self.begin_foreground_job();
1016        // it's okay for execution ids to overflow and wrap, they're just used for an assert
1017        let execution_id = self.execution_id_factory.wrapping_get();
1018        let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
1019            execution_id,
1020            TaskPriority::initial(),
1021            true, // in_top_level_task
1022        ));
1023
1024        let result = TURBO_TASKS
1025            .scope(
1026                self.pin(),
1027                CURRENT_TASK_STATE.scope(current_task_state, async {
1028                    let result = CaptureFuture::new(future).await;
1029
1030                    // wait for all spawned local tasks using `local` to finish
1031                    wait_for_local_tasks().await;
1032
1033                    match result {
1034                        Ok(Ok(value)) => Ok(value),
1035                        Ok(Err(err)) => Err(err.into()),
1036                        Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1037                    }
1038                }),
1039            )
1040            .await;
1041        self.finish_foreground_job();
1042        result
1043    }
1044
1045    pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1046        let this = self.pin();
1047        tokio::spawn(async move {
1048            this.pin()
1049                .run_once(async move {
1050                    this.finish_foreground_job();
1051                    future.await;
1052                    this.begin_foreground_job();
1053                    Ok(())
1054                })
1055                .await
1056                .unwrap()
1057        });
1058    }
1059
1060    pub(crate) fn native_call(
1061        &self,
1062        native_fn: &'static NativeFunction,
1063        this: Option<RawVc>,
1064        arg: &mut dyn DynTaskInputsStorage,
1065        persistence: TaskPersistence,
1066    ) -> RawVc {
1067        RawVc::task_output(self.backend.get_or_create_task(
1068            native_fn,
1069            this,
1070            arg,
1071            current_task_if_available("turbo_function calls"),
1072            persistence,
1073            self,
1074        ))
1075    }
1076
1077    pub fn dynamic_call(
1078        &self,
1079        native_fn: &'static NativeFunction,
1080        this: Option<RawVc>,
1081        arg: &mut dyn DynTaskInputsStorage,
1082        inputs_resolved: InputResolution,
1083        persistence: TaskPersistence,
1084    ) -> RawVc {
1085        if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1086            return self.native_call(native_fn, this, arg, persistence);
1087        }
1088        // Need async resolution — must move the arg to the heap now
1089        let arg = arg.take_box();
1090        let task_type = LocalTaskSpec {
1091            task_type: LocalTaskType::ResolveNative { native_fn },
1092            this,
1093            arg,
1094        };
1095        self.schedule_local_task(task_type, persistence)
1096    }
1097
1098    pub fn trait_call(
1099        &self,
1100        trait_method: &'static TraitMethod,
1101        this: RawVc,
1102        arg: &mut dyn DynTaskInputsStorage,
1103        inputs_resolved: InputResolution,
1104        persistence: TaskPersistence,
1105    ) -> RawVc {
1106        // avoid creating a wrapper task if self is already resolved
1107        // for resolved cells we already know the value type so we can lookup the
1108        // function
1109        if let Some((_, cell_id)) = this.as_task_cell() {
1110            match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1111                Some(native_fn) => {
1112                    if let Some(filter) = native_fn.arg_meta.filter_owned {
1113                        let (resolved, mut arg) = (filter)(arg);
1114                        return self.dynamic_call(
1115                            native_fn,
1116                            Some(this),
1117                            &mut arg,
1118                            resolved,
1119                            persistence,
1120                        );
1121                    } else {
1122                        return self.dynamic_call(
1123                            native_fn,
1124                            Some(this),
1125                            arg,
1126                            inputs_resolved,
1127                            persistence,
1128                        );
1129                    }
1130                }
1131                None => {
1132                    // We are destined to fail at this point, but we just retry resolution in the
1133                    // local task since we cannot report an error from here.
1134                    // TODO: A panic seems appropriate since the immediate caller is to blame
1135                }
1136            }
1137        }
1138
1139        // create a wrapper task to resolve all inputs
1140        let task_type = LocalTaskSpec {
1141            task_type: LocalTaskType::ResolveTrait { trait_method },
1142            this: Some(this),
1143            arg: arg.take_box(),
1144        };
1145
1146        self.schedule_local_task(task_type, persistence)
1147    }
1148
1149    #[track_caller]
1150    pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1151        self.begin_foreground_job();
1152        self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1153
1154        let task = ScheduledTask::Task {
1155            task_id,
1156            span: Span::current(),
1157        };
1158        self.priority_runner.schedule(&self.pin(), task, priority);
1159    }
1160
1161    fn schedule_local_task(
1162        &self,
1163        ty: LocalTaskSpec,
1164        // if this is a `LocalTaskType::Resolve*`, we may spawn another task with this persistence,
1165        persistence: TaskPersistence,
1166    ) -> RawVc {
1167        let task_type = ty.task_type;
1168        let (global_task_state, execution_id, priority, local_task_id) =
1169            CURRENT_TASK_STATE.with(|gts| {
1170                let mut gts_write = gts.write().unwrap();
1171                let local_task_id = gts_write.local_tasks.create(task_type);
1172                (
1173                    gts.clone(),
1174                    gts_write.execution_id,
1175                    gts_write.priority,
1176                    local_task_id,
1177                )
1178            });
1179
1180        let task = ScheduledTask::LocalTask {
1181            ty,
1182            persistence,
1183            execution_id,
1184            local_task_id,
1185            global_task_state,
1186            span: Span::current(),
1187        };
1188        self.priority_runner.schedule(&self.pin(), task, priority);
1189
1190        RawVc::local_output(execution_id, local_task_id, persistence)
1191    }
1192
1193    /// Executes the task inline if possible, returns true if it executed to completion.
1194    fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1195        let this = self.pin();
1196        self.inline_counters.claim_attempted();
1197        if let Some(future) = self.priority_runner.claim(&this, &key) {
1198            let completed = poll_once_or_spawn(future);
1199            if completed {
1200                self.inline_counters.claim_completed();
1201            } else {
1202                self.inline_counters.claim_yielded();
1203            }
1204            return completed;
1205        }
1206        self.inline_counters.claim_failed();
1207        false
1208    }
1209
1210    #[cfg(feature = "inline_execution_stats")]
1211    fn note_waited_for_in_progress_task(&self) {
1212        self.inline_counters.waited_in_progress();
1213    }
1214
1215    fn begin_foreground_job(&self) {
1216        if self
1217            .currently_scheduled_foreground_jobs
1218            .fetch_add(1, Ordering::AcqRel)
1219            == 0
1220        {
1221            *self.start.lock().unwrap() = Some(Instant::now());
1222            self.event_foreground_start.notify(usize::MAX);
1223            self.backend.idle_end(self);
1224        }
1225    }
1226
1227    fn finish_foreground_job(&self) {
1228        if self
1229            .currently_scheduled_foreground_jobs
1230            .fetch_sub(1, Ordering::AcqRel)
1231            == 1
1232        {
1233            self.backend.idle_start(self);
1234            // That's not super race-condition-safe, but it's only for
1235            // statistical reasons
1236            let total = self.scheduled_tasks.load(Ordering::Acquire);
1237            self.scheduled_tasks.store(0, Ordering::Release);
1238            if let Some(start) = *self.start.lock().unwrap() {
1239                let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1240                if let Some(update) = update.as_mut() {
1241                    update.0 += start.elapsed();
1242                    update.1 += total;
1243                } else {
1244                    *update = Some((start.elapsed(), total));
1245                }
1246            }
1247            self.event_foreground_done.notify(usize::MAX);
1248        }
1249    }
1250
1251    fn begin_background_job(&self) {
1252        self.currently_scheduled_background_jobs
1253            .fetch_add(1, Ordering::Relaxed);
1254    }
1255
1256    fn finish_background_job(&self) {
1257        if self
1258            .currently_scheduled_background_jobs
1259            .fetch_sub(1, Ordering::Relaxed)
1260            == 1
1261        {
1262            self.event_background_done.notify(usize::MAX);
1263        }
1264    }
1265
1266    pub fn get_in_progress_count(&self) -> usize {
1267        self.currently_scheduled_foreground_jobs
1268            .load(Ordering::Acquire)
1269    }
1270
1271    /// Counters describing how reads and inline execution interacted. Diagnostics only; a dump of
1272    /// these can be requested with `TURBO_ENGINE_INLINE_STATS=1`.
1273    #[cfg(feature = "inline_execution_stats")]
1274    #[doc(hidden)]
1275    pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1276        let counters = &self.inline_counters;
1277        InlineExecutionStats {
1278            queued: self.priority_runner.total_queued(),
1279            claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1280            claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1281            claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1282            claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1283            waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1284        }
1285    }
1286
1287    /// Waits for the given task to finish executing. This works by performing an untracked read,
1288    /// and discarding the value of the task output.
1289    ///
1290    /// [`ReadConsistency::Eventual`] means that this will return after the task executes, but
1291    /// before all dependencies have completely settled.
1292    ///
1293    /// [`ReadConsistency::Strong`] means that this will also wait for the task and all dependencies
1294    /// to fully settle before returning.
1295    ///
1296    /// As this function is typically called in top-level code that waits for results to be ready
1297    /// for the user to access, most callers should use [`ReadConsistency::Strong`].
1298    pub async fn wait_task_completion(
1299        &self,
1300        id: TaskId,
1301        consistency: ReadConsistency,
1302    ) -> Result<()> {
1303        read_task_output(
1304            self,
1305            id,
1306            ReadOutputOptions {
1307                // INVALIDATION: This doesn't return a value, only waits for it to be ready.
1308                tracking: ReadTracking::Untracked,
1309                consistency,
1310            },
1311        )
1312        .await?;
1313        Ok(())
1314    }
1315
1316    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1317    /// (`aggregation`). Will wait until an update happens.
1318    pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1319        self.aggregated_update_info(aggregation, Duration::MAX)
1320            .await
1321            .unwrap()
1322    }
1323
1324    /// Returns [UpdateInfo] with all updates aggregated over a given duration
1325    /// (`aggregation`). Will only return None when the timeout is reached while
1326    /// waiting for the first update.
1327    pub async fn aggregated_update_info(
1328        &self,
1329        aggregation: Duration,
1330        timeout: Duration,
1331    ) -> Option<UpdateInfo> {
1332        let listener = self
1333            .event_foreground_done
1334            .listen_with_note(|| || "wait for update info".to_string());
1335        let wait_for_finish = {
1336            let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1337            if aggregation.is_zero() {
1338                if let Some((duration, tasks)) = update.take() {
1339                    return Some(UpdateInfo {
1340                        duration,
1341                        tasks,
1342                        reasons: take(reason_set),
1343                        placeholder_for_future_fields: (),
1344                    });
1345                } else {
1346                    true
1347                }
1348            } else {
1349                update.is_none()
1350            }
1351        };
1352        if wait_for_finish {
1353            if timeout == Duration::MAX {
1354                // wait for finish
1355                listener.await;
1356            } else {
1357                // wait for start, then wait for finish or timeout
1358                let start_listener = self
1359                    .event_foreground_start
1360                    .listen_with_note(|| || "wait for update info".to_string());
1361                if self
1362                    .currently_scheduled_foreground_jobs
1363                    .load(Ordering::Acquire)
1364                    == 0
1365                {
1366                    start_listener.await;
1367                } else {
1368                    drop(start_listener);
1369                }
1370                if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1371                    // Timeout
1372                    return None;
1373                }
1374            }
1375        }
1376        if !aggregation.is_zero() {
1377            loop {
1378                select! {
1379                    () = tokio::time::sleep(aggregation) => {
1380                        break;
1381                    }
1382                    () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1383                        // Resets the sleep
1384                    }
1385                }
1386            }
1387        }
1388        let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1389        if let Some((duration, tasks)) = update.take() {
1390            Some(UpdateInfo {
1391                duration,
1392                tasks,
1393                reasons: take(reason_set),
1394                placeholder_for_future_fields: (),
1395            })
1396        } else {
1397            panic!("aggregated_update_info must not called concurrently")
1398        }
1399    }
1400
1401    pub async fn wait_background_done(&self) {
1402        let listener = self.event_background_done.listen();
1403        if self
1404            .currently_scheduled_background_jobs
1405            .load(Ordering::Acquire)
1406            != 0
1407        {
1408            listener.await;
1409        }
1410    }
1411
1412    pub async fn stop_and_wait(&self) {
1413        #[cfg(feature = "inline_execution_stats")]
1414        if inline_stats_requested() {
1415            // Requested with `TURBO_ENGINE_INLINE_STATS=1`; printed rather than traced so it shows
1416            // up without a tracing subscriber configured.
1417            eprintln!(
1418                "turbo-tasks inline execution stats: {:#?}",
1419                self.inline_execution_stats()
1420            );
1421        }
1422        turbo_tasks_future_scope(self.pin(), async move {
1423            self.backend.stopping(self);
1424            self.stopped.store(true, Ordering::Release);
1425            {
1426                let listener = self
1427                    .event_foreground_done
1428                    .listen_with_note(|| || "wait for stop".to_string());
1429                if self
1430                    .currently_scheduled_foreground_jobs
1431                    .load(Ordering::Acquire)
1432                    != 0
1433                {
1434                    listener.await;
1435                }
1436            }
1437            {
1438                let listener = self.event_background_done.listen();
1439                if self
1440                    .currently_scheduled_background_jobs
1441                    .load(Ordering::Acquire)
1442                    != 0
1443                {
1444                    listener.await;
1445                }
1446            }
1447            self.backend.stop(self);
1448            // Deliver compilation events sent during shutdown (e.g. the persistence trace span)
1449            // to subscribers before returning, then close the queue so subscriptions end after
1450            // draining.
1451            self.compilation_events.flush_and_close().await;
1452        })
1453        .await;
1454    }
1455
1456    #[track_caller]
1457    pub(crate) fn schedule_background_job<T>(&self, func: T)
1458    where
1459        T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1460        T::CallOnceFuture: Send,
1461    {
1462        let mut this = self.pin();
1463        self.begin_background_job();
1464        tokio::spawn(
1465            TURBO_TASKS
1466                .scope(this.clone(), async move {
1467                    if !this.stopped.load(Ordering::Acquire) {
1468                        this = func(this).await;
1469                    }
1470                    this.finish_background_job();
1471                })
1472                .in_current_span(),
1473        );
1474    }
1475
1476    fn finish_current_task_state(&self) -> FinishedTaskState {
1477        CURRENT_TASK_STATE.with(|cell| {
1478            let current_task_state = &*cell.write().unwrap();
1479            FinishedTaskState {
1480                #[cfg(feature = "verify_determinism")]
1481                stateful: current_task_state.stateful,
1482                has_invalidator: current_task_state.has_invalidator,
1483            }
1484        })
1485    }
1486
1487    pub fn backend(&self) -> &B {
1488        &self.backend
1489    }
1490
1491    pub fn get_current_task_priority(&self) -> TaskPriority {
1492        CURRENT_TASK_STATE
1493            .try_with(|task_state| task_state.read().unwrap().priority)
1494            .unwrap_or(TaskPriority::initial())
1495    }
1496
1497    pub fn is_idle(&self) -> bool {
1498        self.currently_scheduled_foreground_jobs
1499            .load(Ordering::Acquire)
1500            == 0
1501    }
1502
1503    #[track_caller]
1504    pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1505        self.schedule_background_job(async move |this| {
1506            this.backend.run_backend_job(job, &*this).await;
1507            this
1508        })
1509    }
1510}
1511
1512struct TurboTasksExecutor;
1513
1514/// Run a future and abort the process if a panic is reported
1515///
1516/// Turbtasks catches panics from user code and propagates throught the task tree, but if it happens
1517/// as part of state management we have to abort
1518async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1519    match AssertUnwindSafe(f).catch_unwind().await {
1520        Ok(r) => r,
1521        Err(_) => {
1522            eprintln!(
1523                "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1524                 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1525                 https://github.com/vercel/next.js/discussions and include the panic message \
1526                 and stack trace above.\n\nAborting."
1527            );
1528            abort();
1529        }
1530    }
1531}
1532
1533impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1534    type Future = impl Future<Output = ()> + Send + 'static;
1535
1536    fn execute(
1537        &self,
1538        this: &Arc<TurboTasks<B>>,
1539        scheduled_task: ScheduledTask,
1540        priority: TaskPriority,
1541    ) -> Self::Future {
1542        match scheduled_task {
1543            ScheduledTask::Task { task_id, span } => {
1544                let this2 = this.clone();
1545                let this = this.clone();
1546                let future = async move {
1547                    abort_on_panic(async {
1548                        // it's okay for execution ids to overflow and wrap, they're just used
1549                        // for an assert
1550                        let execution_id = this.execution_id_factory.wrapping_get();
1551                        let current_task_state =
1552                            CurrentTaskStateHandle::new(CurrentTaskState::new(
1553                                task_id,
1554                                execution_id,
1555                                priority,
1556                                false, // in_top_level_task
1557                            ));
1558                        let single_execution_future = async {
1559                            if this.stopped.load(Ordering::Acquire) {
1560                                this.backend.task_execution_canceled(task_id, &*this);
1561                                return None;
1562                            }
1563
1564                            let TaskExecutionSpec { future, span } = this
1565                                .backend
1566                                .try_start_task_execution(task_id, priority, &*this)?;
1567
1568                            // When a reader claimed this task and is polling it inline, let it
1569                            // record the outcome on this span rather than its own.
1570                            InlineExecutionSpanSlot::set(&span);
1571
1572                            async {
1573                                let result = CaptureFuture::new(future).await;
1574
1575                                // wait for all spawned local tasks using `local` to finish
1576                                wait_for_local_tasks().await;
1577
1578                                let result = match result {
1579                                    Ok(Ok(raw_vc)) => {
1580                                        // This is safe because we waited for all local tasks to
1581                                        // complete above
1582                                        raw_vc
1583                                            .to_non_local_unchecked_sync(&*this)
1584                                            .map_err(|err| err.into())
1585                                    }
1586                                    Ok(Err(err)) => Err(err.into()),
1587                                    Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1588                                };
1589
1590                                let finished_state = this.finish_current_task_state();
1591                                let cell_counters = CURRENT_TASK_STATE
1592                                    .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1593                                this.backend.task_execution_completed(
1594                                    task_id,
1595                                    result,
1596                                    &cell_counters,
1597                                    #[cfg(feature = "verify_determinism")]
1598                                    finished_state.stateful,
1599                                    finished_state.has_invalidator,
1600                                    &*this,
1601                                )
1602                            }
1603                            .instrument(span)
1604                            .await
1605                        };
1606                        if let Some(stale_priority) = CURRENT_TASK_STATE
1607                            .scope(current_task_state, single_execution_future)
1608                            .await
1609                        {
1610                            // Task was stale; re-schedule at the correct invalidation priority so
1611                            // other tasks can run in the right priority order.
1612                            this.schedule(task_id, stale_priority);
1613                        }
1614                        this.finish_foreground_job();
1615                    })
1616                    .await
1617                };
1618
1619                Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1620            }
1621            ScheduledTask::LocalTask {
1622                ty,
1623                persistence,
1624                execution_id: _,
1625                local_task_id,
1626                global_task_state,
1627                span,
1628            } => {
1629                let this2 = this.clone();
1630                let this = this.clone();
1631                let task_type = ty.task_type;
1632                let future = async move {
1633                    let span = match &ty.task_type {
1634                        LocalTaskType::ResolveNative { native_fn } => {
1635                            native_fn.resolve_span(priority)
1636                        }
1637                        LocalTaskType::ResolveTrait { trait_method } => {
1638                            trait_method.resolve_span(priority)
1639                        }
1640                    };
1641                    // See the cached-task arm: lets a reader that claimed this local task record
1642                    // the outcome of its inline poll on this span.
1643                    InlineExecutionSpanSlot::set(&span);
1644                    abort_on_panic(
1645                        async move {
1646                            let result = match ty.task_type {
1647                                LocalTaskType::ResolveNative { native_fn } => {
1648                                    LocalTaskType::run_resolve_native(
1649                                        native_fn,
1650                                        ty.this,
1651                                        &*ty.arg,
1652                                        persistence,
1653                                        this,
1654                                    )
1655                                    .await
1656                                }
1657                                LocalTaskType::ResolveTrait { trait_method } => {
1658                                    LocalTaskType::run_resolve_trait(
1659                                        trait_method,
1660                                        ty.this.unwrap(),
1661                                        &*ty.arg,
1662                                        persistence,
1663                                        this,
1664                                    )
1665                                    .await
1666                                }
1667                            };
1668
1669                            let output = match result {
1670                                Ok(raw_vc) => OutputContent::Link(raw_vc),
1671                                Err(err) => OutputContent::Error(
1672                                    TurboTasksExecutionError::from(err)
1673                                        .with_local_task_context(task_type.to_string()),
1674                                ),
1675                            };
1676
1677                            CURRENT_TASK_STATE.with(move |gts| {
1678                                gts.write()
1679                                    .unwrap()
1680                                    .local_tasks
1681                                    .complete(local_task_id, output);
1682                            });
1683                        }
1684                        .instrument(span),
1685                    )
1686                    .await
1687                };
1688                let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1689
1690                Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1691            }
1692        }
1693    }
1694}
1695
1696struct FinishedTaskState {
1697    /// True if the task has state in cells (interior mutability).
1698    /// Only tracked when verify_determinism feature is enabled.
1699    #[cfg(feature = "verify_determinism")]
1700    stateful: bool,
1701
1702    /// True if the task uses an external invalidator
1703    has_invalidator: bool,
1704}
1705
1706impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1707    fn dynamic_call(
1708        &self,
1709        native_fn: &'static NativeFunction,
1710        this: Option<RawVc>,
1711        arg: &mut dyn DynTaskInputsStorage,
1712        inputs_resolved: InputResolution,
1713        persistence: TaskPersistence,
1714    ) -> RawVc {
1715        self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1716    }
1717    fn native_call(
1718        &self,
1719        native_fn: &'static NativeFunction,
1720        this: Option<RawVc>,
1721        arg: &mut dyn DynTaskInputsStorage,
1722        persistence: TaskPersistence,
1723    ) -> RawVc {
1724        self.native_call(native_fn, this, arg, persistence)
1725    }
1726    fn trait_call(
1727        &self,
1728        trait_method: &'static TraitMethod,
1729        this: RawVc,
1730        arg: &mut dyn DynTaskInputsStorage,
1731        inputs_resolved: InputResolution,
1732        persistence: TaskPersistence,
1733    ) -> RawVc {
1734        self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1735    }
1736
1737    #[track_caller]
1738    fn run(
1739        &self,
1740        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1741    ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1742        let this = self.pin();
1743        Box::pin(async move { this.run(future).await })
1744    }
1745
1746    #[track_caller]
1747    fn run_once(
1748        &self,
1749        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1750    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1751        let this = self.pin();
1752        Box::pin(async move { this.run_once(future).await })
1753    }
1754
1755    #[track_caller]
1756    fn run_once_with_reason(
1757        &self,
1758        reason: StaticOrArc<dyn InvalidationReason>,
1759        future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1760    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1761        {
1762            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1763            reason_set.insert(reason);
1764        }
1765        let this = self.pin();
1766        Box::pin(async move { this.run_once(future).await })
1767    }
1768
1769    #[track_caller]
1770    fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1771        self.start_once_process(future)
1772    }
1773
1774    fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1775        if let Err(e) = self.compilation_events.send(event) {
1776            tracing::warn!("Failed to send compilation event: {e}");
1777        }
1778    }
1779
1780    fn get_task_name(&self, task: TaskId) -> String {
1781        self.backend.get_task_name(task, self)
1782    }
1783}
1784
1785impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1786    #[instrument(level = "info", skip_all, name = "invalidate")]
1787    fn invalidate(&self, task: TaskId) {
1788        self.backend.invalidate_task(task, self);
1789    }
1790
1791    #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1792    fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1793        {
1794            let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1795            reason_set.insert(reason);
1796        }
1797        self.backend.invalidate_task(task, self);
1798    }
1799
1800    fn invalidate_serialization(&self, task: TaskId) {
1801        self.backend.invalidate_serialization(task, self);
1802    }
1803
1804    #[track_caller]
1805    fn try_read_task_output(
1806        &self,
1807        task: TaskId,
1808        options: ReadOutputOptions,
1809    ) -> Result<ReadOutcome<RawVc>> {
1810        if options.consistency == ReadConsistency::Eventual {
1811            debug_assert_not_in_top_level_task("read_task_output");
1812        }
1813        self.backend.try_read_task_output(
1814            task,
1815            current_task_if_available("reading Vcs"),
1816            options,
1817            self,
1818        )
1819    }
1820
1821    #[track_caller]
1822    fn try_read_task_cell(
1823        &self,
1824        task: TaskId,
1825        index: CellId,
1826        options: ReadCellOptions,
1827    ) -> Result<ReadOutcome<TypedCellContent>> {
1828        let reader = current_task_if_available("reading Vcs");
1829        self.backend
1830            .try_read_task_cell(task, index, reader, options, self)
1831    }
1832
1833    fn try_read_own_task_cell(
1834        &self,
1835        current_task: TaskId,
1836        index: CellId,
1837    ) -> Result<TypedCellContent> {
1838        self.backend
1839            .try_read_own_task_cell(current_task, index, self)
1840    }
1841
1842    #[track_caller]
1843    fn try_read_local_output(
1844        &self,
1845        execution_id: ExecutionId,
1846        local_task_id: LocalTaskId,
1847    ) -> Result<Result<RawVc, EventListener>> {
1848        debug_assert_not_in_top_level_task("read_local_output");
1849        CURRENT_TASK_STATE.with(|gts| {
1850            let gts_read = gts.read().unwrap();
1851
1852            // Local Vcs are local to their parent task's current execution, and do not exist
1853            // outside of it. This is weakly enforced at compile time using the `NonLocalValue`
1854            // marker trait. This assertion exists to handle any potential escapes that the
1855            // compile-time checks cannot capture.
1856            gts_read.assert_execution_id(execution_id);
1857
1858            match gts_read.local_tasks.get(local_task_id) {
1859                LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1860                LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1861            }
1862        })
1863    }
1864
1865    fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1866        // TODO: Add assert_not_in_top_level_task("read_task_collectibles") check here.
1867        // Collectible reads are eventually consistent.
1868        self.backend.read_task_collectibles(
1869            task,
1870            trait_id,
1871            current_task_if_available("reading collectibles"),
1872            self,
1873        )
1874    }
1875
1876    fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1877        self.try_execute_scheduled_task_inline(key)
1878    }
1879
1880    #[cfg(feature = "inline_execution_stats")]
1881    fn note_waited_for_in_progress_task(&self) {
1882        self.note_waited_for_in_progress_task()
1883    }
1884
1885    fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1886        self.backend.emit_collectible(
1887            trait_type,
1888            collectible,
1889            current_task("emitting collectible"),
1890            self,
1891        );
1892    }
1893
1894    fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1895        self.backend.unemit_collectible(
1896            trait_type,
1897            collectible,
1898            count,
1899            current_task("emitting collectible"),
1900            self,
1901        );
1902    }
1903
1904    fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1905        for (&collectible, &count) in collectibles {
1906            if count > 0 {
1907                self.backend.unemit_collectible(
1908                    trait_type,
1909                    collectible,
1910                    count as u32,
1911                    current_task("emitting collectible"),
1912                    self,
1913                );
1914            }
1915        }
1916    }
1917
1918    fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1919        self.try_read_own_task_cell(task, index)
1920    }
1921
1922    fn update_own_task_cell(
1923        &self,
1924        task: TaskId,
1925        index: CellId,
1926        content: CellContent,
1927        updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1928        content_hash: Option<CellHash>,
1929        verification_mode: VerificationMode,
1930    ) {
1931        self.backend.update_task_cell(
1932            task,
1933            index,
1934            content,
1935            updated_key_hashes,
1936            content_hash,
1937            verification_mode,
1938            self,
1939        );
1940    }
1941
1942    fn connect_task(&self, task: TaskId) {
1943        self.backend
1944            .connect_task(task, current_task_if_available("connecting task"), self);
1945    }
1946
1947    fn mark_own_task_as_finished(&self, task: TaskId) {
1948        self.backend.mark_own_task_as_finished(task, self);
1949    }
1950
1951    fn pin_task_for_gc(&self, task: TaskId) {
1952        self.backend.pin_task_for_gc(task, self);
1953    }
1954
1955    fn unpin_task_for_gc(&self, task: TaskId) {
1956        self.backend.unpin_task_for_gc(task, self);
1957    }
1958
1959    /// Creates a future that inherits the current task id and task state. The current global task
1960    /// will wait for this future to be dropped before exiting.
1961    fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1962        // this is similar to what happens for a local task, except that we keep the local task's
1963        // state as well.
1964        let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1965        global_task_state
1966            .write()
1967            .unwrap()
1968            .local_tasks
1969            .register_detached();
1970        let wrapped = async move {
1971            // use a drop guard for panic safety
1972            struct DropGuard;
1973            impl Drop for DropGuard {
1974                fn drop(&mut self) {
1975                    CURRENT_TASK_STATE
1976                        .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1977                }
1978            }
1979            let _guard = DropGuard;
1980            fut.await;
1981        };
1982        tokio::spawn(TURBO_TASKS.scope(
1983            turbo_tasks(),
1984            CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1985        ));
1986    }
1987
1988    fn task_statistics(&self) -> &TaskStatisticsApi {
1989        self.backend.task_statistics()
1990    }
1991
1992    fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1993        let this = self.pin();
1994        Box::pin(async move {
1995            this.stop_and_wait().await;
1996        })
1997    }
1998
1999    fn subscribe_to_compilation_events(
2000        &self,
2001        event_types: Option<Vec<String>>,
2002    ) -> Receiver<Arc<dyn CompilationEvent>> {
2003        self.compilation_events.subscribe(event_types)
2004    }
2005
2006    fn is_tracking_dependencies(&self) -> bool {
2007        self.backend.is_tracking_dependencies()
2008    }
2009}
2010
2011async fn wait_for_local_tasks() {
2012    let listener =
2013        CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
2014    let Some(listener) = listener else {
2015        return;
2016    };
2017    listener.await;
2018}
2019
2020pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
2021    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2022        Ok(id) => id,
2023        Err(_) => panic!(
2024            "{from} can only be used in the context of a turbo_tasks task execution or \
2025             turbo_tasks run"
2026        ),
2027    }
2028}
2029
2030pub(crate) fn current_task(from: &str) -> TaskId {
2031    match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2032        Ok(Some(id)) => id,
2033        Ok(None) | Err(_) => {
2034            panic!("{from} can only be used in the context of a turbo_tasks task execution")
2035        }
2036    }
2037}
2038
2039/// Panics if we're not in a top-level task (e.g. [`run_once`]). Some function calls should only
2040/// happen in a top-level task (e.g. [`Effects::apply`][crate::Effects::apply]).
2041#[track_caller]
2042pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2043    if !cfg!(debug_assertions) {
2044        return;
2045    }
2046
2047    let in_top_level = CURRENT_TASK_STATE
2048        .try_with(|ts| ts.read().unwrap().in_top_level_task)
2049        .unwrap_or(true);
2050    if !in_top_level {
2051        panic!("{message}");
2052    }
2053}
2054
2055#[track_caller]
2056pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2057    if !cfg!(debug_assertions) {
2058        return;
2059    }
2060
2061    // HACK: We set this inside of `ReadRawVcFuture` to suppress warnings about an internal
2062    // consistency bug
2063    let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2064        .try_with(|&suppressed| suppressed)
2065        .unwrap_or(false);
2066    if suppressed {
2067        return;
2068    }
2069
2070    let in_top_level = CURRENT_TASK_STATE
2071        .try_with(|ts| ts.read().unwrap().in_top_level_task)
2072        .unwrap_or(false);
2073    if in_top_level {
2074        panic!(
2075            "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2076             Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2077             reads to avoid leaking inconsistent return values."
2078        );
2079    }
2080}
2081
2082pub async fn run<T: Send + 'static>(
2083    tt: Arc<dyn TurboTasksApi>,
2084    future: impl Future<Output = Result<T>> + Send + 'static,
2085) -> Result<T> {
2086    let (tx, rx) = tokio::sync::oneshot::channel();
2087
2088    tt.run(Box::pin(async move {
2089        let result = future.await?;
2090        tx.send(result)
2091            .map_err(|_| anyhow!("unable to send result"))?;
2092        Ok(())
2093    }))
2094    .await?;
2095
2096    Ok(rx.await?)
2097}
2098
2099pub async fn run_once<T: Send + 'static>(
2100    tt: Arc<dyn TurboTasksApi>,
2101    future: impl Future<Output = Result<T>> + Send + 'static,
2102) -> Result<T> {
2103    let (tx, rx) = tokio::sync::oneshot::channel();
2104
2105    tt.run_once(Box::pin(async move {
2106        let result = future.await?;
2107        tx.send(result)
2108            .map_err(|_| anyhow!("unable to send result"))?;
2109        Ok(())
2110    }))
2111    .await?;
2112
2113    Ok(rx.await?)
2114}
2115
2116pub async fn run_once_with_reason<T: Send + 'static>(
2117    tt: Arc<dyn TurboTasksApi>,
2118    reason: impl InvalidationReason,
2119    future: impl Future<Output = Result<T>> + Send + 'static,
2120) -> Result<T> {
2121    let (tx, rx) = tokio::sync::oneshot::channel();
2122
2123    tt.run_once_with_reason(
2124        (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2125        Box::pin(async move {
2126            let result = future.await?;
2127            tx.send(result)
2128                .map_err(|_| anyhow!("unable to send result"))?;
2129            Ok(())
2130        }),
2131    )
2132    .await?;
2133
2134    Ok(rx.await?)
2135}
2136
2137/// Calls [`TurboTasks::dynamic_call`] for the current turbo tasks instance.
2138pub fn dynamic_call(
2139    func: &'static NativeFunction,
2140    this: Option<RawVc>,
2141    arg: &mut dyn DynTaskInputsStorage,
2142    inputs_resolved: InputResolution,
2143    persistence: TaskPersistence,
2144) -> RawVc {
2145    with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2146}
2147
2148/// Calls [`TurboTasks::trait_call`] for the current turbo tasks instance.
2149pub fn trait_call(
2150    trait_method: &'static TraitMethod,
2151    this: RawVc,
2152    arg: &mut dyn DynTaskInputsStorage,
2153    inputs_resolved: InputResolution,
2154    persistence: TaskPersistence,
2155) -> RawVc {
2156    with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2157}
2158
2159pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2160    TURBO_TASKS.with(|arc| arc.clone())
2161}
2162
2163pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2164    TURBO_TASKS.with(Arc::downgrade)
2165}
2166
2167pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2168    TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2169}
2170
2171pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2172    TURBO_TASKS.with(|arc| func(arc))
2173}
2174
2175pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2176    TURBO_TASKS.sync_scope(tt, f)
2177}
2178
2179pub fn turbo_tasks_future_scope<T>(
2180    tt: Arc<dyn TurboTasksApi>,
2181    f: impl Future<Output = T>,
2182) -> impl Future<Output = T> {
2183    TURBO_TASKS.scope(tt, f)
2184}
2185
2186/// Spawns the given future within the context of the current task.
2187///
2188/// Beware: this method is not safe to use in production code. It is only
2189/// intended for use in tests and for debugging purposes.
2190pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2191    turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2192}
2193
2194/// Marks the current task as finished. This excludes it from waiting for
2195/// strongly consistency.
2196pub fn mark_finished() {
2197    with_turbo_tasks(|tt| {
2198        tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2199    });
2200}
2201
2202/// Returns a [`SerializationInvalidator`] that can be used to invalidate the
2203/// serialization of the current task cells.
2204///
2205/// Also marks the current task as stateful when the `verify_determinism` feature is enabled,
2206/// since State allocation implies interior mutability.
2207pub fn get_serialization_invalidator() -> SerializationInvalidator {
2208    CURRENT_TASK_STATE.with(|cell| {
2209        let CurrentTaskState {
2210            task_id,
2211            #[cfg(feature = "verify_determinism")]
2212            stateful,
2213            ..
2214        } = &mut *cell.write().unwrap();
2215        #[cfg(feature = "verify_determinism")]
2216        {
2217            *stateful = true;
2218        }
2219        let Some(task_id) = *task_id else {
2220            panic!(
2221                "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2222                 task execution"
2223            );
2224        };
2225        SerializationInvalidator::new(task_id)
2226    })
2227}
2228
2229pub fn mark_invalidator() {
2230    CURRENT_TASK_STATE.with(|cell| {
2231        let CurrentTaskState {
2232            has_invalidator, ..
2233        } = &mut *cell.write().unwrap();
2234        *has_invalidator = true;
2235    })
2236}
2237
2238/// Marks the current task as stateful. This is used to indicate that the task
2239/// has interior mutability (e.g., via [`State`][crate::State]), which means
2240/// the task may produce different outputs even with the same inputs.
2241///
2242/// Only has an effect when the `verify_determinism` feature is enabled.
2243pub fn mark_stateful() {
2244    #[cfg(feature = "verify_determinism")]
2245    {
2246        CURRENT_TASK_STATE.with(|cell| {
2247            let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2248            *stateful = true;
2249        })
2250    }
2251    // No-op when verify_determinism is not enabled
2252}
2253
2254/// Marks the current task context as being in a top-level task. When in a top-level task,
2255/// eventually consistent reads will panic. It is almost always a mistake to perform an eventually
2256/// consistent read at the top-level of the application.
2257pub fn mark_top_level_task() {
2258    if cfg!(debug_assertions) {
2259        CURRENT_TASK_STATE.with(|cell| {
2260            cell.write().unwrap().in_top_level_task = true;
2261        })
2262    }
2263}
2264
2265/// Unmarks the current task context as being in a top-level task. The opposite of
2266/// [`mark_top_level_task`].
2267///
2268/// This utility can be okay in unit tests, where we're observing the internal behavior of
2269/// turbo-tasks, but otherwise, it is probably a mistake to call this function.
2270///
2271/// Calling this will allow eventually-consistent reads at the top-level, potentially exposing
2272/// incomplete computations and internal errors caused by eventual consistency that would've been
2273/// caught when the function was re-run. A strongly-consistent read re-runs parts of a task until
2274/// all of the dependencies have settled.
2275pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2276    if cfg!(debug_assertions) {
2277        CURRENT_TASK_STATE.with(|cell| {
2278            cell.write().unwrap().in_top_level_task = false;
2279        })
2280    }
2281}
2282
2283/// Pins the current task against garbage collection for the rest of the session, keeping it (and,
2284/// via the reachability it anchors, the values it produced) alive even if it becomes disconnected
2285/// from the live task graph. Use this when a value escapes the tracked graph — e.g. a `Vc` sent out
2286/// of a `spawn_detached` future across a channel, or handed across the NAPI boundary — so no
2287/// persistent parent lists it as a child and it would otherwise be collected.
2288///
2289/// No-op outside a task context, and on backends without garbage collection.
2290pub fn prevent_gc() {
2291    if let Some(task) = current_task_if_available("prevent_gc") {
2292        with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2293    }
2294}
2295
2296/// An RAII guard that pins an [`OperationVc`]'s task against garbage collection.
2297pub struct GcRoot<T: ?Sized> {
2298    tt: Arc<dyn TurboTasksApi>,
2299    vc: OperationVc<T>,
2300}
2301
2302impl<T: ?Sized> GcRoot<T> {
2303    /// Pins `vc`'s task, returning a guard that unpins it on drop.
2304    pub fn pin(tt: Arc<dyn TurboTasksApi>, vc: OperationVc<T>) -> Self {
2305        tt.pin_task_for_gc(vc.task_id());
2306        Self { tt, vc }
2307    }
2308}
2309
2310/// A guard derefs to the operation it pins, so [`OperationVc`]'s own methods can be called on it
2311/// directly and `*guard` recovers the operation itself.
2312impl<T: ?Sized> Deref for GcRoot<T> {
2313    type Target = OperationVc<T>;
2314
2315    fn deref(&self) -> &Self::Target {
2316        &self.vc
2317    }
2318}
2319
2320impl<T: ?Sized> Clone for GcRoot<T> {
2321    fn clone(&self) -> Self {
2322        Self::pin(self.tt.clone(), self.vc)
2323    }
2324}
2325
2326impl<T: ?Sized> Drop for GcRoot<T> {
2327    fn drop(&mut self) {
2328        self.tt.unpin_task_for_gc(self.vc.task_id());
2329    }
2330}
2331
2332impl<T: ?Sized> Debug for GcRoot<T> {
2333    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2334        f.debug_struct("GcRoot").field("vc", &self.vc).finish()
2335    }
2336}
2337
2338impl<T: ?Sized> PartialEq for GcRoot<T> {
2339    /// Compares the pinned operation only. Two guards for the same operation are interchangeable
2340    /// as far as reachability is concerned, even though each holds its own pin.
2341    fn eq(&self, other: &Self) -> bool {
2342        self.vc == other.vc
2343    }
2344}
2345
2346impl<T: ?Sized> Eq for GcRoot<T> {}
2347
2348impl<T: ?Sized> Hash for GcRoot<T> {
2349    /// Hashes the pinned operation, consistently with [`PartialEq`], so a guard can be looked up
2350    /// in a set by the [`OperationVc`] it pins (see the [`Borrow`] impl).
2351    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2352        self.vc.hash(state);
2353    }
2354}
2355
2356/// Lets a collection keyed on guards be queried with the bare operation: `Borrow` plus the
2357/// matching [`Hash`]/[`Eq`] impls give `OperationVc<T>: Equivalent<GcRoot<T>>`, so e.g.
2358/// `IndexSet<GcRoot<T>>::swap_remove` accepts an `&OperationVc<T>`.
2359impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2360    fn borrow(&self) -> &OperationVc<T> {
2361        &self.vc
2362    }
2363}
2364
2365/// Safety: a `GcRoot` contains exactly one [`OperationVc`] and no [`Vc`] or [`ResolvedVc`], which
2366/// is what [`OperationValue`] asserts.
2367unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2368
2369/// Safety: mirrors the [`OperationVc`] impl — a `GcRoot` holds no task-local data beyond the
2370/// operation it pins.
2371unsafe impl<T: NonLocalValue + ?Sized> NonLocalValue for GcRoot<T> {}
2372
2373pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2374    with_turbo_tasks(|tt| {
2375        let raw_vc = collectible.node.node;
2376        tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2377    })
2378}
2379
2380pub(crate) async fn read_task_output(
2381    this: &dyn TurboTasksApi,
2382    id: TaskId,
2383    options: ReadOutputOptions,
2384) -> Result<RawVc> {
2385    loop {
2386        match this.try_read_task_output(id, options)? {
2387            ReadOutcome::Value(result) => return Ok(result),
2388            ReadOutcome::Scheduled(listener) => {
2389                // Nobody has started it yet, so take it over instead of waiting for a worker.
2390                if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2391                    continue;
2392                }
2393                listener.await
2394            }
2395            ReadOutcome::InProgress(listener) => {
2396                // A worker is on it — there is nothing to take over, so don't touch the queue.
2397                #[cfg(feature = "inline_execution_stats")]
2398                this.note_waited_for_in_progress_task();
2399                listener.await
2400            }
2401        }
2402    }
2403}
2404
2405/// A reference to a task's cell with methods that allow updating the contents
2406/// of the cell.
2407///
2408/// Mutations should not outside of the task that that owns this cell. Doing so
2409/// is a logic error, and may lead to incorrect caching behavior.
2410#[derive(Clone, Copy)]
2411pub struct CurrentCellRef {
2412    current_task: TaskId,
2413    index: CellId,
2414}
2415
2416type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2417
2418/// What a conditional cell update returns: the new content, the key hashes that changed, and an
2419/// optional hash of the value.
2420type CellUpdate = (
2421    SharedReference,
2422    Option<SmallVec<[u64; 2]>>,
2423    Option<CellHash>,
2424);
2425
2426/// The callback [`CurrentCellRef::conditional_update_with_shared_reference`] takes. It is a `dyn`
2427/// trait object so that the function's body is compiled once rather than once per cell type.
2428type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2429
2430impl CurrentCellRef {
2431    /// Updates the cell if the given `functor` returns a value.
2432    fn conditional_update<T>(
2433        &self,
2434        functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2435    ) where
2436        T: VcValueType,
2437    {
2438        // `FnMut` cannot move out of its captures, and the callee calls this at most once, so
2439        // the `FnOnce` is handed over through an `Option`.
2440        let mut functor = Some(functor);
2441        self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2442            let functor = functor.take().expect("functor is called at most once");
2443            let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2444            let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2445            Some((
2446                SharedReference::new(triomphe::Arc::new(new_value)),
2447                updated_key_hashes,
2448                content_hash,
2449            ))
2450        })
2451    }
2452
2453    /// Updates the cell if the given `functor` returns a `SharedReference`.
2454    ///
2455    /// `functor` is a `dyn` trait object rather than a generic parameter on purpose. This body is
2456    /// identical for every cell type, so making it generic monomorphized it once per
2457    /// `VcValueType` in the dependency graph — over a thousand copies of the same code. The
2458    /// indirect call it costs instead is negligible next to the cell read and update it wraps.
2459    fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2460        let tt = turbo_tasks();
2461        let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2462        let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2463        if let Some((update, updated_key_hashes, content_hash)) = update {
2464            tt.update_own_task_cell(
2465                self.current_task,
2466                self.index,
2467                CellContent(Some(update)),
2468                updated_key_hashes,
2469                content_hash,
2470                VerificationMode::EqualityCheck,
2471            )
2472        }
2473    }
2474
2475    /// Replace the current cell's content with `new_value` if the current content is not equal by
2476    /// value with the existing content.
2477    ///
2478    /// The comparison happens using the value itself, not the [`VcRead::Target`] of that value.
2479    ///
2480    /// Take this example of a custom equality implementation on a transparent wrapper type:
2481    ///
2482    /// ```
2483    /// #[turbo_tasks::value(transparent, eq = "manual")]
2484    /// #[derive(Clone)]
2485    /// struct Wrapper(Vec<u32>);
2486    ///
2487    /// impl PartialEq for Wrapper {
2488    ///     fn eq(&self, other: &Wrapper) -> bool {
2489    ///         // Example: order doesn't matter for equality
2490    ///         let (mut this, mut other) = (self.0.clone(), other.0.clone());
2491    ///         this.sort_unstable();
2492    ///         other.sort_unstable();
2493    ///         this == other
2494    ///     }
2495    /// }
2496    ///
2497    /// impl Eq for Wrapper {}
2498    /// ```
2499    ///
2500    /// Comparisons of [`Vc<Wrapper>`] used when updating the cell will use `Wrapper`'s custom
2501    /// equality implementation, rather than the one provided by the target ([`Vec<u32>`]) type.
2502    ///
2503    /// However, in most cases, the default derived implementation of [`PartialEq`] is used which
2504    /// just forwards to the inner value's [`PartialEq`].
2505    ///
2506    /// If you already have a `SharedReference`, consider calling
2507    /// [`Self::compare_and_update_with_shared_reference`] which can re-use the [`SharedReference`]
2508    /// object.
2509    pub fn compare_and_update<T>(&self, new_value: T)
2510    where
2511        T: PartialEq + VcValueType,
2512    {
2513        self.conditional_update(|old_value| {
2514            if let Some(old_value) = old_value
2515                && old_value == &new_value
2516            {
2517                return None;
2518            }
2519            Some((new_value, None, None))
2520        });
2521    }
2522
2523    /// Replace the current cell's content with `new_shared_reference` if the current content is not
2524    /// equal by value with the existing content.
2525    ///
2526    /// If you already have a `SharedReference`, this is a faster version of
2527    /// [`CurrentCellRef::compare_and_update`].
2528    ///
2529    /// The value should be stored in [`SharedReference`] using the type `T`.
2530    pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2531    where
2532        T: VcValueType + PartialEq,
2533    {
2534        let mut new_shared_reference = Some(new_shared_reference);
2535        self.conditional_update_with_shared_reference(&mut |old_sr| {
2536            let new_shared_reference = new_shared_reference
2537                .take()
2538                .expect("functor is called at most once");
2539            if let Some(old_sr) = old_sr {
2540                let old_value = extract_sr_value::<T>(old_sr);
2541                let new_value = extract_sr_value::<T>(&new_shared_reference);
2542                if old_value == new_value {
2543                    return None;
2544                }
2545            }
2546            Some((new_shared_reference, None, None))
2547        });
2548    }
2549
2550    /// Replace the current cell's content if the new value is different.
2551    ///
2552    /// Like [`Self::compare_and_update`], but also computes and stores a hash of the value.
2553    /// When the cell's transient data is evicted, the stored hash enables the backend to detect
2554    /// whether the value actually changed without re-comparing values—avoiding unnecessary
2555    /// downstream invalidation.
2556    ///
2557    /// Requires `T: DeterministicHash` in addition to `T: PartialEq`.
2558    pub fn hashed_compare_and_update<T>(&self, new_value: T)
2559    where
2560        T: PartialEq + DeterministicHash + VcValueType,
2561    {
2562        self.conditional_update(|old_value| {
2563            if let Some(old_value) = old_value
2564                && old_value == &new_value
2565            {
2566                return None;
2567            }
2568            let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2569
2570            Some((new_value, None, Some(content_hash)))
2571        });
2572    }
2573
2574    /// Replace the current cell's content if the new value (from a pre-existing
2575    /// [`SharedReference`]) is different.
2576    ///
2577    /// Like [`Self::compare_and_update_with_shared_reference`], but also passes a hash
2578    /// for hash-based change detection when transient data has been evicted.
2579    pub fn hashed_compare_and_update_with_shared_reference<T>(
2580        &self,
2581        new_shared_reference: SharedReference,
2582    ) where
2583        T: VcValueType + PartialEq + DeterministicHash,
2584    {
2585        let mut new_shared_reference = Some(new_shared_reference);
2586        self.conditional_update_with_shared_reference(&mut move |old_sr| {
2587            let new_shared_reference = new_shared_reference
2588                .take()
2589                .expect("functor is called at most once");
2590            if let Some(old_sr) = old_sr {
2591                let old_value = extract_sr_value::<T>(old_sr);
2592                let new_value = extract_sr_value::<T>(&new_shared_reference);
2593                if old_value == new_value {
2594                    return None;
2595                }
2596            }
2597            let content_hash =
2598                hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2599            Some((new_shared_reference, None, Some(content_hash)))
2600        });
2601    }
2602
2603    /// See [`Self::compare_and_update`], but selectively update individual keys.
2604    pub fn keyed_compare_and_update<T>(&self, new_value: T)
2605    where
2606        T: PartialEq + VcValueType,
2607        VcReadTarget<T>: KeyedEq,
2608        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2609    {
2610        self.conditional_update(|old_value| {
2611            let Some(old_value) = old_value else {
2612                return Some((new_value, None, None));
2613            };
2614            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2615            let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2616            let updated_keys = old_value.different_keys(new_value_ref);
2617            if updated_keys.is_empty() {
2618                return None;
2619            }
2620            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2621            let updated_key_hashes = updated_keys
2622                .into_iter()
2623                .map(|key| FxBuildHasher.hash_one(key))
2624                .collect();
2625            Some((new_value, Some(updated_key_hashes), None))
2626        });
2627    }
2628
2629    /// See [`Self::compare_and_update_with_shared_reference`], but selectively update individual
2630    /// keys.
2631    pub fn keyed_compare_and_update_with_shared_reference<T>(
2632        &self,
2633        new_shared_reference: SharedReference,
2634    ) where
2635        T: VcValueType + PartialEq,
2636        VcReadTarget<T>: KeyedEq,
2637        <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2638    {
2639        let mut new_shared_reference = Some(new_shared_reference);
2640        self.conditional_update_with_shared_reference(&mut |old_sr| {
2641            let new_shared_reference = new_shared_reference
2642                .take()
2643                .expect("functor is called at most once");
2644            let Some(old_sr) = old_sr else {
2645                return Some((new_shared_reference, None, None));
2646            };
2647            let old_value = extract_sr_value::<T>(old_sr);
2648            let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2649            let new_value = extract_sr_value::<T>(&new_shared_reference);
2650            let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2651            let updated_keys = old_value.different_keys(new_value);
2652            if updated_keys.is_empty() {
2653                return None;
2654            }
2655            // Duplicates are very unlikely, but ok since the backend is deduplicating them
2656            let updated_key_hashes = updated_keys
2657                .into_iter()
2658                .map(|key| FxBuildHasher.hash_one(key))
2659                .collect();
2660            Some((new_shared_reference, Some(updated_key_hashes), None))
2661        });
2662    }
2663
2664    /// Unconditionally updates the content of the cell.
2665    pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2666    where
2667        T: VcValueType,
2668    {
2669        let tt = turbo_tasks();
2670        tt.update_own_task_cell(
2671            self.current_task,
2672            self.index,
2673            CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2674            None,
2675            None,
2676            verification_mode,
2677        )
2678    }
2679
2680    /// A faster version of [`Self::update`] if you already have a
2681    /// [`SharedReference`].
2682    ///
2683    /// If the passed-in [`SharedReference`] is the same as the existing cell's
2684    /// by identity, no update is performed.
2685    ///
2686    /// The value should be stored in [`SharedReference`] using the type `T`.
2687    pub fn update_with_shared_reference(
2688        &self,
2689        shared_ref: SharedReference,
2690        verification_mode: VerificationMode,
2691    ) {
2692        let tt = turbo_tasks();
2693        let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2694            let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2695            if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2696                // pointer equality (not value equality)
2697                shared_ref_exp != shared_ref
2698            } else {
2699                true
2700            }
2701        } else {
2702            true
2703        };
2704        if update {
2705            tt.update_own_task_cell(
2706                self.current_task,
2707                self.index,
2708                CellContent(Some(shared_ref)),
2709                None,
2710                None,
2711                verification_mode,
2712            )
2713        }
2714    }
2715}
2716
2717impl From<CurrentCellRef> for RawVc {
2718    fn from(cell: CurrentCellRef) -> Self {
2719        RawVc::task_cell(cell.current_task, cell.index)
2720    }
2721}
2722
2723fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2724    sr.0.downcast_ref::<T>()
2725        .expect("cannot update SharedReference of different type")
2726}
2727
2728pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2729    find_cell_by_id(T::get_value_type_id())
2730}
2731
2732pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2733    CURRENT_TASK_STATE.with(|ts| {
2734        let current_task = current_task("celling turbo_tasks values");
2735        let mut ts = ts.write().unwrap();
2736        let map = ts.cell_counters.as_mut().unwrap();
2737        let current_index = map.entry(ty).or_default();
2738        let index = *current_index;
2739        assert!(
2740            index <= CellId::MAX_CELL_INDEX,
2741            "task allocated more than {} cells of a single type",
2742            CellId::MAX_CELL_INDEX as u64 + 1,
2743        );
2744        *current_index += 1;
2745        CurrentCellRef {
2746            current_task,
2747            index: CellId::new(ty, index),
2748        }
2749    })
2750}
2751
2752pub(crate) async fn read_local_output(
2753    this: &dyn TurboTasksApi,
2754    execution_id: ExecutionId,
2755    local_task_id: LocalTaskId,
2756) -> Result<RawVc> {
2757    loop {
2758        match this.try_read_local_output(execution_id, local_task_id)? {
2759            Ok(raw_vc) => return Ok(raw_vc),
2760            Err(event_listener) => {
2761                // The local task is not done yet. If it is only scheduled, execute it right here
2762                // instead of waiting for a worker to pick it up.
2763                if execute_read_target_inline(
2764                    this,
2765                    ScheduleKey::LocalTask(execution_id, local_task_id),
2766                ) {
2767                    continue;
2768                }
2769                event_listener.await
2770            }
2771        }
2772    }
2773}
2774
2775#[cfg(test)]
2776mod tests {
2777    use super::*;
2778
2779    #[test]
2780    fn test_inline_execution_depth_guard_restores_depth() {
2781        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2782        {
2783            let _outer = InlineExecutionDepthGuard::enter();
2784            {
2785                let _inner = InlineExecutionDepthGuard::enter();
2786                assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2787            }
2788            assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2789        }
2790        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2791    }
2792
2793    #[test]
2794    fn test_inline_depth_cap() {
2795        assert!(inline_execution_allowed(), "nothing is nested yet");
2796        let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2797            .map(|_| InlineExecutionDepthGuard::enter())
2798            .collect::<Vec<_>>();
2799        assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2800        assert!(
2801            !inline_execution_allowed(),
2802            "at the nesting cap reads wait for a worker instead of executing inline"
2803        );
2804
2805        // One level below the cap inline execution is allowed again.
2806        guards.pop();
2807        assert!(inline_execution_allowed());
2808    }
2809
2810    #[tokio::test]
2811    async fn test_poll_once_or_spawn_completed_execution() {
2812        assert!(
2813            poll_once_or_spawn(async {}),
2814            "a future that completes on the first poll is executed inline"
2815        );
2816        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2817    }
2818
2819    #[tokio::test]
2820    async fn test_poll_once_or_spawn_pending_execution() {
2821        let (tx, rx) = tokio::sync::oneshot::channel();
2822        let done = Arc::new(AtomicBool::new(false));
2823        let done_in_task = done.clone();
2824        assert!(
2825            !poll_once_or_spawn(async move {
2826                // Yields on the first poll, so it cannot be executed inline.
2827                tokio::task::yield_now().await;
2828                done_in_task.store(true, Ordering::SeqCst);
2829                let _ = tx.send(());
2830            }),
2831            "a future that yields is not completed inline"
2832        );
2833        assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2834
2835        // ...but it was spawned, so it still runs to completion.
2836        rx.await.unwrap();
2837        assert!(done.load(Ordering::SeqCst));
2838    }
2839}