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
57pub trait TurboTasksCallApi: Sync + Send {
60 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 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 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 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
115
116 fn get_task_name(&self, task: TaskId) -> String;
118}
119
120pub 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 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 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
172
173 #[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 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 fn pin_task_for_gc(&self, task: TaskId);
205
206 fn unpin_task_for_gc(&self, task: TaskId);
208
209 fn connect_task(&self, task: TaskId);
210
211 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 fn is_tracking_dependencies(&self) -> bool;
228}
229
230pub struct Unused<T> {
232 inner: T,
233}
234
235impl<T> Unused<T> {
236 pub unsafe fn new_unchecked(inner: T) -> Self {
242 Self { inner }
243 }
244
245 pub unsafe fn get_unchecked(&self) -> &T {
251 &self.inner
252 }
253
254 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 Persistent,
273
274 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#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
295pub enum InputResolution {
296 Resolved,
299 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 #[default]
324 Eventual,
325 Strong,
330}
331
332#[derive(Clone, Copy, Debug, Eq, PartialEq)]
333pub enum ReadCellTracking {
334 Tracked {
336 key: Option<u64>,
338 },
339 TrackOnlyError,
344 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 #[default]
390 Tracked,
391 TrackOnlyError,
396 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
500pub enum ScheduleKey {
501 Task(TaskId),
503 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#[cfg(feature = "inline_execution_stats")]
528#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
529pub struct InlineExecutionStats {
530 pub queued: u64,
532 pub claim_attempted: u64,
534 pub claim_completed: u64,
536 pub claim_yielded: u64,
538 pub claim_failed: u64,
540 pub waited_in_progress: u64,
542}
543
544#[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 #[inline]
566 fn claim_attempted(&self) {
567 #[cfg(feature = "inline_execution_stats")]
568 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
569 }
570
571 #[inline]
573 fn claim_completed(&self) {
574 #[cfg(feature = "inline_execution_stats")]
575 self.claim_completed.fetch_add(1, Ordering::Relaxed);
576 }
577
578 #[inline]
580 fn claim_yielded(&self) {
581 #[cfg(feature = "inline_execution_stats")]
582 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
583 }
584
585 #[inline]
587 fn claim_failed(&self) {
588 #[cfg(feature = "inline_execution_stats")]
589 self.claim_failed.fetch_add(1, Ordering::Relaxed);
590 }
591
592 #[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#[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
609const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
612
613thread_local! {
614 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
616}
617
618fn inline_execution_allowed() -> bool {
620 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
621}
622
623struct 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
639fn 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 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
664pub(crate) fn execute_read_target_inline(
666 turbo_tasks: &dyn TurboTasksApi,
667 key: ScheduleKey,
668) -> bool {
669 if !inline_execution_allowed() {
670 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 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_foreground_start: Event,
693 event_foreground_done: Event,
696 event_background_done: Event,
698 compilation_events: CompilationEventQueue,
699}
700
701struct CurrentTaskState {
710 task_id: Option<TaskId>,
711 execution_id: ExecutionId,
712 priority: TaskPriority,
713
714 #[cfg(feature = "verify_determinism")]
717 stateful: bool,
718
719 has_invalidator: bool,
721
722 in_top_level_task: bool,
725
726 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
731
732 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#[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
821task_local! {
823 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
825
826 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
827
828 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
833
834 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
837}
838
839#[derive(Clone, Default)]
850struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
851
852impl InlineExecutionSpanSlot {
853 fn set(span: &Span) {
855 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
856 *slot.0.lock().unwrap() = Some(span.clone());
857 });
858 }
859
860 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 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 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 pub fn pin_task_for_gc(&self, task_id: TaskId) {
939 self.backend.pin_task_for_gc(task_id, self);
940 }
941
942 pub fn unpin_task_for_gc(&self, task_id: TaskId) {
944 self.backend.unpin_task_for_gc(task_id, self);
945 }
946
947 #[track_caller]
951 fn spawn_once_task<T, Fut>(&self, future: Fut)
952 where
953 T: ?Sized,
954 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
955 {
956 let id = self.backend.create_transient_task(
957 TransientTaskType::Once(Box::pin(async move {
958 mark_top_level_task();
959 let raw_vc = future.await?.node;
960 raw_vc.to_non_local().await
961 })),
962 self,
963 );
964 self.schedule(id, TaskPriority::initial());
965 }
966
967 pub async fn run_once<T: Send + 'static>(
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 #[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 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, ));
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_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 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 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 }
1136 }
1137 }
1138
1139 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 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 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 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 #[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 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 tracking: ReadTracking::Untracked,
1309 consistency,
1310 },
1311 )
1312 .await?;
1313 Ok(())
1314 }
1315
1316 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 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 listener.await;
1356 } else {
1357 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 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 }
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 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 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
1514async 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 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, ));
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 InlineExecutionSpanSlot::set(&span);
1571
1572 async {
1573 let result = CaptureFuture::new(future).await;
1574
1575 wait_for_local_tasks().await;
1577
1578 let result = match result {
1579 Ok(Ok(raw_vc)) => {
1580 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 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 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 #[cfg(feature = "verify_determinism")]
1700 stateful: bool,
1701
1702 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 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 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 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1962 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 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#[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 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
2137pub 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
2148pub 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
2186pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2191 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2192}
2193
2194pub fn mark_finished() {
2197 with_turbo_tasks(|tt| {
2198 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2199 });
2200}
2201
2202pub 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
2238pub 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 }
2253
2254pub 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
2265pub 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
2283pub 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
2296pub struct GcRoot<T: ?Sized> {
2298 tt: Arc<dyn TurboTasksApi>,
2299 vc: OperationVc<T>,
2300}
2301
2302impl<T: ?Sized> GcRoot<T> {
2303 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
2310impl<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 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 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2352 self.vc.hash(state);
2353 }
2354}
2355
2356impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2360 fn borrow(&self) -> &OperationVc<T> {
2361 &self.vc
2362 }
2363}
2364
2365unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2368
2369unsafe 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 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2391 continue;
2392 }
2393 listener.await
2394 }
2395 ReadOutcome::InProgress(listener) => {
2396 #[cfg(feature = "inline_execution_stats")]
2398 this.note_waited_for_in_progress_task();
2399 listener.await
2400 }
2401 }
2402 }
2403}
2404
2405#[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
2418type CellUpdate = (
2421 SharedReference,
2422 Option<SmallVec<[u64; 2]>>,
2423 Option<CellHash>,
2424);
2425
2426type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2429
2430impl CurrentCellRef {
2431 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 rx.await.unwrap();
2837 assert!(done.load(Ordering::SeqCst));
2838 }
2839}