1use std::{
2 borrow::Borrow,
3 cell::Cell,
4 cmp::Reverse,
5 fmt::{Debug, Display},
6 future::Future,
7 hash::{BuildHasher, BuildHasherDefault, Hash},
8 mem::take,
9 ops::Deref,
10 panic::AssertUnwindSafe,
11 pin::Pin,
12 process::abort,
13 sync::{
14 Arc, Mutex, RwLock, Weak,
15 atomic::{AtomicBool, AtomicUsize, Ordering},
16 },
17 task::{Context, Poll, Waker},
18 time::{Duration, Instant},
19};
20
21use anyhow::{Result, anyhow};
22use auto_hash_map::AutoMap;
23use bincode::{Decode, Encode};
24use either::Either;
25use futures::FutureExt;
26use rustc_hash::{FxBuildHasher, FxHasher};
27use serde::{Deserialize, Serialize};
28use smallvec::SmallVec;
29use tokio::{select, sync::mpsc::Receiver, task_local};
30use tracing::{Instrument, Span, instrument};
31use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash128};
32
33use crate::{
34 CellId, Completion, InvalidationReason, InvalidationReasonSet, NonLocalValue, OperationValue,
35 OperationVc, OutputContent, RawVc, ReadCellOptions, ReadOutcome, ReadOutputOptions, ResolvedVc,
36 SharedReference, TaskId, TraitMethod, ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType,
37 backend::{
38 Backend, CellContent, CellHash, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType,
39 TurboTasksExecutionError, TypedCellContent, VerificationMode,
40 },
41 capture_future::CaptureFuture,
42 dyn_task_inputs::DynTaskInputsStorage,
43 event::{Event, EventListener},
44 id::{ExecutionId, LocalTaskId, TraitTypeId},
45 keyed::KeyedEq,
46 local_task_tracker::LocalTaskTracker,
47 macro_helpers::NativeFunction,
48 message_queue::{CompilationEvent, CompilationEventQueue},
49 priority_runner::{Claimable, Executor, PriorityRunner},
50 registry,
51 serialization_invalidation::SerializationInvalidator,
52 task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
53 task_statistics::TaskStatisticsApi,
54 trace::TraceRawVcs,
55 util::{IdFactory, StaticOrArc},
56};
57
58pub trait TurboTasksCallApi: Sync + Send {
61 fn dynamic_call(
68 &self,
69 native_fn: &'static NativeFunction,
70 this: Option<RawVc>,
71 arg: &mut dyn DynTaskInputsStorage,
72 inputs_resolved: InputResolution,
73 persistence: TaskPersistence,
74 ) -> RawVc;
75 fn native_call(
78 &self,
79 native_fn: &'static NativeFunction,
80 this: Option<RawVc>,
81 arg: &mut dyn DynTaskInputsStorage,
82 persistence: TaskPersistence,
83 ) -> RawVc;
84 fn trait_call(
91 &self,
92 trait_method: &'static TraitMethod,
93 this: RawVc,
94 arg: &mut dyn DynTaskInputsStorage,
95 inputs_resolved: InputResolution,
96 persistence: TaskPersistence,
97 ) -> RawVc;
98
99 fn run(
100 &self,
101 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
102 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>>;
103 fn run_once(
104 &self,
105 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
106 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
107 fn run_once_with_reason(
108 &self,
109 reason: StaticOrArc<dyn InvalidationReason>,
110 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
111 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
112 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
113
114 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
116
117 fn get_task_name(&self, task: TaskId) -> String;
119}
120
121pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
127 fn invalidate(&self, task: TaskId);
128 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>);
129
130 fn invalidate_serialization(&self, task: TaskId);
131
132 fn try_read_task_output(
133 &self,
134 task: TaskId,
135 options: ReadOutputOptions,
136 ) -> Result<ReadOutcome<RawVc>>;
137
138 fn try_read_task_cell(
139 &self,
140 task: TaskId,
141 index: CellId,
142 options: ReadCellOptions,
143 ) -> Result<ReadOutcome<TypedCellContent>>;
144
145 fn try_read_local_output(
160 &self,
161 execution_id: ExecutionId,
162 local_task_id: LocalTaskId,
163 ) -> Result<Result<RawVc, EventListener>>;
164
165 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
166
167 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
173
174 #[cfg(feature = "inline_execution_stats")]
177 fn note_waited_for_in_progress_task(&self);
178
179 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
180 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
181 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
182
183 fn try_read_own_task_cell(
186 &self,
187 current_task: TaskId,
188 index: CellId,
189 ) -> Result<TypedCellContent>;
190
191 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
192 fn update_own_task_cell(
193 &self,
194 task: TaskId,
195 index: CellId,
196 content: CellContent,
197 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
198 content_hash: Option<CellHash>,
199 verification_mode: VerificationMode,
200 );
201 fn mark_own_task_as_finished(&self, task: TaskId);
202
203 fn pin_task_for_gc(&self, task: TaskId);
206
207 fn unpin_task_for_gc(&self, task: TaskId);
209
210 fn connect_task(&self, task: TaskId);
211
212 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
217
218 fn task_statistics(&self) -> &TaskStatisticsApi;
219
220 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
221
222 fn subscribe_to_compilation_events(
223 &self,
224 event_types: Option<Vec<String>>,
225 ) -> Receiver<Arc<dyn CompilationEvent>>;
226
227 fn is_tracking_dependencies(&self) -> bool;
229}
230
231pub struct Unused<T> {
233 inner: T,
234}
235
236impl<T> Unused<T> {
237 pub unsafe fn new_unchecked(inner: T) -> Self {
243 Self { inner }
244 }
245
246 pub unsafe fn get_unchecked(&self) -> &T {
252 &self.inner
253 }
254
255 pub fn into(self) -> T {
257 self.inner
258 }
259}
260
261#[allow(clippy::manual_non_exhaustive)]
262pub struct UpdateInfo {
263 pub duration: Duration,
264 pub tasks: usize,
265 pub reasons: InvalidationReasonSet,
266 #[allow(dead_code)]
267 placeholder_for_future_fields: (),
268}
269
270#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
271pub enum TaskPersistence {
272 Persistent,
274
275 Transient,
282}
283
284impl Display for TaskPersistence {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 match self {
287 TaskPersistence::Persistent => write!(f, "persistent"),
288 TaskPersistence::Transient => write!(f, "transient"),
289 }
290 }
291}
292
293#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
296pub enum InputResolution {
297 Resolved,
300 Unresolved,
302}
303
304impl InputResolution {
305 #[inline]
306 pub fn from_is_resolved(is_resolved: bool) -> Self {
307 if is_resolved {
308 Self::Resolved
309 } else {
310 Self::Unresolved
311 }
312 }
313
314 #[inline]
315 pub fn is_resolved(self) -> bool {
316 matches!(self, Self::Resolved)
317 }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
321pub enum ReadConsistency {
322 #[default]
325 Eventual,
326 Strong,
331}
332
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334pub enum ReadCellTracking {
335 Tracked {
337 key: Option<u64>,
339 },
340 TrackOnlyError,
345 Untracked,
350}
351
352impl ReadCellTracking {
353 pub fn should_track(&self, is_err: bool) -> bool {
354 match self {
355 ReadCellTracking::Tracked { .. } => true,
356 ReadCellTracking::TrackOnlyError => is_err,
357 ReadCellTracking::Untracked => false,
358 }
359 }
360
361 pub fn key(&self) -> Option<u64> {
362 match self {
363 ReadCellTracking::Tracked { key } => *key,
364 ReadCellTracking::TrackOnlyError => None,
365 ReadCellTracking::Untracked => None,
366 }
367 }
368}
369
370impl Default for ReadCellTracking {
371 fn default() -> Self {
372 ReadCellTracking::Tracked { key: None }
373 }
374}
375
376impl Display for ReadCellTracking {
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 match self {
379 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
380 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
381 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
382 ReadCellTracking::Untracked => write!(f, "untracked"),
383 }
384 }
385}
386
387#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
388pub enum ReadTracking {
389 #[default]
391 Tracked,
392 TrackOnlyError,
397 Untracked,
402}
403
404impl ReadTracking {
405 pub fn should_track(&self, is_err: bool) -> bool {
406 match self {
407 ReadTracking::Tracked => true,
408 ReadTracking::TrackOnlyError => is_err,
409 ReadTracking::Untracked => false,
410 }
411 }
412}
413
414impl Display for ReadTracking {
415 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416 match self {
417 ReadTracking::Tracked => write!(f, "tracked"),
418 ReadTracking::TrackOnlyError => write!(f, "track only error"),
419 ReadTracking::Untracked => write!(f, "untracked"),
420 }
421 }
422}
423
424#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
425pub enum TaskPriority {
426 #[default]
427 Initial,
428 Invalidation {
429 priority: Reverse<u32>,
430 },
431 Recomputation,
432}
433
434impl TaskPriority {
435 pub fn invalidation(priority: u32) -> Self {
436 Self::Invalidation {
437 priority: Reverse(priority),
438 }
439 }
440
441 pub fn initial() -> Self {
442 Self::Initial
443 }
444
445 pub fn leaf() -> Self {
446 Self::Invalidation {
447 priority: Reverse(0),
448 }
449 }
450
451 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
452 match self {
453 TaskPriority::Initial => parent_priority,
454 TaskPriority::Invalidation { priority } => {
455 if let TaskPriority::Invalidation {
456 priority: parent_priority,
457 } = parent_priority
458 && priority.0 < parent_priority.0
459 {
460 Self::Invalidation {
461 priority: Reverse(parent_priority.0.saturating_add(1)),
462 }
463 } else {
464 *self
465 }
466 }
467 TaskPriority::Recomputation => TaskPriority::Recomputation,
468 }
469 }
470}
471
472impl Display for TaskPriority {
473 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474 match self {
475 TaskPriority::Initial => write!(f, "initial"),
476 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
477 TaskPriority::Recomputation => write!(f, "recomputation"),
478 }
479 }
480}
481
482enum ScheduledTask {
483 Task {
484 task_id: TaskId,
485 span: Span,
486 },
487 LocalTask {
488 ty: LocalTaskSpec,
489 persistence: TaskPersistence,
490 execution_id: ExecutionId,
491 local_task_id: LocalTaskId,
492 global_task_state: CurrentTaskStateHandle,
493 span: Span,
494 },
495}
496
497#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
501pub enum ScheduleKey {
502 Task(TaskId),
504 LocalTask(ExecutionId, LocalTaskId),
506}
507
508impl Claimable for ScheduledTask {
509 type Key = ScheduleKey;
510
511 fn claim_key(&self) -> Option<ScheduleKey> {
512 Some(match self {
513 ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
514 ScheduledTask::LocalTask {
515 execution_id,
516 local_task_id,
517 ..
518 } => ScheduleKey::LocalTask(*execution_id, *local_task_id),
519 })
520 }
521}
522
523#[cfg(feature = "inline_execution_stats")]
524use std::sync::atomic::AtomicU64;
525
526#[cfg(feature = "inline_execution_stats")]
529#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
530pub struct InlineExecutionStats {
531 pub queued: u64,
533 pub claim_attempted: u64,
535 pub claim_completed: u64,
537 pub claim_yielded: u64,
539 pub claim_failed: u64,
541 pub waited_in_progress: u64,
543}
544
545#[derive(Default)]
551struct InlineExecutionCounters {
552 #[cfg(feature = "inline_execution_stats")]
553 claim_attempted: AtomicU64,
554 #[cfg(feature = "inline_execution_stats")]
555 claim_completed: AtomicU64,
556 #[cfg(feature = "inline_execution_stats")]
557 claim_yielded: AtomicU64,
558 #[cfg(feature = "inline_execution_stats")]
559 claim_failed: AtomicU64,
560 #[cfg(feature = "inline_execution_stats")]
561 waited_in_progress: AtomicU64,
562}
563
564impl InlineExecutionCounters {
565 #[inline]
567 fn claim_attempted(&self) {
568 #[cfg(feature = "inline_execution_stats")]
569 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
570 }
571
572 #[inline]
574 fn claim_completed(&self) {
575 #[cfg(feature = "inline_execution_stats")]
576 self.claim_completed.fetch_add(1, Ordering::Relaxed);
577 }
578
579 #[inline]
581 fn claim_yielded(&self) {
582 #[cfg(feature = "inline_execution_stats")]
583 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
584 }
585
586 #[inline]
588 fn claim_failed(&self) {
589 #[cfg(feature = "inline_execution_stats")]
590 self.claim_failed.fetch_add(1, Ordering::Relaxed);
591 }
592
593 #[cfg(feature = "inline_execution_stats")]
595 #[inline]
596 fn waited_in_progress(&self) {
597 self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
598 }
599}
600
601#[cfg(feature = "inline_execution_stats")]
603pub(crate) fn inline_stats_requested() -> bool {
604 static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
605 std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
606 });
607 *REQUESTED
608}
609
610const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
613
614thread_local! {
615 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
617}
618
619fn inline_execution_allowed() -> bool {
621 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
622}
623
624struct InlineExecutionDepthGuard;
626
627impl InlineExecutionDepthGuard {
628 fn enter() -> Self {
629 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
630 Self
631 }
632}
633
634impl Drop for InlineExecutionDepthGuard {
635 fn drop(&mut self) {
636 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
637 }
638}
639
640fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
643 let _depth_guard = InlineExecutionDepthGuard::enter();
644 let span_slot = InlineExecutionSpanSlot::default();
645 let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
646 match future
650 .as_mut()
651 .poll(&mut Context::from_waker(Waker::noop()))
652 {
653 Poll::Ready(()) => {
654 span_slot.record("complete");
655 true
656 }
657 Poll::Pending => {
658 span_slot.record("partial");
659 tokio::task::spawn(future);
660 false
661 }
662 }
663}
664
665pub(crate) fn execute_read_target_inline(
667 turbo_tasks: &dyn TurboTasksApi,
668 key: ScheduleKey,
669) -> bool {
670 if !inline_execution_allowed() {
671 return false;
673 }
674 turbo_tasks.try_execute_scheduled_task_inline(key)
675}
676
677pub struct TurboTasks<B: Backend + 'static> {
678 this: Weak<Self>,
679 backend: B,
680 execution_id_factory: IdFactory<ExecutionId>,
681 stopped: AtomicBool,
682 currently_scheduled_foreground_jobs: AtomicUsize,
683 currently_scheduled_background_jobs: AtomicUsize,
684 scheduled_tasks: AtomicUsize,
685 inline_counters: InlineExecutionCounters,
688 priority_runner:
689 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
690 start: Mutex<Option<Instant>>,
691 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
692 event_foreground_start: Event,
694 event_foreground_done: Event,
697 event_background_done: Event,
699 compilation_events: CompilationEventQueue,
700}
701
702struct CurrentTaskState {
711 task_id: Option<TaskId>,
712 execution_id: ExecutionId,
713 priority: TaskPriority,
714
715 #[cfg(feature = "verify_determinism")]
718 stateful: bool,
719
720 has_invalidator: bool,
722
723 in_top_level_task: bool,
726
727 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
732
733 local_tasks: LocalTaskTracker,
736}
737
738impl CurrentTaskState {
739 fn new(
740 task_id: TaskId,
741 execution_id: ExecutionId,
742 priority: TaskPriority,
743 in_top_level_task: bool,
744 ) -> Self {
745 Self {
746 task_id: Some(task_id),
747 execution_id,
748 priority,
749 #[cfg(feature = "verify_determinism")]
750 stateful: false,
751 has_invalidator: false,
752 in_top_level_task,
753 cell_counters: Some(AutoMap::default()),
754 local_tasks: LocalTaskTracker::new(),
755 }
756 }
757
758 fn new_temporary(
759 execution_id: ExecutionId,
760 priority: TaskPriority,
761 in_top_level_task: bool,
762 ) -> Self {
763 Self {
764 task_id: None,
765 execution_id,
766 priority,
767 #[cfg(feature = "verify_determinism")]
768 stateful: false,
769 has_invalidator: false,
770 in_top_level_task,
771 cell_counters: None,
772 local_tasks: LocalTaskTracker::new(),
773 }
774 }
775
776 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
777 if self.execution_id != expected_execution_id {
778 panic!(
779 "Local tasks can only be scheduled/awaited within the same execution of the \
780 parent task that created them"
781 );
782 }
783 }
784}
785
786#[derive(Clone)]
790struct CurrentTaskStateHandle {
791 inner: Arc<CurrentTaskStateInner>,
792}
793
794struct CurrentTaskStateInner {
795 current_task_id: Option<TaskId>,
796 state: RwLock<CurrentTaskState>,
797}
798
799impl CurrentTaskStateHandle {
800 fn new(state: CurrentTaskState) -> Self {
801 Self {
802 inner: Arc::new(CurrentTaskStateInner {
803 current_task_id: state.task_id,
804 state: RwLock::new(state),
805 }),
806 }
807 }
808
809 fn current_task_id(&self) -> Option<TaskId> {
810 self.inner.current_task_id
811 }
812}
813
814impl Deref for CurrentTaskStateHandle {
815 type Target = RwLock<CurrentTaskState>;
816
817 fn deref(&self) -> &Self::Target {
818 &self.inner.state
819 }
820}
821
822task_local! {
824 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
826
827 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
828
829 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
834
835 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
838}
839
840#[derive(Clone, Default)]
851struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
852
853impl InlineExecutionSpanSlot {
854 fn set(span: &Span) {
856 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
857 *slot.0.lock().unwrap() = Some(span.clone());
858 });
859 }
860
861 fn record(&self, outcome: &'static str) {
865 if let Some(span) = self.0.lock().unwrap().as_ref() {
866 span.record("inline_execution", outcome);
867 }
868 }
869}
870
871impl<B: Backend + 'static> TurboTasks<B> {
872 pub fn new(backend: B) -> Arc<Self> {
878 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
879 let this = Arc::new_cyclic(|this| Self {
880 this: this.clone(),
881 backend,
882 execution_id_factory,
883 stopped: AtomicBool::new(false),
884 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
885 currently_scheduled_background_jobs: AtomicUsize::new(0),
886 scheduled_tasks: AtomicUsize::new(0),
887 inline_counters: InlineExecutionCounters::default(),
888 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
889 start: Default::default(),
890 aggregated_update: Default::default(),
891 event_foreground_done: Event::new(|| {
892 || "TurboTasks::event_foreground_done".to_string()
893 }),
894 event_foreground_start: Event::new(|| {
895 || "TurboTasks::event_foreground_start".to_string()
896 }),
897 event_background_done: Event::new(|| {
898 || "TurboTasks::event_background_done".to_string()
899 }),
900 compilation_events: CompilationEventQueue::default(),
901 });
902 this.backend.startup(&*this);
903 this
904 }
905
906 pub fn pin(&self) -> Arc<Self> {
907 self.this.upgrade().unwrap()
908 }
909
910 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
912 where
913 T: ?Sized,
914 F: Fn() -> Fut + Send + Sync + Clone + 'static,
915 Fut: Future<Output = Result<Vc<T>>> + Send,
916 {
917 let id = self.backend.create_transient_task(
918 TransientTaskType::Root(Box::new(move || {
919 let functor = functor.clone();
920 Box::pin(async move {
921 mark_top_level_task();
922 let raw_vc = functor().await?.node;
923 raw_vc.to_non_local().await
924 })
925 })),
926 self,
927 );
928 self.schedule(id, TaskPriority::initial());
929 id
930 }
931
932 pub fn dispose_root_task(&self, task_id: TaskId) {
933 self.backend.dispose_root_task(task_id, self);
934 }
935
936 pub fn pin_task_for_gc(&self, task_id: TaskId) {
940 self.backend.pin_task_for_gc(task_id, self);
941 }
942
943 pub fn unpin_task_for_gc(&self, task_id: TaskId) {
945 self.backend.unpin_task_for_gc(task_id, self);
946 }
947
948 #[track_caller]
952 fn spawn_once_task<T, Fut>(&self, future: Fut)
953 where
954 T: ?Sized,
955 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
956 {
957 let id = self.backend.create_transient_task(
958 TransientTaskType::Once(Box::pin(async move {
959 mark_top_level_task();
960 let raw_vc = future.await?.node;
961 raw_vc.to_non_local().await
962 })),
963 self,
964 );
965 self.schedule(id, TaskPriority::initial());
966 }
967
968 pub async fn run_once<T: TraceRawVcs + Send + 'static>(
969 &self,
970 future: impl Future<Output = Result<T>> + Send + 'static,
971 ) -> Result<T> {
972 let (tx, rx) = tokio::sync::oneshot::channel();
973 self.spawn_once_task(async move {
974 mark_top_level_task();
975 let result = future.await;
976 tx.send(result)
977 .map_err(|_| anyhow!("unable to send result"))?;
978 Ok(Completion::new())
979 });
980
981 rx.await?
982 }
983
984 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
985 pub async fn run<T: TraceRawVcs + Send + 'static>(
986 &self,
987 future: impl Future<Output = Result<T>> + Send + 'static,
988 ) -> Result<T, TurboTasksExecutionError> {
989 self.begin_foreground_job();
990 let execution_id = self.execution_id_factory.wrapping_get();
992 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
993 execution_id,
994 TaskPriority::initial(),
995 true, ));
997
998 let result = TURBO_TASKS
999 .scope(
1000 self.pin(),
1001 CURRENT_TASK_STATE.scope(current_task_state, async {
1002 let result = CaptureFuture::new(future).await;
1003
1004 wait_for_local_tasks().await;
1006
1007 match result {
1008 Ok(Ok(value)) => Ok(value),
1009 Ok(Err(err)) => Err(err.into()),
1010 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1011 }
1012 }),
1013 )
1014 .await;
1015 self.finish_foreground_job();
1016 result
1017 }
1018
1019 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1020 let this = self.pin();
1021 tokio::spawn(async move {
1022 this.pin()
1023 .run_once(async move {
1024 this.finish_foreground_job();
1025 future.await;
1026 this.begin_foreground_job();
1027 Ok(())
1028 })
1029 .await
1030 .unwrap()
1031 });
1032 }
1033
1034 pub(crate) fn native_call(
1035 &self,
1036 native_fn: &'static NativeFunction,
1037 this: Option<RawVc>,
1038 arg: &mut dyn DynTaskInputsStorage,
1039 persistence: TaskPersistence,
1040 ) -> RawVc {
1041 RawVc::task_output(self.backend.get_or_create_task(
1042 native_fn,
1043 this,
1044 arg,
1045 current_task_if_available("turbo_function calls"),
1046 persistence,
1047 self,
1048 ))
1049 }
1050
1051 pub fn dynamic_call(
1052 &self,
1053 native_fn: &'static NativeFunction,
1054 this: Option<RawVc>,
1055 arg: &mut dyn DynTaskInputsStorage,
1056 inputs_resolved: InputResolution,
1057 persistence: TaskPersistence,
1058 ) -> RawVc {
1059 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1060 return self.native_call(native_fn, this, arg, persistence);
1061 }
1062 let arg = arg.take_box();
1064 let task_type = LocalTaskSpec {
1065 task_type: LocalTaskType::ResolveNative { native_fn },
1066 this,
1067 arg,
1068 };
1069 self.schedule_local_task(task_type, persistence)
1070 }
1071
1072 pub fn trait_call(
1073 &self,
1074 trait_method: &'static TraitMethod,
1075 this: RawVc,
1076 arg: &mut dyn DynTaskInputsStorage,
1077 inputs_resolved: InputResolution,
1078 persistence: TaskPersistence,
1079 ) -> RawVc {
1080 if let Some((_, cell_id)) = this.as_task_cell() {
1084 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1085 Some(native_fn) => {
1086 if let Some(filter) = native_fn.arg_meta.filter_owned {
1087 let (resolved, mut arg) = (filter)(arg);
1088 return self.dynamic_call(
1089 native_fn,
1090 Some(this),
1091 &mut arg,
1092 resolved,
1093 persistence,
1094 );
1095 } else {
1096 return self.dynamic_call(
1097 native_fn,
1098 Some(this),
1099 arg,
1100 inputs_resolved,
1101 persistence,
1102 );
1103 }
1104 }
1105 None => {
1106 }
1110 }
1111 }
1112
1113 let task_type = LocalTaskSpec {
1115 task_type: LocalTaskType::ResolveTrait { trait_method },
1116 this: Some(this),
1117 arg: arg.take_box(),
1118 };
1119
1120 self.schedule_local_task(task_type, persistence)
1121 }
1122
1123 #[track_caller]
1124 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1125 self.begin_foreground_job();
1126 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1127
1128 let task = ScheduledTask::Task {
1129 task_id,
1130 span: Span::current(),
1131 };
1132 self.priority_runner.schedule(&self.pin(), task, priority);
1133 }
1134
1135 fn schedule_local_task(
1136 &self,
1137 ty: LocalTaskSpec,
1138 persistence: TaskPersistence,
1140 ) -> RawVc {
1141 let task_type = ty.task_type;
1142 let (global_task_state, execution_id, priority, local_task_id) =
1143 CURRENT_TASK_STATE.with(|gts| {
1144 let mut gts_write = gts.write().unwrap();
1145 let local_task_id = gts_write.local_tasks.create(task_type);
1146 (
1147 gts.clone(),
1148 gts_write.execution_id,
1149 gts_write.priority,
1150 local_task_id,
1151 )
1152 });
1153
1154 let task = ScheduledTask::LocalTask {
1155 ty,
1156 persistence,
1157 execution_id,
1158 local_task_id,
1159 global_task_state,
1160 span: Span::current(),
1161 };
1162 self.priority_runner.schedule(&self.pin(), task, priority);
1163
1164 RawVc::local_output(execution_id, local_task_id, persistence)
1165 }
1166
1167 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1169 let this = self.pin();
1170 self.inline_counters.claim_attempted();
1171 if let Some(future) = self.priority_runner.claim(&this, &key) {
1172 let completed = poll_once_or_spawn(future);
1173 if completed {
1174 self.inline_counters.claim_completed();
1175 } else {
1176 self.inline_counters.claim_yielded();
1177 }
1178 return completed;
1179 }
1180 self.inline_counters.claim_failed();
1181 false
1182 }
1183
1184 #[cfg(feature = "inline_execution_stats")]
1185 fn note_waited_for_in_progress_task(&self) {
1186 self.inline_counters.waited_in_progress();
1187 }
1188
1189 fn begin_foreground_job(&self) {
1190 if self
1191 .currently_scheduled_foreground_jobs
1192 .fetch_add(1, Ordering::AcqRel)
1193 == 0
1194 {
1195 *self.start.lock().unwrap() = Some(Instant::now());
1196 self.event_foreground_start.notify(usize::MAX);
1197 self.backend.idle_end(self);
1198 }
1199 }
1200
1201 fn finish_foreground_job(&self) {
1202 if self
1203 .currently_scheduled_foreground_jobs
1204 .fetch_sub(1, Ordering::AcqRel)
1205 == 1
1206 {
1207 self.backend.idle_start(self);
1208 let total = self.scheduled_tasks.load(Ordering::Acquire);
1211 self.scheduled_tasks.store(0, Ordering::Release);
1212 if let Some(start) = *self.start.lock().unwrap() {
1213 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1214 if let Some(update) = update.as_mut() {
1215 update.0 += start.elapsed();
1216 update.1 += total;
1217 } else {
1218 *update = Some((start.elapsed(), total));
1219 }
1220 }
1221 self.event_foreground_done.notify(usize::MAX);
1222 }
1223 }
1224
1225 fn begin_background_job(&self) {
1226 self.currently_scheduled_background_jobs
1227 .fetch_add(1, Ordering::Relaxed);
1228 }
1229
1230 fn finish_background_job(&self) {
1231 if self
1232 .currently_scheduled_background_jobs
1233 .fetch_sub(1, Ordering::Relaxed)
1234 == 1
1235 {
1236 self.event_background_done.notify(usize::MAX);
1237 }
1238 }
1239
1240 pub fn get_in_progress_count(&self) -> usize {
1241 self.currently_scheduled_foreground_jobs
1242 .load(Ordering::Acquire)
1243 }
1244
1245 #[cfg(feature = "inline_execution_stats")]
1248 #[doc(hidden)]
1249 pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1250 let counters = &self.inline_counters;
1251 InlineExecutionStats {
1252 queued: self.priority_runner.total_queued(),
1253 claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1254 claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1255 claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1256 claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1257 waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1258 }
1259 }
1260
1261 pub async fn wait_task_completion(
1273 &self,
1274 id: TaskId,
1275 consistency: ReadConsistency,
1276 ) -> Result<()> {
1277 read_task_output(
1278 self,
1279 id,
1280 ReadOutputOptions {
1281 tracking: ReadTracking::Untracked,
1283 consistency,
1284 },
1285 )
1286 .await?;
1287 Ok(())
1288 }
1289
1290 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1293 self.aggregated_update_info(aggregation, Duration::MAX)
1294 .await
1295 .unwrap()
1296 }
1297
1298 pub async fn aggregated_update_info(
1302 &self,
1303 aggregation: Duration,
1304 timeout: Duration,
1305 ) -> Option<UpdateInfo> {
1306 let listener = self
1307 .event_foreground_done
1308 .listen_with_note(|| || "wait for update info".to_string());
1309 let wait_for_finish = {
1310 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1311 if aggregation.is_zero() {
1312 if let Some((duration, tasks)) = update.take() {
1313 return Some(UpdateInfo {
1314 duration,
1315 tasks,
1316 reasons: take(reason_set),
1317 placeholder_for_future_fields: (),
1318 });
1319 } else {
1320 true
1321 }
1322 } else {
1323 update.is_none()
1324 }
1325 };
1326 if wait_for_finish {
1327 if timeout == Duration::MAX {
1328 listener.await;
1330 } else {
1331 let start_listener = self
1333 .event_foreground_start
1334 .listen_with_note(|| || "wait for update info".to_string());
1335 if self
1336 .currently_scheduled_foreground_jobs
1337 .load(Ordering::Acquire)
1338 == 0
1339 {
1340 start_listener.await;
1341 } else {
1342 drop(start_listener);
1343 }
1344 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1345 return None;
1347 }
1348 }
1349 }
1350 if !aggregation.is_zero() {
1351 loop {
1352 select! {
1353 () = tokio::time::sleep(aggregation) => {
1354 break;
1355 }
1356 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1357 }
1359 }
1360 }
1361 }
1362 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1363 if let Some((duration, tasks)) = update.take() {
1364 Some(UpdateInfo {
1365 duration,
1366 tasks,
1367 reasons: take(reason_set),
1368 placeholder_for_future_fields: (),
1369 })
1370 } else {
1371 panic!("aggregated_update_info must not called concurrently")
1372 }
1373 }
1374
1375 pub async fn wait_background_done(&self) {
1376 let listener = self.event_background_done.listen();
1377 if self
1378 .currently_scheduled_background_jobs
1379 .load(Ordering::Acquire)
1380 != 0
1381 {
1382 listener.await;
1383 }
1384 }
1385
1386 pub async fn stop_and_wait(&self) {
1387 #[cfg(feature = "inline_execution_stats")]
1388 if inline_stats_requested() {
1389 eprintln!(
1392 "turbo-tasks inline execution stats: {:#?}",
1393 self.inline_execution_stats()
1394 );
1395 }
1396 turbo_tasks_future_scope(self.pin(), async move {
1397 self.backend.stopping(self);
1398 self.stopped.store(true, Ordering::Release);
1399 {
1400 let listener = self
1401 .event_foreground_done
1402 .listen_with_note(|| || "wait for stop".to_string());
1403 if self
1404 .currently_scheduled_foreground_jobs
1405 .load(Ordering::Acquire)
1406 != 0
1407 {
1408 listener.await;
1409 }
1410 }
1411 {
1412 let listener = self.event_background_done.listen();
1413 if self
1414 .currently_scheduled_background_jobs
1415 .load(Ordering::Acquire)
1416 != 0
1417 {
1418 listener.await;
1419 }
1420 }
1421 self.backend.stop(self);
1422 })
1423 .await;
1424 }
1425
1426 #[track_caller]
1427 pub(crate) fn schedule_background_job<T>(&self, func: T)
1428 where
1429 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1430 T::CallOnceFuture: Send,
1431 {
1432 let mut this = self.pin();
1433 self.begin_background_job();
1434 tokio::spawn(
1435 TURBO_TASKS
1436 .scope(this.clone(), async move {
1437 if !this.stopped.load(Ordering::Acquire) {
1438 this = func(this).await;
1439 }
1440 this.finish_background_job();
1441 })
1442 .in_current_span(),
1443 );
1444 }
1445
1446 fn finish_current_task_state(&self) -> FinishedTaskState {
1447 CURRENT_TASK_STATE.with(|cell| {
1448 let current_task_state = &*cell.write().unwrap();
1449 FinishedTaskState {
1450 #[cfg(feature = "verify_determinism")]
1451 stateful: current_task_state.stateful,
1452 has_invalidator: current_task_state.has_invalidator,
1453 }
1454 })
1455 }
1456
1457 pub fn backend(&self) -> &B {
1458 &self.backend
1459 }
1460
1461 pub fn get_current_task_priority(&self) -> TaskPriority {
1462 CURRENT_TASK_STATE
1463 .try_with(|task_state| task_state.read().unwrap().priority)
1464 .unwrap_or(TaskPriority::initial())
1465 }
1466
1467 pub fn is_idle(&self) -> bool {
1468 self.currently_scheduled_foreground_jobs
1469 .load(Ordering::Acquire)
1470 == 0
1471 }
1472
1473 #[track_caller]
1474 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1475 self.schedule_background_job(async move |this| {
1476 this.backend.run_backend_job(job, &*this).await;
1477 this
1478 })
1479 }
1480}
1481
1482struct TurboTasksExecutor;
1483
1484async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1489 match AssertUnwindSafe(f).catch_unwind().await {
1490 Ok(r) => r,
1491 Err(_) => {
1492 eprintln!(
1493 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1494 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1495 https://github.com/vercel/next.js/discussions and include the panic message \
1496 and stack trace above.\n\nAborting."
1497 );
1498 abort();
1499 }
1500 }
1501}
1502
1503impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1504 type Future = impl Future<Output = ()> + Send + 'static;
1505
1506 fn execute(
1507 &self,
1508 this: &Arc<TurboTasks<B>>,
1509 scheduled_task: ScheduledTask,
1510 priority: TaskPriority,
1511 ) -> Self::Future {
1512 match scheduled_task {
1513 ScheduledTask::Task { task_id, span } => {
1514 let this2 = this.clone();
1515 let this = this.clone();
1516 let future = async move {
1517 abort_on_panic(async {
1518 let execution_id = this.execution_id_factory.wrapping_get();
1521 let current_task_state =
1522 CurrentTaskStateHandle::new(CurrentTaskState::new(
1523 task_id,
1524 execution_id,
1525 priority,
1526 false, ));
1528 let single_execution_future = async {
1529 if this.stopped.load(Ordering::Acquire) {
1530 this.backend.task_execution_canceled(task_id, &*this);
1531 return None;
1532 }
1533
1534 let TaskExecutionSpec { future, span } = this
1535 .backend
1536 .try_start_task_execution(task_id, priority, &*this)?;
1537
1538 InlineExecutionSpanSlot::set(&span);
1541
1542 async {
1543 let result = CaptureFuture::new(future).await;
1544
1545 wait_for_local_tasks().await;
1547
1548 let result = match result {
1549 Ok(Ok(raw_vc)) => {
1550 raw_vc
1553 .to_non_local_unchecked_sync(&*this)
1554 .map_err(|err| err.into())
1555 }
1556 Ok(Err(err)) => Err(err.into()),
1557 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1558 };
1559
1560 let finished_state = this.finish_current_task_state();
1561 let cell_counters = CURRENT_TASK_STATE
1562 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1563 this.backend.task_execution_completed(
1564 task_id,
1565 result,
1566 &cell_counters,
1567 #[cfg(feature = "verify_determinism")]
1568 finished_state.stateful,
1569 finished_state.has_invalidator,
1570 &*this,
1571 )
1572 }
1573 .instrument(span)
1574 .await
1575 };
1576 if let Some(stale_priority) = CURRENT_TASK_STATE
1577 .scope(current_task_state, single_execution_future)
1578 .await
1579 {
1580 this.schedule(task_id, stale_priority);
1583 }
1584 this.finish_foreground_job();
1585 })
1586 .await
1587 };
1588
1589 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1590 }
1591 ScheduledTask::LocalTask {
1592 ty,
1593 persistence,
1594 execution_id: _,
1595 local_task_id,
1596 global_task_state,
1597 span,
1598 } => {
1599 let this2 = this.clone();
1600 let this = this.clone();
1601 let task_type = ty.task_type;
1602 let future = async move {
1603 let span = match &ty.task_type {
1604 LocalTaskType::ResolveNative { native_fn } => {
1605 native_fn.resolve_span(priority)
1606 }
1607 LocalTaskType::ResolveTrait { trait_method } => {
1608 trait_method.resolve_span(priority)
1609 }
1610 };
1611 InlineExecutionSpanSlot::set(&span);
1614 abort_on_panic(
1615 async move {
1616 let result = match ty.task_type {
1617 LocalTaskType::ResolveNative { native_fn } => {
1618 LocalTaskType::run_resolve_native(
1619 native_fn,
1620 ty.this,
1621 &*ty.arg,
1622 persistence,
1623 this,
1624 )
1625 .await
1626 }
1627 LocalTaskType::ResolveTrait { trait_method } => {
1628 LocalTaskType::run_resolve_trait(
1629 trait_method,
1630 ty.this.unwrap(),
1631 &*ty.arg,
1632 persistence,
1633 this,
1634 )
1635 .await
1636 }
1637 };
1638
1639 let output = match result {
1640 Ok(raw_vc) => OutputContent::Link(raw_vc),
1641 Err(err) => OutputContent::Error(
1642 TurboTasksExecutionError::from(err)
1643 .with_local_task_context(task_type.to_string()),
1644 ),
1645 };
1646
1647 CURRENT_TASK_STATE.with(move |gts| {
1648 gts.write()
1649 .unwrap()
1650 .local_tasks
1651 .complete(local_task_id, output);
1652 });
1653 }
1654 .instrument(span),
1655 )
1656 .await
1657 };
1658 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1659
1660 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1661 }
1662 }
1663 }
1664}
1665
1666struct FinishedTaskState {
1667 #[cfg(feature = "verify_determinism")]
1670 stateful: bool,
1671
1672 has_invalidator: bool,
1674}
1675
1676impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1677 fn dynamic_call(
1678 &self,
1679 native_fn: &'static NativeFunction,
1680 this: Option<RawVc>,
1681 arg: &mut dyn DynTaskInputsStorage,
1682 inputs_resolved: InputResolution,
1683 persistence: TaskPersistence,
1684 ) -> RawVc {
1685 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1686 }
1687 fn native_call(
1688 &self,
1689 native_fn: &'static NativeFunction,
1690 this: Option<RawVc>,
1691 arg: &mut dyn DynTaskInputsStorage,
1692 persistence: TaskPersistence,
1693 ) -> RawVc {
1694 self.native_call(native_fn, this, arg, persistence)
1695 }
1696 fn trait_call(
1697 &self,
1698 trait_method: &'static TraitMethod,
1699 this: RawVc,
1700 arg: &mut dyn DynTaskInputsStorage,
1701 inputs_resolved: InputResolution,
1702 persistence: TaskPersistence,
1703 ) -> RawVc {
1704 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1705 }
1706
1707 #[track_caller]
1708 fn run(
1709 &self,
1710 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1711 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1712 let this = self.pin();
1713 Box::pin(async move { this.run(future).await })
1714 }
1715
1716 #[track_caller]
1717 fn run_once(
1718 &self,
1719 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1720 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1721 let this = self.pin();
1722 Box::pin(async move { this.run_once(future).await })
1723 }
1724
1725 #[track_caller]
1726 fn run_once_with_reason(
1727 &self,
1728 reason: StaticOrArc<dyn InvalidationReason>,
1729 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1730 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1731 {
1732 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1733 reason_set.insert(reason);
1734 }
1735 let this = self.pin();
1736 Box::pin(async move { this.run_once(future).await })
1737 }
1738
1739 #[track_caller]
1740 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1741 self.start_once_process(future)
1742 }
1743
1744 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1745 if let Err(e) = self.compilation_events.send(event) {
1746 tracing::warn!("Failed to send compilation event: {e}");
1747 }
1748 }
1749
1750 fn get_task_name(&self, task: TaskId) -> String {
1751 self.backend.get_task_name(task, self)
1752 }
1753}
1754
1755impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1756 #[instrument(level = "info", skip_all, name = "invalidate")]
1757 fn invalidate(&self, task: TaskId) {
1758 self.backend.invalidate_task(task, self);
1759 }
1760
1761 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1762 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1763 {
1764 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1765 reason_set.insert(reason);
1766 }
1767 self.backend.invalidate_task(task, self);
1768 }
1769
1770 fn invalidate_serialization(&self, task: TaskId) {
1771 self.backend.invalidate_serialization(task, self);
1772 }
1773
1774 #[track_caller]
1775 fn try_read_task_output(
1776 &self,
1777 task: TaskId,
1778 options: ReadOutputOptions,
1779 ) -> Result<ReadOutcome<RawVc>> {
1780 if options.consistency == ReadConsistency::Eventual {
1781 debug_assert_not_in_top_level_task("read_task_output");
1782 }
1783 self.backend.try_read_task_output(
1784 task,
1785 current_task_if_available("reading Vcs"),
1786 options,
1787 self,
1788 )
1789 }
1790
1791 #[track_caller]
1792 fn try_read_task_cell(
1793 &self,
1794 task: TaskId,
1795 index: CellId,
1796 options: ReadCellOptions,
1797 ) -> Result<ReadOutcome<TypedCellContent>> {
1798 let reader = current_task_if_available("reading Vcs");
1799 self.backend
1800 .try_read_task_cell(task, index, reader, options, self)
1801 }
1802
1803 fn try_read_own_task_cell(
1804 &self,
1805 current_task: TaskId,
1806 index: CellId,
1807 ) -> Result<TypedCellContent> {
1808 self.backend
1809 .try_read_own_task_cell(current_task, index, self)
1810 }
1811
1812 #[track_caller]
1813 fn try_read_local_output(
1814 &self,
1815 execution_id: ExecutionId,
1816 local_task_id: LocalTaskId,
1817 ) -> Result<Result<RawVc, EventListener>> {
1818 debug_assert_not_in_top_level_task("read_local_output");
1819 CURRENT_TASK_STATE.with(|gts| {
1820 let gts_read = gts.read().unwrap();
1821
1822 gts_read.assert_execution_id(execution_id);
1827
1828 match gts_read.local_tasks.get(local_task_id) {
1829 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1830 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1831 }
1832 })
1833 }
1834
1835 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1836 self.backend.read_task_collectibles(
1839 task,
1840 trait_id,
1841 current_task_if_available("reading collectibles"),
1842 self,
1843 )
1844 }
1845
1846 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1847 self.try_execute_scheduled_task_inline(key)
1848 }
1849
1850 #[cfg(feature = "inline_execution_stats")]
1851 fn note_waited_for_in_progress_task(&self) {
1852 self.note_waited_for_in_progress_task()
1853 }
1854
1855 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1856 self.backend.emit_collectible(
1857 trait_type,
1858 collectible,
1859 current_task("emitting collectible"),
1860 self,
1861 );
1862 }
1863
1864 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1865 self.backend.unemit_collectible(
1866 trait_type,
1867 collectible,
1868 count,
1869 current_task("emitting collectible"),
1870 self,
1871 );
1872 }
1873
1874 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1875 for (&collectible, &count) in collectibles {
1876 if count > 0 {
1877 self.backend.unemit_collectible(
1878 trait_type,
1879 collectible,
1880 count as u32,
1881 current_task("emitting collectible"),
1882 self,
1883 );
1884 }
1885 }
1886 }
1887
1888 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1889 self.try_read_own_task_cell(task, index)
1890 }
1891
1892 fn update_own_task_cell(
1893 &self,
1894 task: TaskId,
1895 index: CellId,
1896 content: CellContent,
1897 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1898 content_hash: Option<CellHash>,
1899 verification_mode: VerificationMode,
1900 ) {
1901 self.backend.update_task_cell(
1902 task,
1903 index,
1904 content,
1905 updated_key_hashes,
1906 content_hash,
1907 verification_mode,
1908 self,
1909 );
1910 }
1911
1912 fn connect_task(&self, task: TaskId) {
1913 self.backend
1914 .connect_task(task, current_task_if_available("connecting task"), self);
1915 }
1916
1917 fn mark_own_task_as_finished(&self, task: TaskId) {
1918 self.backend.mark_own_task_as_finished(task, self);
1919 }
1920
1921 fn pin_task_for_gc(&self, task: TaskId) {
1922 self.backend.pin_task_for_gc(task, self);
1923 }
1924
1925 fn unpin_task_for_gc(&self, task: TaskId) {
1926 self.backend.unpin_task_for_gc(task, self);
1927 }
1928
1929 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1932 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1935 global_task_state
1936 .write()
1937 .unwrap()
1938 .local_tasks
1939 .register_detached();
1940 let wrapped = async move {
1941 struct DropGuard;
1943 impl Drop for DropGuard {
1944 fn drop(&mut self) {
1945 CURRENT_TASK_STATE
1946 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1947 }
1948 }
1949 let _guard = DropGuard;
1950 fut.await;
1951 };
1952 tokio::spawn(TURBO_TASKS.scope(
1953 turbo_tasks(),
1954 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1955 ));
1956 }
1957
1958 fn task_statistics(&self) -> &TaskStatisticsApi {
1959 self.backend.task_statistics()
1960 }
1961
1962 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1963 let this = self.pin();
1964 Box::pin(async move {
1965 this.stop_and_wait().await;
1966 })
1967 }
1968
1969 fn subscribe_to_compilation_events(
1970 &self,
1971 event_types: Option<Vec<String>>,
1972 ) -> Receiver<Arc<dyn CompilationEvent>> {
1973 self.compilation_events.subscribe(event_types)
1974 }
1975
1976 fn is_tracking_dependencies(&self) -> bool {
1977 self.backend.is_tracking_dependencies()
1978 }
1979}
1980
1981async fn wait_for_local_tasks() {
1982 let listener =
1983 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
1984 let Some(listener) = listener else {
1985 return;
1986 };
1987 listener.await;
1988}
1989
1990pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
1991 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1992 Ok(id) => id,
1993 Err(_) => panic!(
1994 "{from} can only be used in the context of a turbo_tasks task execution or \
1995 turbo_tasks run"
1996 ),
1997 }
1998}
1999
2000pub(crate) fn current_task(from: &str) -> TaskId {
2001 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2002 Ok(Some(id)) => id,
2003 Ok(None) | Err(_) => {
2004 panic!("{from} can only be used in the context of a turbo_tasks task execution")
2005 }
2006 }
2007}
2008
2009#[track_caller]
2012pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2013 if !cfg!(debug_assertions) {
2014 return;
2015 }
2016
2017 let in_top_level = CURRENT_TASK_STATE
2018 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2019 .unwrap_or(true);
2020 if !in_top_level {
2021 panic!("{message}");
2022 }
2023}
2024
2025#[track_caller]
2026pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2027 if !cfg!(debug_assertions) {
2028 return;
2029 }
2030
2031 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2034 .try_with(|&suppressed| suppressed)
2035 .unwrap_or(false);
2036 if suppressed {
2037 return;
2038 }
2039
2040 let in_top_level = CURRENT_TASK_STATE
2041 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2042 .unwrap_or(false);
2043 if in_top_level {
2044 panic!(
2045 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2046 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2047 reads to avoid leaking inconsistent return values."
2048 );
2049 }
2050}
2051
2052pub async fn run<T: Send + 'static>(
2053 tt: Arc<dyn TurboTasksApi>,
2054 future: impl Future<Output = Result<T>> + Send + 'static,
2055) -> Result<T> {
2056 let (tx, rx) = tokio::sync::oneshot::channel();
2057
2058 tt.run(Box::pin(async move {
2059 let result = future.await?;
2060 tx.send(result)
2061 .map_err(|_| anyhow!("unable to send result"))?;
2062 Ok(())
2063 }))
2064 .await?;
2065
2066 Ok(rx.await?)
2067}
2068
2069pub async fn run_once<T: Send + 'static>(
2070 tt: Arc<dyn TurboTasksApi>,
2071 future: impl Future<Output = Result<T>> + Send + 'static,
2072) -> Result<T> {
2073 let (tx, rx) = tokio::sync::oneshot::channel();
2074
2075 tt.run_once(Box::pin(async move {
2076 let result = future.await?;
2077 tx.send(result)
2078 .map_err(|_| anyhow!("unable to send result"))?;
2079 Ok(())
2080 }))
2081 .await?;
2082
2083 Ok(rx.await?)
2084}
2085
2086pub async fn run_once_with_reason<T: Send + 'static>(
2087 tt: Arc<dyn TurboTasksApi>,
2088 reason: impl InvalidationReason,
2089 future: impl Future<Output = Result<T>> + Send + 'static,
2090) -> Result<T> {
2091 let (tx, rx) = tokio::sync::oneshot::channel();
2092
2093 tt.run_once_with_reason(
2094 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2095 Box::pin(async move {
2096 let result = future.await?;
2097 tx.send(result)
2098 .map_err(|_| anyhow!("unable to send result"))?;
2099 Ok(())
2100 }),
2101 )
2102 .await?;
2103
2104 Ok(rx.await?)
2105}
2106
2107pub fn dynamic_call(
2109 func: &'static NativeFunction,
2110 this: Option<RawVc>,
2111 arg: &mut dyn DynTaskInputsStorage,
2112 inputs_resolved: InputResolution,
2113 persistence: TaskPersistence,
2114) -> RawVc {
2115 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2116}
2117
2118pub fn trait_call(
2120 trait_method: &'static TraitMethod,
2121 this: RawVc,
2122 arg: &mut dyn DynTaskInputsStorage,
2123 inputs_resolved: InputResolution,
2124 persistence: TaskPersistence,
2125) -> RawVc {
2126 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2127}
2128
2129pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2130 TURBO_TASKS.with(|arc| arc.clone())
2131}
2132
2133pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2134 TURBO_TASKS.with(Arc::downgrade)
2135}
2136
2137pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2138 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2139}
2140
2141pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2142 TURBO_TASKS.with(|arc| func(arc))
2143}
2144
2145pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2146 TURBO_TASKS.sync_scope(tt, f)
2147}
2148
2149pub fn turbo_tasks_future_scope<T>(
2150 tt: Arc<dyn TurboTasksApi>,
2151 f: impl Future<Output = T>,
2152) -> impl Future<Output = T> {
2153 TURBO_TASKS.scope(tt, f)
2154}
2155
2156pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2161 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2162}
2163
2164pub fn mark_finished() {
2167 with_turbo_tasks(|tt| {
2168 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2169 });
2170}
2171
2172pub fn get_serialization_invalidator() -> SerializationInvalidator {
2178 CURRENT_TASK_STATE.with(|cell| {
2179 let CurrentTaskState {
2180 task_id,
2181 #[cfg(feature = "verify_determinism")]
2182 stateful,
2183 ..
2184 } = &mut *cell.write().unwrap();
2185 #[cfg(feature = "verify_determinism")]
2186 {
2187 *stateful = true;
2188 }
2189 let Some(task_id) = *task_id else {
2190 panic!(
2191 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2192 task execution"
2193 );
2194 };
2195 SerializationInvalidator::new(task_id)
2196 })
2197}
2198
2199pub fn mark_invalidator() {
2200 CURRENT_TASK_STATE.with(|cell| {
2201 let CurrentTaskState {
2202 has_invalidator, ..
2203 } = &mut *cell.write().unwrap();
2204 *has_invalidator = true;
2205 })
2206}
2207
2208pub fn mark_stateful() {
2214 #[cfg(feature = "verify_determinism")]
2215 {
2216 CURRENT_TASK_STATE.with(|cell| {
2217 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2218 *stateful = true;
2219 })
2220 }
2221 }
2223
2224pub fn mark_top_level_task() {
2228 if cfg!(debug_assertions) {
2229 CURRENT_TASK_STATE.with(|cell| {
2230 cell.write().unwrap().in_top_level_task = true;
2231 })
2232 }
2233}
2234
2235pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2246 if cfg!(debug_assertions) {
2247 CURRENT_TASK_STATE.with(|cell| {
2248 cell.write().unwrap().in_top_level_task = false;
2249 })
2250 }
2251}
2252
2253pub fn prevent_gc() {
2261 if let Some(task) = current_task_if_available("prevent_gc") {
2262 with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2263 }
2264}
2265
2266pub struct GcRoot<T: ?Sized> {
2268 tt: Arc<dyn TurboTasksApi>,
2269 vc: OperationVc<T>,
2270}
2271
2272impl<T: ?Sized> GcRoot<T> {
2273 pub fn pin(tt: Arc<dyn TurboTasksApi>, vc: OperationVc<T>) -> Self {
2275 tt.pin_task_for_gc(vc.task_id());
2276 Self { tt, vc }
2277 }
2278}
2279
2280impl<T: ?Sized> Deref for GcRoot<T> {
2283 type Target = OperationVc<T>;
2284
2285 fn deref(&self) -> &Self::Target {
2286 &self.vc
2287 }
2288}
2289
2290impl<T: ?Sized> Clone for GcRoot<T> {
2291 fn clone(&self) -> Self {
2292 Self::pin(self.tt.clone(), self.vc)
2293 }
2294}
2295
2296impl<T: ?Sized> Drop for GcRoot<T> {
2297 fn drop(&mut self) {
2298 self.tt.unpin_task_for_gc(self.vc.task_id());
2299 }
2300}
2301
2302impl<T: ?Sized> Debug for GcRoot<T> {
2303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2304 f.debug_struct("GcRoot").field("vc", &self.vc).finish()
2305 }
2306}
2307
2308impl<T: ?Sized> PartialEq for GcRoot<T> {
2309 fn eq(&self, other: &Self) -> bool {
2312 self.vc == other.vc
2313 }
2314}
2315
2316impl<T: ?Sized> Eq for GcRoot<T> {}
2317
2318impl<T: ?Sized> Hash for GcRoot<T> {
2319 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2322 self.vc.hash(state);
2323 }
2324}
2325
2326impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2330 fn borrow(&self) -> &OperationVc<T> {
2331 &self.vc
2332 }
2333}
2334
2335impl<T: ?Sized> TraceRawVcs for GcRoot<T> {
2336 fn trace_raw_vcs(&self, trace_context: &mut crate::trace::TraceRawVcsContext) {
2337 self.vc.trace_raw_vcs(trace_context);
2338 }
2339}
2340
2341unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2344
2345unsafe impl<T: NonLocalValue + ?Sized> NonLocalValue for GcRoot<T> {}
2348
2349pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2350 with_turbo_tasks(|tt| {
2351 let raw_vc = collectible.node.node;
2352 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2353 })
2354}
2355
2356pub(crate) async fn read_task_output(
2357 this: &dyn TurboTasksApi,
2358 id: TaskId,
2359 options: ReadOutputOptions,
2360) -> Result<RawVc> {
2361 loop {
2362 match this.try_read_task_output(id, options)? {
2363 ReadOutcome::Value(result) => return Ok(result),
2364 ReadOutcome::Scheduled(listener) => {
2365 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2367 continue;
2368 }
2369 listener.await
2370 }
2371 ReadOutcome::InProgress(listener) => {
2372 #[cfg(feature = "inline_execution_stats")]
2374 this.note_waited_for_in_progress_task();
2375 listener.await
2376 }
2377 }
2378 }
2379}
2380
2381#[derive(Clone, Copy)]
2387pub struct CurrentCellRef {
2388 current_task: TaskId,
2389 index: CellId,
2390}
2391
2392type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2393
2394type CellUpdate = (
2397 SharedReference,
2398 Option<SmallVec<[u64; 2]>>,
2399 Option<CellHash>,
2400);
2401
2402type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2405
2406impl CurrentCellRef {
2407 fn conditional_update<T>(
2409 &self,
2410 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2411 ) where
2412 T: VcValueType,
2413 {
2414 let mut functor = Some(functor);
2417 self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2418 let functor = functor.take().expect("functor is called at most once");
2419 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2420 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2421 Some((
2422 SharedReference::new(triomphe::Arc::new(new_value)),
2423 updated_key_hashes,
2424 content_hash,
2425 ))
2426 })
2427 }
2428
2429 fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2436 let tt = turbo_tasks();
2437 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2438 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2439 if let Some((update, updated_key_hashes, content_hash)) = update {
2440 tt.update_own_task_cell(
2441 self.current_task,
2442 self.index,
2443 CellContent(Some(update)),
2444 updated_key_hashes,
2445 content_hash,
2446 VerificationMode::EqualityCheck,
2447 )
2448 }
2449 }
2450
2451 pub fn compare_and_update<T>(&self, new_value: T)
2486 where
2487 T: PartialEq + VcValueType,
2488 {
2489 self.conditional_update(|old_value| {
2490 if let Some(old_value) = old_value
2491 && old_value == &new_value
2492 {
2493 return None;
2494 }
2495 Some((new_value, None, None))
2496 });
2497 }
2498
2499 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2507 where
2508 T: VcValueType + PartialEq,
2509 {
2510 let mut new_shared_reference = Some(new_shared_reference);
2511 self.conditional_update_with_shared_reference(&mut |old_sr| {
2512 let new_shared_reference = new_shared_reference
2513 .take()
2514 .expect("functor is called at most once");
2515 if let Some(old_sr) = old_sr {
2516 let old_value = extract_sr_value::<T>(old_sr);
2517 let new_value = extract_sr_value::<T>(&new_shared_reference);
2518 if old_value == new_value {
2519 return None;
2520 }
2521 }
2522 Some((new_shared_reference, None, None))
2523 });
2524 }
2525
2526 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2535 where
2536 T: PartialEq + DeterministicHash + VcValueType,
2537 {
2538 self.conditional_update(|old_value| {
2539 if let Some(old_value) = old_value
2540 && old_value == &new_value
2541 {
2542 return None;
2543 }
2544 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2545
2546 Some((new_value, None, Some(content_hash)))
2547 });
2548 }
2549
2550 pub fn hashed_compare_and_update_with_shared_reference<T>(
2556 &self,
2557 new_shared_reference: SharedReference,
2558 ) where
2559 T: VcValueType + PartialEq + DeterministicHash,
2560 {
2561 let mut new_shared_reference = Some(new_shared_reference);
2562 self.conditional_update_with_shared_reference(&mut move |old_sr| {
2563 let new_shared_reference = new_shared_reference
2564 .take()
2565 .expect("functor is called at most once");
2566 if let Some(old_sr) = old_sr {
2567 let old_value = extract_sr_value::<T>(old_sr);
2568 let new_value = extract_sr_value::<T>(&new_shared_reference);
2569 if old_value == new_value {
2570 return None;
2571 }
2572 }
2573 let content_hash =
2574 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2575 Some((new_shared_reference, None, Some(content_hash)))
2576 });
2577 }
2578
2579 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2581 where
2582 T: PartialEq + VcValueType,
2583 VcReadTarget<T>: KeyedEq,
2584 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2585 {
2586 self.conditional_update(|old_value| {
2587 let Some(old_value) = old_value else {
2588 return Some((new_value, None, None));
2589 };
2590 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2591 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2592 let updated_keys = old_value.different_keys(new_value_ref);
2593 if updated_keys.is_empty() {
2594 return None;
2595 }
2596 let updated_key_hashes = updated_keys
2598 .into_iter()
2599 .map(|key| FxBuildHasher.hash_one(key))
2600 .collect();
2601 Some((new_value, Some(updated_key_hashes), None))
2602 });
2603 }
2604
2605 pub fn keyed_compare_and_update_with_shared_reference<T>(
2608 &self,
2609 new_shared_reference: SharedReference,
2610 ) where
2611 T: VcValueType + PartialEq,
2612 VcReadTarget<T>: KeyedEq,
2613 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2614 {
2615 let mut new_shared_reference = Some(new_shared_reference);
2616 self.conditional_update_with_shared_reference(&mut |old_sr| {
2617 let new_shared_reference = new_shared_reference
2618 .take()
2619 .expect("functor is called at most once");
2620 let Some(old_sr) = old_sr else {
2621 return Some((new_shared_reference, None, None));
2622 };
2623 let old_value = extract_sr_value::<T>(old_sr);
2624 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2625 let new_value = extract_sr_value::<T>(&new_shared_reference);
2626 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2627 let updated_keys = old_value.different_keys(new_value);
2628 if updated_keys.is_empty() {
2629 return None;
2630 }
2631 let updated_key_hashes = updated_keys
2633 .into_iter()
2634 .map(|key| FxBuildHasher.hash_one(key))
2635 .collect();
2636 Some((new_shared_reference, Some(updated_key_hashes), None))
2637 });
2638 }
2639
2640 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2642 where
2643 T: VcValueType,
2644 {
2645 let tt = turbo_tasks();
2646 tt.update_own_task_cell(
2647 self.current_task,
2648 self.index,
2649 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2650 None,
2651 None,
2652 verification_mode,
2653 )
2654 }
2655
2656 pub fn update_with_shared_reference(
2664 &self,
2665 shared_ref: SharedReference,
2666 verification_mode: VerificationMode,
2667 ) {
2668 let tt = turbo_tasks();
2669 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2670 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2671 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2672 shared_ref_exp != shared_ref
2674 } else {
2675 true
2676 }
2677 } else {
2678 true
2679 };
2680 if update {
2681 tt.update_own_task_cell(
2682 self.current_task,
2683 self.index,
2684 CellContent(Some(shared_ref)),
2685 None,
2686 None,
2687 verification_mode,
2688 )
2689 }
2690 }
2691}
2692
2693impl From<CurrentCellRef> for RawVc {
2694 fn from(cell: CurrentCellRef) -> Self {
2695 RawVc::task_cell(cell.current_task, cell.index)
2696 }
2697}
2698
2699fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2700 sr.0.downcast_ref::<T>()
2701 .expect("cannot update SharedReference of different type")
2702}
2703
2704pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2705 find_cell_by_id(T::get_value_type_id())
2706}
2707
2708pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2709 CURRENT_TASK_STATE.with(|ts| {
2710 let current_task = current_task("celling turbo_tasks values");
2711 let mut ts = ts.write().unwrap();
2712 let map = ts.cell_counters.as_mut().unwrap();
2713 let current_index = map.entry(ty).or_default();
2714 let index = *current_index;
2715 assert!(
2716 index <= CellId::MAX_CELL_INDEX,
2717 "task allocated more than {} cells of a single type",
2718 CellId::MAX_CELL_INDEX as u64 + 1,
2719 );
2720 *current_index += 1;
2721 CurrentCellRef {
2722 current_task,
2723 index: CellId::new(ty, index),
2724 }
2725 })
2726}
2727
2728pub(crate) async fn read_local_output(
2729 this: &dyn TurboTasksApi,
2730 execution_id: ExecutionId,
2731 local_task_id: LocalTaskId,
2732) -> Result<RawVc> {
2733 loop {
2734 match this.try_read_local_output(execution_id, local_task_id)? {
2735 Ok(raw_vc) => return Ok(raw_vc),
2736 Err(event_listener) => {
2737 if execute_read_target_inline(
2740 this,
2741 ScheduleKey::LocalTask(execution_id, local_task_id),
2742 ) {
2743 continue;
2744 }
2745 event_listener.await
2746 }
2747 }
2748 }
2749}
2750
2751#[cfg(test)]
2752mod tests {
2753 use super::*;
2754
2755 #[test]
2756 fn test_inline_execution_depth_guard_restores_depth() {
2757 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2758 {
2759 let _outer = InlineExecutionDepthGuard::enter();
2760 {
2761 let _inner = InlineExecutionDepthGuard::enter();
2762 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2763 }
2764 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2765 }
2766 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2767 }
2768
2769 #[test]
2770 fn test_inline_depth_cap() {
2771 assert!(inline_execution_allowed(), "nothing is nested yet");
2772 let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2773 .map(|_| InlineExecutionDepthGuard::enter())
2774 .collect::<Vec<_>>();
2775 assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2776 assert!(
2777 !inline_execution_allowed(),
2778 "at the nesting cap reads wait for a worker instead of executing inline"
2779 );
2780
2781 guards.pop();
2783 assert!(inline_execution_allowed());
2784 }
2785
2786 #[tokio::test]
2787 async fn test_poll_once_or_spawn_completed_execution() {
2788 assert!(
2789 poll_once_or_spawn(async {}),
2790 "a future that completes on the first poll is executed inline"
2791 );
2792 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2793 }
2794
2795 #[tokio::test]
2796 async fn test_poll_once_or_spawn_pending_execution() {
2797 let (tx, rx) = tokio::sync::oneshot::channel();
2798 let done = Arc::new(AtomicBool::new(false));
2799 let done_in_task = done.clone();
2800 assert!(
2801 !poll_once_or_spawn(async move {
2802 tokio::task::yield_now().await;
2804 done_in_task.store(true, Ordering::SeqCst);
2805 let _ = tx.send(());
2806 }),
2807 "a future that yields is not completed inline"
2808 );
2809 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2810
2811 rx.await.unwrap();
2813 assert!(done.load(Ordering::SeqCst));
2814 }
2815}