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 fn invalidate_many(&self, tasks: &[TaskId]);
131 fn invalidate_many_with_reasons(
133 &self,
134 tasks: &[TaskId],
135 reasons: Vec<StaticOrArc<dyn InvalidationReason>>,
136 );
137
138 fn invalidate_serialization(&self, task: TaskId);
139
140 fn try_read_task_output(
141 &self,
142 task: TaskId,
143 options: ReadOutputOptions,
144 ) -> Result<ReadOutcome<RawVc>>;
145
146 fn try_read_task_cell(
147 &self,
148 task: TaskId,
149 index: CellId,
150 options: ReadCellOptions,
151 ) -> Result<ReadOutcome<TypedCellContent>>;
152
153 fn try_read_local_output(
169 &self,
170 execution_id: ExecutionId,
171 local_task_id: LocalTaskId,
172 ) -> Result<Result<RawVc, EventListener>>;
173
174 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
175
176 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
182
183 #[cfg(feature = "inline_execution_stats")]
186 fn note_waited_for_in_progress_task(&self);
187
188 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
189 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
190 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
191
192 fn try_read_own_task_cell(
195 &self,
196 current_task: TaskId,
197 index: CellId,
198 ) -> Result<TypedCellContent>;
199
200 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
201 fn update_own_task_cell(
202 &self,
203 task: TaskId,
204 index: CellId,
205 content: CellContent,
206 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
207 content_hash: Option<CellHash>,
208 verification_mode: VerificationMode,
209 );
210 fn mark_own_task_as_finished(&self, task: TaskId);
211
212 fn pin_task_for_gc(&self, task: TaskId);
215
216 fn unpin_task_for_gc(&self, task: TaskId);
218
219 fn connect_task(&self, task: TaskId);
220
221 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
226
227 fn task_statistics(&self) -> &TaskStatisticsApi;
228
229 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
230
231 fn subscribe_to_compilation_events(
232 &self,
233 event_types: Option<Vec<String>>,
234 ) -> Receiver<Arc<dyn CompilationEvent>>;
235
236 fn is_tracking_dependencies(&self) -> bool;
238}
239
240pub struct Unused<T> {
242 inner: T,
243}
244
245impl<T> Unused<T> {
246 pub unsafe fn new_unchecked(inner: T) -> Self {
252 Self { inner }
253 }
254
255 pub unsafe fn get_unchecked(&self) -> &T {
261 &self.inner
262 }
263
264 pub fn into(self) -> T {
266 self.inner
267 }
268}
269
270#[allow(clippy::manual_non_exhaustive)]
271pub struct UpdateInfo {
272 pub duration: Duration,
273 pub tasks: usize,
274 pub reasons: InvalidationReasonSet,
275 #[allow(dead_code)]
276 placeholder_for_future_fields: (),
277}
278
279#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
280pub enum TaskPersistence {
281 Persistent,
283
284 Transient,
291}
292
293impl Display for TaskPersistence {
294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295 match self {
296 TaskPersistence::Persistent => write!(f, "persistent"),
297 TaskPersistence::Transient => write!(f, "transient"),
298 }
299 }
300}
301
302#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
305pub enum InputResolution {
306 Resolved,
309 Unresolved,
311}
312
313impl InputResolution {
314 #[inline]
315 pub fn from_is_resolved(is_resolved: bool) -> Self {
316 if is_resolved {
317 Self::Resolved
318 } else {
319 Self::Unresolved
320 }
321 }
322
323 #[inline]
324 pub fn is_resolved(self) -> bool {
325 matches!(self, Self::Resolved)
326 }
327}
328
329#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
330pub enum ReadConsistency {
331 #[default]
334 Eventual,
335 Strong,
340}
341
342#[derive(Clone, Copy, Debug, Eq, PartialEq)]
343pub enum ReadCellTracking {
344 Tracked {
346 key: Option<u64>,
348 },
349 TrackOnlyError,
354 Untracked,
359}
360
361impl ReadCellTracking {
362 pub fn should_track(&self, is_err: bool) -> bool {
363 match self {
364 ReadCellTracking::Tracked { .. } => true,
365 ReadCellTracking::TrackOnlyError => is_err,
366 ReadCellTracking::Untracked => false,
367 }
368 }
369
370 pub fn key(&self) -> Option<u64> {
371 match self {
372 ReadCellTracking::Tracked { key } => *key,
373 ReadCellTracking::TrackOnlyError => None,
374 ReadCellTracking::Untracked => None,
375 }
376 }
377}
378
379impl Default for ReadCellTracking {
380 fn default() -> Self {
381 ReadCellTracking::Tracked { key: None }
382 }
383}
384
385impl Display for ReadCellTracking {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 match self {
388 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
389 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
390 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
391 ReadCellTracking::Untracked => write!(f, "untracked"),
392 }
393 }
394}
395
396#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
397pub enum ReadTracking {
398 #[default]
400 Tracked,
401 TrackOnlyError,
406 Untracked,
411}
412
413impl ReadTracking {
414 pub fn should_track(&self, is_err: bool) -> bool {
415 match self {
416 ReadTracking::Tracked => true,
417 ReadTracking::TrackOnlyError => is_err,
418 ReadTracking::Untracked => false,
419 }
420 }
421}
422
423impl Display for ReadTracking {
424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425 match self {
426 ReadTracking::Tracked => write!(f, "tracked"),
427 ReadTracking::TrackOnlyError => write!(f, "track only error"),
428 ReadTracking::Untracked => write!(f, "untracked"),
429 }
430 }
431}
432
433#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
434pub enum TaskPriority {
435 #[default]
436 Initial,
437 Invalidation {
438 priority: Reverse<u32>,
439 },
440 Recomputation,
441}
442
443impl TaskPriority {
444 pub fn invalidation(priority: u32) -> Self {
445 Self::Invalidation {
446 priority: Reverse(priority),
447 }
448 }
449
450 pub fn initial() -> Self {
451 Self::Initial
452 }
453
454 pub fn leaf() -> Self {
455 Self::Invalidation {
456 priority: Reverse(0),
457 }
458 }
459
460 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
461 match self {
462 TaskPriority::Initial => parent_priority,
463 TaskPriority::Invalidation { priority } => {
464 if let TaskPriority::Invalidation {
465 priority: parent_priority,
466 } = parent_priority
467 && priority.0 < parent_priority.0
468 {
469 Self::Invalidation {
470 priority: Reverse(parent_priority.0.saturating_add(1)),
471 }
472 } else {
473 *self
474 }
475 }
476 TaskPriority::Recomputation => TaskPriority::Recomputation,
477 }
478 }
479}
480
481impl Display for TaskPriority {
482 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483 match self {
484 TaskPriority::Initial => write!(f, "initial"),
485 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
486 TaskPriority::Recomputation => write!(f, "recomputation"),
487 }
488 }
489}
490
491enum ScheduledTask {
492 Task {
493 task_id: TaskId,
494 span: Span,
495 },
496 LocalTask {
497 ty: LocalTaskSpec,
498 persistence: TaskPersistence,
499 parent_task: Option<TaskId>,
502 execution_id: ExecutionId,
503 local_task_id: LocalTaskId,
504 global_task_state: CurrentTaskStateHandle,
505 span: Span,
506 },
507}
508
509#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
513pub enum ScheduleKey {
514 Task(TaskId),
516 LocalTask(Option<TaskId>, ExecutionId, LocalTaskId),
525}
526
527impl ScheduleKey {
528 pub(crate) fn current_local_task(
530 execution_id: ExecutionId,
531 local_task_id: LocalTaskId,
532 ) -> Self {
533 let parent_task = CURRENT_TASK_STATE.with(|ts| ts.current_task_id());
534 Self::LocalTask(parent_task, execution_id, local_task_id)
535 }
536}
537
538impl Claimable for ScheduledTask {
539 type Key = ScheduleKey;
540
541 fn claim_key(&self) -> ScheduleKey {
542 match self {
543 ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
544 ScheduledTask::LocalTask {
545 parent_task,
546 execution_id,
547 local_task_id,
548 ..
549 } => ScheduleKey::LocalTask(*parent_task, *execution_id, *local_task_id),
550 }
551 }
552}
553
554#[cfg(feature = "inline_execution_stats")]
555use std::sync::atomic::AtomicU64;
556
557#[cfg(feature = "inline_execution_stats")]
560#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
561pub struct InlineExecutionStats {
562 pub queued: u64,
564 pub claim_attempted: u64,
566 pub claim_completed: u64,
568 pub claim_yielded: u64,
570 pub claim_failed: u64,
572 pub waited_in_progress: u64,
574}
575
576#[derive(Default)]
582struct InlineExecutionCounters {
583 #[cfg(feature = "inline_execution_stats")]
584 claim_attempted: AtomicU64,
585 #[cfg(feature = "inline_execution_stats")]
586 claim_completed: AtomicU64,
587 #[cfg(feature = "inline_execution_stats")]
588 claim_yielded: AtomicU64,
589 #[cfg(feature = "inline_execution_stats")]
590 claim_failed: AtomicU64,
591 #[cfg(feature = "inline_execution_stats")]
592 waited_in_progress: AtomicU64,
593}
594
595impl InlineExecutionCounters {
596 #[inline]
598 fn claim_attempted(&self) {
599 #[cfg(feature = "inline_execution_stats")]
600 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
601 }
602
603 #[inline]
605 fn claim_completed(&self) {
606 #[cfg(feature = "inline_execution_stats")]
607 self.claim_completed.fetch_add(1, Ordering::Relaxed);
608 }
609
610 #[inline]
612 fn claim_yielded(&self) {
613 #[cfg(feature = "inline_execution_stats")]
614 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
615 }
616
617 #[inline]
619 fn claim_failed(&self) {
620 #[cfg(feature = "inline_execution_stats")]
621 self.claim_failed.fetch_add(1, Ordering::Relaxed);
622 }
623
624 #[cfg(feature = "inline_execution_stats")]
626 #[inline]
627 fn waited_in_progress(&self) {
628 self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
629 }
630}
631
632#[cfg(feature = "inline_execution_stats")]
634pub(crate) fn inline_stats_requested() -> bool {
635 static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
636 std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
637 });
638 *REQUESTED
639}
640
641const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
644
645thread_local! {
646 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
648}
649
650fn inline_execution_allowed() -> bool {
652 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
653}
654
655struct InlineExecutionDepthGuard;
657
658impl InlineExecutionDepthGuard {
659 fn enter() -> Self {
660 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
661 Self
662 }
663}
664
665impl Drop for InlineExecutionDepthGuard {
666 fn drop(&mut self) {
667 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
668 }
669}
670
671fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
674 let _depth_guard = InlineExecutionDepthGuard::enter();
675 let span_slot = InlineExecutionSpanSlot::default();
676 let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
677 match future
681 .as_mut()
682 .poll(&mut Context::from_waker(Waker::noop()))
683 {
684 Poll::Ready(()) => {
685 span_slot.record("complete");
686 true
687 }
688 Poll::Pending => {
689 span_slot.record("partial");
690 tokio::task::spawn(future);
691 false
692 }
693 }
694}
695
696pub(crate) fn execute_read_target_inline(
698 turbo_tasks: &dyn TurboTasksApi,
699 key: ScheduleKey,
700) -> bool {
701 if !inline_execution_allowed() {
702 return false;
704 }
705 turbo_tasks.try_execute_scheduled_task_inline(key)
706}
707
708pub struct TurboTasks<B: Backend + 'static> {
709 this: Weak<Self>,
710 backend: B,
711 execution_id_factory: IdFactory<ExecutionId>,
712 stopped: AtomicBool,
713 currently_scheduled_foreground_jobs: AtomicUsize,
714 currently_scheduled_background_jobs: AtomicUsize,
715 scheduled_tasks: AtomicUsize,
716 inline_counters: InlineExecutionCounters,
719 priority_runner:
720 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
721 start: Mutex<Option<Instant>>,
722 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
723 event_foreground_start: Event,
725 event_foreground_done: Event,
728 event_background_done: Event,
730 compilation_events: CompilationEventQueue,
731}
732
733struct CurrentTaskState {
742 task_id: Option<TaskId>,
743 execution_id: ExecutionId,
744 priority: TaskPriority,
745
746 #[cfg(feature = "verify_determinism")]
749 stateful: bool,
750
751 has_invalidator: bool,
753
754 in_top_level_task: bool,
757
758 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
763
764 local_tasks: LocalTaskTracker,
767}
768
769impl CurrentTaskState {
770 fn new(
771 task_id: TaskId,
772 execution_id: ExecutionId,
773 priority: TaskPriority,
774 in_top_level_task: bool,
775 ) -> Self {
776 Self {
777 task_id: Some(task_id),
778 execution_id,
779 priority,
780 #[cfg(feature = "verify_determinism")]
781 stateful: false,
782 has_invalidator: false,
783 in_top_level_task,
784 cell_counters: Some(AutoMap::default()),
785 local_tasks: LocalTaskTracker::new(),
786 }
787 }
788
789 fn new_temporary(
790 execution_id: ExecutionId,
791 priority: TaskPriority,
792 in_top_level_task: bool,
793 ) -> Self {
794 Self {
795 task_id: None,
796 execution_id,
797 priority,
798 #[cfg(feature = "verify_determinism")]
799 stateful: false,
800 has_invalidator: false,
801 in_top_level_task,
802 cell_counters: None,
803 local_tasks: LocalTaskTracker::new(),
804 }
805 }
806
807 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
808 if self.execution_id != expected_execution_id {
809 panic!(
810 "Local tasks can only be scheduled/awaited within the same execution of the \
811 parent task that created them"
812 );
813 }
814 }
815}
816
817#[derive(Clone)]
821struct CurrentTaskStateHandle {
822 inner: Arc<CurrentTaskStateInner>,
823}
824
825struct CurrentTaskStateInner {
826 current_task_id: Option<TaskId>,
827 state: RwLock<CurrentTaskState>,
828}
829
830impl CurrentTaskStateHandle {
831 fn new(state: CurrentTaskState) -> Self {
832 Self {
833 inner: Arc::new(CurrentTaskStateInner {
834 current_task_id: state.task_id,
835 state: RwLock::new(state),
836 }),
837 }
838 }
839
840 fn current_task_id(&self) -> Option<TaskId> {
841 self.inner.current_task_id
842 }
843}
844
845impl Deref for CurrentTaskStateHandle {
846 type Target = RwLock<CurrentTaskState>;
847
848 fn deref(&self) -> &Self::Target {
849 &self.inner.state
850 }
851}
852
853task_local! {
855 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
857
858 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
859
860 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
865
866 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
869}
870
871#[derive(Clone, Default)]
882struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
883
884impl InlineExecutionSpanSlot {
885 fn set(span: &Span) {
887 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
888 *slot.0.lock().unwrap() = Some(span.clone());
889 });
890 }
891
892 fn record(&self, outcome: &'static str) {
896 if let Some(span) = self.0.lock().unwrap().as_ref() {
897 span.record("inline_execution", outcome);
898 }
899 }
900}
901
902impl<B: Backend + 'static> TurboTasks<B> {
903 pub fn new(backend: B) -> Arc<Self> {
909 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
910 let this = Arc::new_cyclic(|this| Self {
911 this: this.clone(),
912 backend,
913 execution_id_factory,
914 stopped: AtomicBool::new(false),
915 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
916 currently_scheduled_background_jobs: AtomicUsize::new(0),
917 scheduled_tasks: AtomicUsize::new(0),
918 inline_counters: InlineExecutionCounters::default(),
919 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
920 start: Default::default(),
921 aggregated_update: Default::default(),
922 event_foreground_done: Event::new(|| {
923 || "TurboTasks::event_foreground_done".to_string()
924 }),
925 event_foreground_start: Event::new(|| {
926 || "TurboTasks::event_foreground_start".to_string()
927 }),
928 event_background_done: Event::new(|| {
929 || "TurboTasks::event_background_done".to_string()
930 }),
931 compilation_events: CompilationEventQueue::default(),
932 });
933 this.backend.startup(&*this);
934 this
935 }
936
937 pub fn pin(&self) -> Arc<Self> {
938 self.this.upgrade().unwrap()
939 }
940
941 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
943 where
944 T: ?Sized,
945 F: Fn() -> Fut + Send + Sync + Clone + 'static,
946 Fut: Future<Output = Result<Vc<T>>> + Send,
947 {
948 let id = self.backend.create_transient_task(
949 TransientTaskType::Root(Box::new(move || {
950 let functor = functor.clone();
951 Box::pin(async move {
952 mark_top_level_task();
953 let raw_vc = functor().await?.node;
954 raw_vc.to_non_local().await
955 })
956 })),
957 self,
958 );
959 self.schedule(id, TaskPriority::initial());
960 id
961 }
962
963 pub fn dispose_root_task(&self, task_id: TaskId) {
964 self.backend.dispose_root_task(task_id, self);
965 }
966
967 pub fn pin_task_for_gc(&self, task_id: TaskId) {
971 self.backend.pin_task_for_gc(task_id, self);
972 }
973
974 pub fn unpin_task_for_gc(&self, task_id: TaskId) {
976 self.backend.unpin_task_for_gc(task_id, self);
977 }
978
979 #[track_caller]
983 fn spawn_once_task<T, Fut>(&self, future: Fut)
984 where
985 T: ?Sized,
986 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
987 {
988 let id = self.backend.create_transient_task(
989 TransientTaskType::Once(Box::pin(async move {
990 mark_top_level_task();
991 let raw_vc = future.await?.node;
992 raw_vc.to_non_local().await
993 })),
994 self,
995 );
996 self.schedule(id, TaskPriority::initial());
997 }
998
999 pub async fn run_once<T: Send + 'static>(
1022 &self,
1023 future: impl Future<Output = Result<T>> + Send + 'static,
1024 ) -> Result<T> {
1025 let (tx, rx) = tokio::sync::oneshot::channel();
1026 self.spawn_once_task(async move {
1027 mark_top_level_task();
1028 let result = future.await;
1029 tx.send(result)
1030 .map_err(|_| anyhow!("unable to send result"))?;
1031 Ok(Completion::new())
1032 });
1033
1034 rx.await?
1035 }
1036
1037 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
1043 pub async fn run<T: Send + 'static>(
1044 &self,
1045 future: impl Future<Output = Result<T>> + Send + 'static,
1046 ) -> Result<T, TurboTasksExecutionError> {
1047 self.begin_foreground_job();
1048 let execution_id = self.execution_id_factory.wrapping_get();
1050 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
1051 execution_id,
1052 TaskPriority::initial(),
1053 true, ));
1055
1056 let result = TURBO_TASKS
1057 .scope(
1058 self.pin(),
1059 CURRENT_TASK_STATE.scope(current_task_state, async {
1060 let result = CaptureFuture::new(future).await;
1061
1062 wait_for_local_tasks().await;
1064
1065 match result {
1066 Ok(Ok(value)) => Ok(value),
1067 Ok(Err(err)) => Err(err.into()),
1068 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1069 }
1070 }),
1071 )
1072 .await;
1073 self.finish_foreground_job();
1074 result
1075 }
1076
1077 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1078 let this = self.pin();
1079 tokio::spawn(async move {
1080 this.pin()
1081 .run_once(async move {
1082 this.finish_foreground_job();
1083 future.await;
1084 this.begin_foreground_job();
1085 Ok(())
1086 })
1087 .await
1088 .unwrap()
1089 });
1090 }
1091
1092 pub(crate) fn native_call(
1093 &self,
1094 native_fn: &'static NativeFunction,
1095 this: Option<RawVc>,
1096 arg: &mut dyn DynTaskInputsStorage,
1097 persistence: TaskPersistence,
1098 ) -> RawVc {
1099 RawVc::task_output(self.backend.get_or_create_task(
1100 native_fn,
1101 this,
1102 arg,
1103 current_task_if_available("turbo_function calls"),
1104 persistence,
1105 self,
1106 ))
1107 }
1108
1109 pub fn dynamic_call(
1110 &self,
1111 native_fn: &'static NativeFunction,
1112 this: Option<RawVc>,
1113 arg: &mut dyn DynTaskInputsStorage,
1114 inputs_resolved: InputResolution,
1115 persistence: TaskPersistence,
1116 ) -> RawVc {
1117 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1118 return self.native_call(native_fn, this, arg, persistence);
1119 }
1120 let arg = arg.take_box();
1122 let task_type = LocalTaskSpec {
1123 task_type: LocalTaskType::ResolveNative { native_fn },
1124 this,
1125 arg,
1126 };
1127 self.schedule_local_task(task_type, persistence)
1128 }
1129
1130 pub fn trait_call(
1131 &self,
1132 trait_method: &'static TraitMethod,
1133 this: RawVc,
1134 arg: &mut dyn DynTaskInputsStorage,
1135 inputs_resolved: InputResolution,
1136 persistence: TaskPersistence,
1137 ) -> RawVc {
1138 if let Some((_, cell_id)) = this.as_task_cell() {
1142 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1143 Some(native_fn) => {
1144 if let Some(filter) = native_fn.arg_meta.filter_owned {
1145 let (resolved, mut arg) = (filter)(arg);
1146 return self.dynamic_call(
1147 native_fn,
1148 Some(this),
1149 &mut arg,
1150 resolved,
1151 persistence,
1152 );
1153 } else {
1154 return self.dynamic_call(
1155 native_fn,
1156 Some(this),
1157 arg,
1158 inputs_resolved,
1159 persistence,
1160 );
1161 }
1162 }
1163 None => {
1164 }
1168 }
1169 }
1170
1171 let task_type = LocalTaskSpec {
1173 task_type: LocalTaskType::ResolveTrait { trait_method },
1174 this: Some(this),
1175 arg: arg.take_box(),
1176 };
1177
1178 self.schedule_local_task(task_type, persistence)
1179 }
1180
1181 #[track_caller]
1182 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1183 self.begin_foreground_job();
1184 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1185
1186 let task = ScheduledTask::Task {
1187 task_id,
1188 span: Span::current(),
1189 };
1190 self.priority_runner.schedule(&self.pin(), task, priority);
1191 }
1192
1193 #[track_caller]
1196 pub fn schedule_many(&self, tasks: &[(TaskId, TaskPriority)]) {
1197 if tasks.is_empty() {
1198 return;
1199 }
1200 self.begin_foreground_jobs(tasks.len());
1201 self.scheduled_tasks
1202 .fetch_add(tasks.len(), Ordering::AcqRel);
1203
1204 let span = Span::current();
1205 self.priority_runner.schedule_many(
1206 &self.pin(),
1207 tasks.iter().map(|&(task_id, priority)| {
1208 (
1209 ScheduledTask::Task {
1210 task_id,
1211 span: span.clone(),
1212 },
1213 priority,
1214 )
1215 }),
1216 );
1217 }
1218
1219 fn schedule_local_task(
1220 &self,
1221 ty: LocalTaskSpec,
1222 persistence: TaskPersistence,
1224 ) -> RawVc {
1225 let task_type = ty.task_type;
1226 let (global_task_state, parent_task, execution_id, priority, local_task_id) =
1227 CURRENT_TASK_STATE.with(|gts| {
1228 let mut gts_write = gts.write().unwrap();
1229 let local_task_id = gts_write.local_tasks.create(task_type);
1230 (
1231 gts.clone(),
1232 gts_write.task_id,
1233 gts_write.execution_id,
1234 gts_write.priority,
1235 local_task_id,
1236 )
1237 });
1238
1239 let task = ScheduledTask::LocalTask {
1240 ty,
1241 persistence,
1242 parent_task,
1243 execution_id,
1244 local_task_id,
1245 global_task_state,
1246 span: Span::current(),
1247 };
1248 self.priority_runner.schedule(&self.pin(), task, priority);
1249
1250 RawVc::local_output(execution_id, local_task_id, persistence)
1251 }
1252
1253 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1255 let this = self.pin();
1256 self.inline_counters.claim_attempted();
1257 if let Some(future) = self.priority_runner.claim(&this, &key) {
1258 let completed = poll_once_or_spawn(future);
1259 if completed {
1260 self.inline_counters.claim_completed();
1261 } else {
1262 self.inline_counters.claim_yielded();
1263 }
1264 return completed;
1265 }
1266 self.inline_counters.claim_failed();
1267 false
1268 }
1269
1270 #[cfg(feature = "inline_execution_stats")]
1271 fn note_waited_for_in_progress_task(&self) {
1272 self.inline_counters.waited_in_progress();
1273 }
1274
1275 fn begin_foreground_job(&self) {
1276 self.begin_foreground_jobs(1);
1277 }
1278
1279 fn begin_foreground_jobs(&self, count: usize) {
1281 debug_assert!(count > 0);
1282 if self
1283 .currently_scheduled_foreground_jobs
1284 .fetch_add(count, Ordering::AcqRel)
1285 == 0
1286 {
1287 *self.start.lock().unwrap() = Some(Instant::now());
1288 self.event_foreground_start.notify(usize::MAX);
1289 self.backend.idle_end(self);
1290 }
1291 }
1292
1293 fn finish_foreground_job(&self) {
1294 if self
1295 .currently_scheduled_foreground_jobs
1296 .fetch_sub(1, Ordering::AcqRel)
1297 == 1
1298 {
1299 self.backend.idle_start(self);
1300 let total = self.scheduled_tasks.load(Ordering::Acquire);
1303 self.scheduled_tasks.store(0, Ordering::Release);
1304 if let Some(start) = *self.start.lock().unwrap() {
1305 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1306 if let Some(update) = update.as_mut() {
1307 update.0 += start.elapsed();
1308 update.1 += total;
1309 } else {
1310 *update = Some((start.elapsed(), total));
1311 }
1312 }
1313 self.event_foreground_done.notify(usize::MAX);
1314 }
1315 }
1316
1317 fn begin_background_job(&self) {
1318 self.currently_scheduled_background_jobs
1319 .fetch_add(1, Ordering::Relaxed);
1320 }
1321
1322 fn finish_background_job(&self) {
1323 if self
1324 .currently_scheduled_background_jobs
1325 .fetch_sub(1, Ordering::Relaxed)
1326 == 1
1327 {
1328 self.event_background_done.notify(usize::MAX);
1329 }
1330 }
1331
1332 pub fn get_in_progress_count(&self) -> usize {
1333 self.currently_scheduled_foreground_jobs
1334 .load(Ordering::Acquire)
1335 }
1336
1337 #[cfg(feature = "inline_execution_stats")]
1340 #[doc(hidden)]
1341 pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1342 let counters = &self.inline_counters;
1343 InlineExecutionStats {
1344 queued: self.priority_runner.total_queued(),
1345 claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1346 claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1347 claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1348 claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1349 waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1350 }
1351 }
1352
1353 pub async fn wait_task_completion(
1365 &self,
1366 id: TaskId,
1367 consistency: ReadConsistency,
1368 ) -> Result<()> {
1369 read_task_output(
1370 self,
1371 id,
1372 ReadOutputOptions {
1373 tracking: ReadTracking::Untracked,
1375 consistency,
1376 },
1377 )
1378 .await?;
1379 Ok(())
1380 }
1381
1382 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1385 self.aggregated_update_info(aggregation, Duration::MAX)
1386 .await
1387 .unwrap()
1388 }
1389
1390 pub async fn aggregated_update_info(
1394 &self,
1395 aggregation: Duration,
1396 timeout: Duration,
1397 ) -> Option<UpdateInfo> {
1398 let listener = self
1399 .event_foreground_done
1400 .listen_with_note(|| || "wait for update info".to_string());
1401 let wait_for_finish = {
1402 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1403 if aggregation.is_zero() {
1404 if let Some((duration, tasks)) = update.take() {
1405 return Some(UpdateInfo {
1406 duration,
1407 tasks,
1408 reasons: take(reason_set),
1409 placeholder_for_future_fields: (),
1410 });
1411 } else {
1412 true
1413 }
1414 } else {
1415 update.is_none()
1416 }
1417 };
1418 if wait_for_finish {
1419 if timeout == Duration::MAX {
1420 listener.await;
1422 } else {
1423 let start_listener = self
1425 .event_foreground_start
1426 .listen_with_note(|| || "wait for update info".to_string());
1427 if self
1428 .currently_scheduled_foreground_jobs
1429 .load(Ordering::Acquire)
1430 == 0
1431 {
1432 start_listener.await;
1433 } else {
1434 drop(start_listener);
1435 }
1436 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1437 return None;
1439 }
1440 }
1441 }
1442 if !aggregation.is_zero() {
1443 loop {
1444 select! {
1445 () = tokio::time::sleep(aggregation) => {
1446 break;
1447 }
1448 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1449 }
1451 }
1452 }
1453 }
1454 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1455 if let Some((duration, tasks)) = update.take() {
1456 Some(UpdateInfo {
1457 duration,
1458 tasks,
1459 reasons: take(reason_set),
1460 placeholder_for_future_fields: (),
1461 })
1462 } else {
1463 panic!("aggregated_update_info must not called concurrently")
1464 }
1465 }
1466
1467 pub async fn wait_background_done(&self) {
1468 let listener = self.event_background_done.listen();
1469 if self
1470 .currently_scheduled_background_jobs
1471 .load(Ordering::Acquire)
1472 != 0
1473 {
1474 listener.await;
1475 }
1476 }
1477
1478 pub async fn stop_and_wait(&self) {
1479 #[cfg(feature = "inline_execution_stats")]
1480 if inline_stats_requested() {
1481 eprintln!(
1484 "turbo-tasks inline execution stats: {:#?}",
1485 self.inline_execution_stats()
1486 );
1487 }
1488 turbo_tasks_future_scope(self.pin(), async move {
1489 self.backend.stopping(self);
1490 self.stopped.store(true, Ordering::Release);
1491 {
1492 let listener = self
1493 .event_foreground_done
1494 .listen_with_note(|| || "wait for stop".to_string());
1495 if self
1496 .currently_scheduled_foreground_jobs
1497 .load(Ordering::Acquire)
1498 != 0
1499 {
1500 listener.await;
1501 }
1502 }
1503 {
1504 let listener = self.event_background_done.listen();
1505 if self
1506 .currently_scheduled_background_jobs
1507 .load(Ordering::Acquire)
1508 != 0
1509 {
1510 listener.await;
1511 }
1512 }
1513 self.backend.stop(self);
1514 self.compilation_events.flush_and_close().await;
1518 })
1519 .await;
1520 }
1521
1522 #[track_caller]
1523 pub(crate) fn schedule_background_job<T>(&self, func: T)
1524 where
1525 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1526 T::CallOnceFuture: Send,
1527 {
1528 let mut this = self.pin();
1529 self.begin_background_job();
1530 tokio::spawn(
1531 TURBO_TASKS
1532 .scope(this.clone(), async move {
1533 if !this.stopped.load(Ordering::Acquire) {
1534 this = func(this).await;
1535 }
1536 this.finish_background_job();
1537 })
1538 .in_current_span(),
1539 );
1540 }
1541
1542 fn finish_current_task_state(&self) -> FinishedTaskState {
1543 CURRENT_TASK_STATE.with(|cell| {
1544 let current_task_state = &*cell.write().unwrap();
1545 FinishedTaskState {
1546 #[cfg(feature = "verify_determinism")]
1547 stateful: current_task_state.stateful,
1548 has_invalidator: current_task_state.has_invalidator,
1549 }
1550 })
1551 }
1552
1553 pub fn backend(&self) -> &B {
1554 &self.backend
1555 }
1556
1557 pub fn get_current_task_priority(&self) -> TaskPriority {
1558 CURRENT_TASK_STATE
1559 .try_with(|task_state| task_state.read().unwrap().priority)
1560 .unwrap_or(TaskPriority::initial())
1561 }
1562
1563 pub fn is_idle(&self) -> bool {
1564 self.currently_scheduled_foreground_jobs
1565 .load(Ordering::Acquire)
1566 == 0
1567 }
1568
1569 #[track_caller]
1570 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1571 self.schedule_background_job(async move |this| {
1572 this.backend.run_backend_job(job, &*this).await;
1573 this
1574 })
1575 }
1576}
1577
1578struct TurboTasksExecutor;
1579
1580async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1585 match AssertUnwindSafe(f).catch_unwind().await {
1586 Ok(r) => r,
1587 Err(_) => {
1588 eprintln!(
1589 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1590 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1591 https://github.com/vercel/next.js/discussions and include the panic message \
1592 and stack trace above.\n\nAborting."
1593 );
1594 abort();
1595 }
1596 }
1597}
1598
1599impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1600 const LOWEST_PRIORITY: TaskPriority = TaskPriority::Initial;
1601
1602 type Future = impl Future<Output = ()> + Send + 'static;
1603
1604 fn execute(
1605 &self,
1606 this: &Arc<TurboTasks<B>>,
1607 scheduled_task: ScheduledTask,
1608 priority: TaskPriority,
1609 ) -> Self::Future {
1610 match scheduled_task {
1611 ScheduledTask::Task { task_id, span } => {
1612 let this2 = this.clone();
1613 let this = this.clone();
1614 let future = async move {
1615 abort_on_panic(async {
1616 let execution_id = this.execution_id_factory.wrapping_get();
1619 let current_task_state =
1620 CurrentTaskStateHandle::new(CurrentTaskState::new(
1621 task_id,
1622 execution_id,
1623 priority,
1624 false, ));
1626 let single_execution_future = async {
1627 if this.stopped.load(Ordering::Acquire) {
1628 this.backend.task_execution_canceled(task_id, &*this);
1629 return None;
1630 }
1631
1632 let TaskExecutionSpec { future, span } = this
1633 .backend
1634 .try_start_task_execution(task_id, priority, &*this)?;
1635
1636 InlineExecutionSpanSlot::set(&span);
1639
1640 async {
1641 let result = CaptureFuture::new(future).await;
1642
1643 wait_for_local_tasks().await;
1645
1646 let result = match result {
1647 Ok(Ok(raw_vc)) => {
1648 raw_vc
1651 .to_non_local_unchecked_sync(&*this)
1652 .map_err(|err| err.into())
1653 }
1654 Ok(Err(err)) => Err(err.into()),
1655 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1656 };
1657
1658 let finished_state = this.finish_current_task_state();
1659 let cell_counters = CURRENT_TASK_STATE
1660 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1661 this.backend.task_execution_completed(
1662 task_id,
1663 result,
1664 &cell_counters,
1665 #[cfg(feature = "verify_determinism")]
1666 finished_state.stateful,
1667 finished_state.has_invalidator,
1668 &*this,
1669 )
1670 }
1671 .instrument(span)
1672 .await
1673 };
1674 if let Some(stale_priority) = CURRENT_TASK_STATE
1675 .scope(current_task_state, single_execution_future)
1676 .await
1677 {
1678 this.schedule(task_id, stale_priority);
1681 }
1682 this.finish_foreground_job();
1683 })
1684 .await
1685 };
1686
1687 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1688 }
1689 ScheduledTask::LocalTask {
1690 ty,
1691 persistence,
1692 parent_task: _,
1693 execution_id: _,
1694 local_task_id,
1695 global_task_state,
1696 span,
1697 } => {
1698 let this2 = this.clone();
1699 let this = this.clone();
1700 let task_type = ty.task_type;
1701 let future = async move {
1702 let span = match &ty.task_type {
1703 LocalTaskType::ResolveNative { native_fn } => {
1704 native_fn.resolve_span(priority)
1705 }
1706 LocalTaskType::ResolveTrait { trait_method } => {
1707 trait_method.resolve_span(priority)
1708 }
1709 };
1710 InlineExecutionSpanSlot::set(&span);
1713 abort_on_panic(
1714 async move {
1715 let result = match ty.task_type {
1716 LocalTaskType::ResolveNative { native_fn } => {
1717 LocalTaskType::run_resolve_native(
1718 native_fn,
1719 ty.this,
1720 &*ty.arg,
1721 persistence,
1722 this,
1723 )
1724 .await
1725 }
1726 LocalTaskType::ResolveTrait { trait_method } => {
1727 LocalTaskType::run_resolve_trait(
1728 trait_method,
1729 ty.this.unwrap(),
1730 &*ty.arg,
1731 persistence,
1732 this,
1733 )
1734 .await
1735 }
1736 };
1737
1738 let output = match result {
1739 Ok(raw_vc) => OutputContent::Link(raw_vc),
1740 Err(err) => OutputContent::Error(
1741 TurboTasksExecutionError::from(err)
1742 .with_local_task_context(task_type.to_string()),
1743 ),
1744 };
1745
1746 CURRENT_TASK_STATE.with(move |gts| {
1747 gts.write()
1748 .unwrap()
1749 .local_tasks
1750 .complete(local_task_id, output);
1751 });
1752 }
1753 .instrument(span),
1754 )
1755 .await
1756 };
1757 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1758
1759 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1760 }
1761 }
1762 }
1763
1764 fn discard(
1765 &self,
1766 this: &Arc<TurboTasks<B>>,
1767 scheduled_task: ScheduledTask,
1768 _priority: TaskPriority,
1769 ) {
1770 match scheduled_task {
1771 ScheduledTask::Task { .. } => this.finish_foreground_job(),
1774 ScheduledTask::LocalTask { .. } => {}
1777 }
1778 }
1779}
1780
1781struct FinishedTaskState {
1782 #[cfg(feature = "verify_determinism")]
1785 stateful: bool,
1786
1787 has_invalidator: bool,
1789}
1790
1791impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1792 fn dynamic_call(
1793 &self,
1794 native_fn: &'static NativeFunction,
1795 this: Option<RawVc>,
1796 arg: &mut dyn DynTaskInputsStorage,
1797 inputs_resolved: InputResolution,
1798 persistence: TaskPersistence,
1799 ) -> RawVc {
1800 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1801 }
1802 fn native_call(
1803 &self,
1804 native_fn: &'static NativeFunction,
1805 this: Option<RawVc>,
1806 arg: &mut dyn DynTaskInputsStorage,
1807 persistence: TaskPersistence,
1808 ) -> RawVc {
1809 self.native_call(native_fn, this, arg, persistence)
1810 }
1811 fn trait_call(
1812 &self,
1813 trait_method: &'static TraitMethod,
1814 this: RawVc,
1815 arg: &mut dyn DynTaskInputsStorage,
1816 inputs_resolved: InputResolution,
1817 persistence: TaskPersistence,
1818 ) -> RawVc {
1819 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1820 }
1821
1822 #[track_caller]
1823 fn run(
1824 &self,
1825 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1826 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1827 let this = self.pin();
1828 Box::pin(async move { this.run(future).await })
1829 }
1830
1831 #[track_caller]
1832 fn run_once(
1833 &self,
1834 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1835 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1836 let this = self.pin();
1837 Box::pin(async move { this.run_once(future).await })
1838 }
1839
1840 #[track_caller]
1841 fn run_once_with_reason(
1842 &self,
1843 reason: StaticOrArc<dyn InvalidationReason>,
1844 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1845 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1846 {
1847 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1848 reason_set.insert(reason);
1849 }
1850 let this = self.pin();
1851 Box::pin(async move { this.run_once(future).await })
1852 }
1853
1854 #[track_caller]
1855 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1856 self.start_once_process(future)
1857 }
1858
1859 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1860 if let Err(e) = self.compilation_events.send(event) {
1861 tracing::warn!("Failed to send compilation event: {e}");
1862 }
1863 }
1864
1865 fn get_task_name(&self, task: TaskId) -> String {
1866 self.backend.get_task_name(task, self)
1867 }
1868}
1869
1870impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1871 #[instrument(level = "info", skip_all, name = "invalidate")]
1872 fn invalidate(&self, task: TaskId) {
1873 self.backend.invalidate_task(task, self);
1874 }
1875
1876 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1877 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1878 {
1879 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1880 reason_set.insert(reason);
1881 }
1882 self.backend.invalidate_task(task, self);
1883 }
1884
1885 #[instrument(level = "info", skip_all, name = "invalidate many", fields(count = tasks.len()))]
1886 fn invalidate_many(&self, tasks: &[TaskId]) {
1887 if tasks.is_empty() {
1888 return;
1889 }
1890 self.backend.invalidate_tasks(tasks, self);
1891 }
1892
1893 #[instrument(level = "info", skip_all, name = "invalidate many", fields(count = tasks.len()))]
1894 fn invalidate_many_with_reasons(
1895 &self,
1896 tasks: &[TaskId],
1897 reasons: Vec<StaticOrArc<dyn InvalidationReason>>,
1898 ) {
1899 if tasks.is_empty() {
1900 return;
1901 }
1902 {
1904 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1905 for reason in reasons {
1906 reason_set.insert(reason);
1907 }
1908 }
1909 self.backend.invalidate_tasks(tasks, self);
1910 }
1911
1912 fn invalidate_serialization(&self, task: TaskId) {
1913 self.backend.invalidate_serialization(task, self);
1914 }
1915
1916 #[track_caller]
1917 fn try_read_task_output(
1918 &self,
1919 task: TaskId,
1920 options: ReadOutputOptions,
1921 ) -> Result<ReadOutcome<RawVc>> {
1922 if options.consistency == ReadConsistency::Eventual {
1923 debug_assert_not_in_top_level_task("read_task_output");
1924 }
1925 self.backend.try_read_task_output(
1926 task,
1927 current_task_if_available("reading Vcs"),
1928 options,
1929 self,
1930 )
1931 }
1932
1933 #[track_caller]
1934 fn try_read_task_cell(
1935 &self,
1936 task: TaskId,
1937 index: CellId,
1938 options: ReadCellOptions,
1939 ) -> Result<ReadOutcome<TypedCellContent>> {
1940 let reader = current_task_if_available("reading Vcs");
1941 self.backend
1942 .try_read_task_cell(task, index, reader, options, self)
1943 }
1944
1945 fn try_read_own_task_cell(
1946 &self,
1947 current_task: TaskId,
1948 index: CellId,
1949 ) -> Result<TypedCellContent> {
1950 self.backend
1951 .try_read_own_task_cell(current_task, index, self)
1952 }
1953
1954 #[track_caller]
1955 fn try_read_local_output(
1956 &self,
1957 execution_id: ExecutionId,
1958 local_task_id: LocalTaskId,
1959 ) -> Result<Result<RawVc, EventListener>> {
1960 debug_assert_not_in_top_level_task("read_local_output");
1961 CURRENT_TASK_STATE.with(|gts| {
1962 let gts_read = gts.read().unwrap();
1963
1964 gts_read.assert_execution_id(execution_id);
1969
1970 match gts_read.local_tasks.get(local_task_id) {
1971 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1972 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1973 }
1974 })
1975 }
1976
1977 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1978 self.backend.read_task_collectibles(
1981 task,
1982 trait_id,
1983 current_task_if_available("reading collectibles"),
1984 self,
1985 )
1986 }
1987
1988 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1989 self.try_execute_scheduled_task_inline(key)
1990 }
1991
1992 #[cfg(feature = "inline_execution_stats")]
1993 fn note_waited_for_in_progress_task(&self) {
1994 self.note_waited_for_in_progress_task()
1995 }
1996
1997 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1998 self.backend.emit_collectible(
1999 trait_type,
2000 collectible,
2001 current_task("emitting collectible"),
2002 self,
2003 );
2004 }
2005
2006 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
2007 self.backend.unemit_collectible(
2008 trait_type,
2009 collectible,
2010 count,
2011 current_task("emitting collectible"),
2012 self,
2013 );
2014 }
2015
2016 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
2017 if !collectibles.values().any(|&count| count > 0) {
2018 return;
2019 }
2020 self.backend.unemit_collectibles(
2021 trait_type,
2022 collectibles,
2023 current_task("unemitting collectibles"),
2024 self,
2025 );
2026 }
2027
2028 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
2029 self.try_read_own_task_cell(task, index)
2030 }
2031
2032 fn update_own_task_cell(
2033 &self,
2034 task: TaskId,
2035 index: CellId,
2036 content: CellContent,
2037 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
2038 content_hash: Option<CellHash>,
2039 verification_mode: VerificationMode,
2040 ) {
2041 self.backend.update_task_cell(
2042 task,
2043 index,
2044 content,
2045 updated_key_hashes,
2046 content_hash,
2047 verification_mode,
2048 self,
2049 );
2050 }
2051
2052 fn connect_task(&self, task: TaskId) {
2053 self.backend
2054 .connect_task(task, current_task_if_available("connecting task"), self);
2055 }
2056
2057 fn mark_own_task_as_finished(&self, task: TaskId) {
2058 self.backend.mark_own_task_as_finished(task, self);
2059 }
2060
2061 fn pin_task_for_gc(&self, task: TaskId) {
2062 self.backend.pin_task_for_gc(task, self);
2063 }
2064
2065 fn unpin_task_for_gc(&self, task: TaskId) {
2066 self.backend.unpin_task_for_gc(task, self);
2067 }
2068
2069 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
2072 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
2075 global_task_state
2076 .write()
2077 .unwrap()
2078 .local_tasks
2079 .register_detached();
2080 let wrapped = async move {
2081 struct DropGuard;
2083 impl Drop for DropGuard {
2084 fn drop(&mut self) {
2085 CURRENT_TASK_STATE
2086 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
2087 }
2088 }
2089 let _guard = DropGuard;
2090 fut.await;
2091 };
2092 tokio::spawn(TURBO_TASKS.scope(
2093 turbo_tasks(),
2094 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
2095 ));
2096 }
2097
2098 fn task_statistics(&self) -> &TaskStatisticsApi {
2099 self.backend.task_statistics()
2100 }
2101
2102 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
2103 let this = self.pin();
2104 Box::pin(async move {
2105 this.stop_and_wait().await;
2106 })
2107 }
2108
2109 fn subscribe_to_compilation_events(
2110 &self,
2111 event_types: Option<Vec<String>>,
2112 ) -> Receiver<Arc<dyn CompilationEvent>> {
2113 self.compilation_events.subscribe(event_types)
2114 }
2115
2116 fn is_tracking_dependencies(&self) -> bool {
2117 self.backend.is_tracking_dependencies()
2118 }
2119}
2120
2121async fn wait_for_local_tasks() {
2122 let listener =
2123 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
2124 let Some(listener) = listener else {
2125 return;
2126 };
2127 listener.await;
2128}
2129
2130pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
2131 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2132 Ok(id) => id,
2133 Err(_) => panic!(
2134 "{from} can only be used in the context of a turbo_tasks task execution or \
2135 turbo_tasks run"
2136 ),
2137 }
2138}
2139
2140pub(crate) fn current_task(from: &str) -> TaskId {
2141 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2142 Ok(Some(id)) => id,
2143 Ok(None) | Err(_) => {
2144 panic!("{from} can only be used in the context of a turbo_tasks task execution")
2145 }
2146 }
2147}
2148
2149#[track_caller]
2152pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2153 if !cfg!(debug_assertions) {
2154 return;
2155 }
2156
2157 let in_top_level = CURRENT_TASK_STATE
2158 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2159 .unwrap_or(true);
2160 if !in_top_level {
2161 panic!("{message}");
2162 }
2163}
2164
2165#[track_caller]
2166pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2167 if !cfg!(debug_assertions) {
2168 return;
2169 }
2170
2171 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2174 .try_with(|&suppressed| suppressed)
2175 .unwrap_or(false);
2176 if suppressed {
2177 return;
2178 }
2179
2180 let in_top_level = CURRENT_TASK_STATE
2181 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2182 .unwrap_or(false);
2183 if in_top_level {
2184 panic!(
2185 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2186 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2187 reads to avoid leaking inconsistent return values."
2188 );
2189 }
2190}
2191
2192pub async fn run<T: Send + 'static>(
2193 tt: Arc<dyn TurboTasksApi>,
2194 future: impl Future<Output = Result<T>> + Send + 'static,
2195) -> Result<T> {
2196 let (tx, rx) = tokio::sync::oneshot::channel();
2197
2198 tt.run(Box::pin(async move {
2199 let result = future.await?;
2200 tx.send(result)
2201 .map_err(|_| anyhow!("unable to send result"))?;
2202 Ok(())
2203 }))
2204 .await?;
2205
2206 Ok(rx.await?)
2207}
2208
2209pub async fn run_once<T: Send + 'static>(
2210 tt: Arc<dyn TurboTasksApi>,
2211 future: impl Future<Output = Result<T>> + Send + 'static,
2212) -> Result<T> {
2213 let (tx, rx) = tokio::sync::oneshot::channel();
2214
2215 tt.run_once(Box::pin(async move {
2216 let result = future.await?;
2217 tx.send(result)
2218 .map_err(|_| anyhow!("unable to send result"))?;
2219 Ok(())
2220 }))
2221 .await?;
2222
2223 Ok(rx.await?)
2224}
2225
2226pub async fn run_once_with_reason<T: Send + 'static>(
2227 tt: Arc<dyn TurboTasksApi>,
2228 reason: impl InvalidationReason,
2229 future: impl Future<Output = Result<T>> + Send + 'static,
2230) -> Result<T> {
2231 let (tx, rx) = tokio::sync::oneshot::channel();
2232
2233 tt.run_once_with_reason(
2234 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2235 Box::pin(async move {
2236 let result = future.await?;
2237 tx.send(result)
2238 .map_err(|_| anyhow!("unable to send result"))?;
2239 Ok(())
2240 }),
2241 )
2242 .await?;
2243
2244 Ok(rx.await?)
2245}
2246
2247pub fn dynamic_call(
2249 func: &'static NativeFunction,
2250 this: Option<RawVc>,
2251 arg: &mut dyn DynTaskInputsStorage,
2252 inputs_resolved: InputResolution,
2253 persistence: TaskPersistence,
2254) -> RawVc {
2255 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2256}
2257
2258pub fn trait_call(
2260 trait_method: &'static TraitMethod,
2261 this: RawVc,
2262 arg: &mut dyn DynTaskInputsStorage,
2263 inputs_resolved: InputResolution,
2264 persistence: TaskPersistence,
2265) -> RawVc {
2266 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2267}
2268
2269pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2270 TURBO_TASKS.with(|arc| arc.clone())
2271}
2272
2273pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2274 TURBO_TASKS.with(Arc::downgrade)
2275}
2276
2277pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2278 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2279}
2280
2281pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2282 TURBO_TASKS.with(|arc| func(arc))
2283}
2284
2285pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2286 TURBO_TASKS.sync_scope(tt, f)
2287}
2288
2289pub(crate) fn ensure_turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2293 let is_current = TURBO_TASKS
2294 .try_with(|current| std::ptr::addr_eq(Arc::as_ptr(current), Arc::as_ptr(&tt)))
2295 .unwrap_or(false);
2296 if is_current {
2297 f()
2298 } else {
2299 TURBO_TASKS.sync_scope(tt, f)
2300 }
2301}
2302
2303pub fn turbo_tasks_future_scope<T>(
2304 tt: Arc<dyn TurboTasksApi>,
2305 f: impl Future<Output = T>,
2306) -> impl Future<Output = T> {
2307 TURBO_TASKS.scope(tt, f)
2308}
2309
2310pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2315 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2316}
2317
2318pub fn mark_finished() {
2321 with_turbo_tasks(|tt| {
2322 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2323 });
2324}
2325
2326pub fn get_serialization_invalidator() -> SerializationInvalidator {
2332 CURRENT_TASK_STATE.with(|cell| {
2333 let CurrentTaskState {
2334 task_id,
2335 #[cfg(feature = "verify_determinism")]
2336 stateful,
2337 ..
2338 } = &mut *cell.write().unwrap();
2339 #[cfg(feature = "verify_determinism")]
2340 {
2341 *stateful = true;
2342 }
2343 let Some(task_id) = *task_id else {
2344 panic!(
2345 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2346 task execution"
2347 );
2348 };
2349 SerializationInvalidator::new(task_id)
2350 })
2351}
2352
2353pub fn mark_invalidator() {
2354 CURRENT_TASK_STATE.with(|cell| {
2355 let CurrentTaskState {
2356 has_invalidator, ..
2357 } = &mut *cell.write().unwrap();
2358 *has_invalidator = true;
2359 })
2360}
2361
2362pub fn mark_stateful() {
2368 #[cfg(feature = "verify_determinism")]
2369 {
2370 CURRENT_TASK_STATE.with(|cell| {
2371 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2372 *stateful = true;
2373 })
2374 }
2375 }
2377
2378pub fn mark_top_level_task() {
2382 if cfg!(debug_assertions) {
2383 CURRENT_TASK_STATE.with(|cell| {
2384 cell.write().unwrap().in_top_level_task = true;
2385 })
2386 }
2387}
2388
2389pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2400 if cfg!(debug_assertions) {
2401 CURRENT_TASK_STATE.with(|cell| {
2402 cell.write().unwrap().in_top_level_task = false;
2403 })
2404 }
2405}
2406
2407pub fn prevent_gc() {
2415 if let Some(task) = current_task_if_available("prevent_gc") {
2416 with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2417 }
2418}
2419
2420pub struct GcRoot<T: ?Sized> {
2422 tt: Arc<dyn TurboTasksApi>,
2423 vc: OperationVc<T>,
2424}
2425
2426impl<T: ?Sized> GcRoot<T> {
2427 pub fn pin(tt: Arc<dyn TurboTasksApi>, vc: OperationVc<T>) -> Self {
2429 tt.pin_task_for_gc(vc.task_id());
2430 Self { tt, vc }
2431 }
2432}
2433
2434impl<T: ?Sized> Deref for GcRoot<T> {
2437 type Target = OperationVc<T>;
2438
2439 fn deref(&self) -> &Self::Target {
2440 &self.vc
2441 }
2442}
2443
2444impl<T: ?Sized> Clone for GcRoot<T> {
2445 fn clone(&self) -> Self {
2446 Self::pin(self.tt.clone(), self.vc)
2447 }
2448}
2449
2450impl<T: ?Sized> Drop for GcRoot<T> {
2451 fn drop(&mut self) {
2452 self.tt.unpin_task_for_gc(self.vc.task_id());
2453 }
2454}
2455
2456impl<T: ?Sized> Debug for GcRoot<T> {
2457 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2458 f.debug_struct("GcRoot").field("vc", &self.vc).finish()
2459 }
2460}
2461
2462impl<T: ?Sized> PartialEq for GcRoot<T> {
2463 fn eq(&self, other: &Self) -> bool {
2466 self.vc == other.vc
2467 }
2468}
2469
2470impl<T: ?Sized> Eq for GcRoot<T> {}
2471
2472impl<T: ?Sized> Hash for GcRoot<T> {
2473 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2476 self.vc.hash(state);
2477 }
2478}
2479
2480impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2484 fn borrow(&self) -> &OperationVc<T> {
2485 &self.vc
2486 }
2487}
2488
2489unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2492
2493unsafe impl<T: NonLocalValue + ?Sized> NonLocalValue for GcRoot<T> {}
2496
2497pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2498 with_turbo_tasks(|tt| {
2499 let raw_vc = collectible.node.node;
2500 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2501 })
2502}
2503
2504pub(crate) async fn read_task_output(
2505 this: &dyn TurboTasksApi,
2506 id: TaskId,
2507 options: ReadOutputOptions,
2508) -> Result<RawVc> {
2509 loop {
2510 match this.try_read_task_output(id, options)? {
2511 ReadOutcome::Value(result) => return Ok(result),
2512 ReadOutcome::Scheduled(listener) => {
2513 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2515 continue;
2516 }
2517 listener.await
2518 }
2519 ReadOutcome::InProgress(listener) => {
2520 #[cfg(feature = "inline_execution_stats")]
2522 this.note_waited_for_in_progress_task();
2523 listener.await
2524 }
2525 }
2526 }
2527}
2528
2529#[derive(Clone, Copy)]
2535pub struct CurrentCellRef {
2536 current_task: TaskId,
2537 index: CellId,
2538}
2539
2540type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2541
2542type CellUpdate = (
2545 SharedReference,
2546 Option<SmallVec<[u64; 2]>>,
2547 Option<CellHash>,
2548);
2549
2550type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2553
2554impl CurrentCellRef {
2555 fn conditional_update<T>(
2557 &self,
2558 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2559 ) where
2560 T: VcValueType,
2561 {
2562 let mut functor = Some(functor);
2565 self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2566 let functor = functor.take().expect("functor is called at most once");
2567 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2568 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2569 Some((
2570 SharedReference::new(triomphe::Arc::new(new_value)),
2571 updated_key_hashes,
2572 content_hash,
2573 ))
2574 })
2575 }
2576
2577 fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2584 let tt = turbo_tasks();
2585 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2586 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2587 if let Some((update, updated_key_hashes, content_hash)) = update {
2588 tt.update_own_task_cell(
2589 self.current_task,
2590 self.index,
2591 CellContent(Some(update)),
2592 updated_key_hashes,
2593 content_hash,
2594 VerificationMode::EqualityCheck,
2595 )
2596 }
2597 }
2598
2599 pub fn compare_and_update<T>(&self, new_value: T)
2634 where
2635 T: PartialEq + VcValueType,
2636 {
2637 self.conditional_update(|old_value| {
2638 if let Some(old_value) = old_value
2639 && old_value == &new_value
2640 {
2641 return None;
2642 }
2643 Some((new_value, None, None))
2644 });
2645 }
2646
2647 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2655 where
2656 T: VcValueType + PartialEq,
2657 {
2658 let mut new_shared_reference = Some(new_shared_reference);
2659 self.conditional_update_with_shared_reference(&mut |old_sr| {
2660 let new_shared_reference = new_shared_reference
2661 .take()
2662 .expect("functor is called at most once");
2663 if let Some(old_sr) = old_sr {
2664 let old_value = extract_sr_value::<T>(old_sr);
2665 let new_value = extract_sr_value::<T>(&new_shared_reference);
2666 if old_value == new_value {
2667 return None;
2668 }
2669 }
2670 Some((new_shared_reference, None, None))
2671 });
2672 }
2673
2674 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2683 where
2684 T: PartialEq + DeterministicHash + VcValueType,
2685 {
2686 self.conditional_update(|old_value| {
2687 if let Some(old_value) = old_value
2688 && old_value == &new_value
2689 {
2690 return None;
2691 }
2692 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2693
2694 Some((new_value, None, Some(content_hash)))
2695 });
2696 }
2697
2698 pub fn hashed_compare_and_update_with_shared_reference<T>(
2704 &self,
2705 new_shared_reference: SharedReference,
2706 ) where
2707 T: VcValueType + PartialEq + DeterministicHash,
2708 {
2709 let mut new_shared_reference = Some(new_shared_reference);
2710 self.conditional_update_with_shared_reference(&mut move |old_sr| {
2711 let new_shared_reference = new_shared_reference
2712 .take()
2713 .expect("functor is called at most once");
2714 if let Some(old_sr) = old_sr {
2715 let old_value = extract_sr_value::<T>(old_sr);
2716 let new_value = extract_sr_value::<T>(&new_shared_reference);
2717 if old_value == new_value {
2718 return None;
2719 }
2720 }
2721 let content_hash =
2722 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2723 Some((new_shared_reference, None, Some(content_hash)))
2724 });
2725 }
2726
2727 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2729 where
2730 T: PartialEq + VcValueType,
2731 VcReadTarget<T>: KeyedEq,
2732 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2733 {
2734 self.conditional_update(|old_value| {
2735 let Some(old_value) = old_value else {
2736 return Some((new_value, None, None));
2737 };
2738 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2739 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2740 let updated_keys = old_value.different_keys(new_value_ref);
2741 if updated_keys.is_empty() {
2742 return None;
2743 }
2744 let updated_key_hashes = updated_keys
2746 .into_iter()
2747 .map(|key| FxBuildHasher.hash_one(key))
2748 .collect();
2749 Some((new_value, Some(updated_key_hashes), None))
2750 });
2751 }
2752
2753 pub fn keyed_compare_and_update_with_shared_reference<T>(
2756 &self,
2757 new_shared_reference: SharedReference,
2758 ) where
2759 T: VcValueType + PartialEq,
2760 VcReadTarget<T>: KeyedEq,
2761 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2762 {
2763 let mut new_shared_reference = Some(new_shared_reference);
2764 self.conditional_update_with_shared_reference(&mut |old_sr| {
2765 let new_shared_reference = new_shared_reference
2766 .take()
2767 .expect("functor is called at most once");
2768 let Some(old_sr) = old_sr else {
2769 return Some((new_shared_reference, None, None));
2770 };
2771 let old_value = extract_sr_value::<T>(old_sr);
2772 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2773 let new_value = extract_sr_value::<T>(&new_shared_reference);
2774 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2775 let updated_keys = old_value.different_keys(new_value);
2776 if updated_keys.is_empty() {
2777 return None;
2778 }
2779 let updated_key_hashes = updated_keys
2781 .into_iter()
2782 .map(|key| FxBuildHasher.hash_one(key))
2783 .collect();
2784 Some((new_shared_reference, Some(updated_key_hashes), None))
2785 });
2786 }
2787
2788 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2790 where
2791 T: VcValueType,
2792 {
2793 let tt = turbo_tasks();
2794 tt.update_own_task_cell(
2795 self.current_task,
2796 self.index,
2797 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2798 None,
2799 None,
2800 verification_mode,
2801 )
2802 }
2803
2804 pub fn update_with_shared_reference(
2812 &self,
2813 shared_ref: SharedReference,
2814 verification_mode: VerificationMode,
2815 ) {
2816 let tt = turbo_tasks();
2817 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2818 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2819 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2820 shared_ref_exp != shared_ref
2822 } else {
2823 true
2824 }
2825 } else {
2826 true
2827 };
2828 if update {
2829 tt.update_own_task_cell(
2830 self.current_task,
2831 self.index,
2832 CellContent(Some(shared_ref)),
2833 None,
2834 None,
2835 verification_mode,
2836 )
2837 }
2838 }
2839}
2840
2841impl From<CurrentCellRef> for RawVc {
2842 fn from(cell: CurrentCellRef) -> Self {
2843 RawVc::task_cell(cell.current_task, cell.index)
2844 }
2845}
2846
2847fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2848 sr.0.downcast_ref::<T>()
2849 .expect("cannot update SharedReference of different type")
2850}
2851
2852pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2853 find_cell_by_id(T::get_value_type_id())
2854}
2855
2856pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2857 CURRENT_TASK_STATE.with(|ts| {
2858 let current_task = current_task("celling turbo_tasks values");
2859 let mut ts = ts.write().unwrap();
2860 let map = ts.cell_counters.as_mut().unwrap();
2861 let current_index = map.entry(ty).or_default();
2862 let index = *current_index;
2863 assert!(
2864 index <= CellId::MAX_CELL_INDEX,
2865 "task allocated more than {} cells of a single type",
2866 CellId::MAX_CELL_INDEX as u64 + 1,
2867 );
2868 *current_index += 1;
2869 CurrentCellRef {
2870 current_task,
2871 index: CellId::new(ty, index),
2872 }
2873 })
2874}
2875
2876pub(crate) async fn read_local_output(
2877 this: &dyn TurboTasksApi,
2878 execution_id: ExecutionId,
2879 local_task_id: LocalTaskId,
2880) -> Result<RawVc> {
2881 loop {
2882 match this.try_read_local_output(execution_id, local_task_id)? {
2883 Ok(raw_vc) => return Ok(raw_vc),
2884 Err(event_listener) => {
2885 if execute_read_target_inline(
2888 this,
2889 ScheduleKey::current_local_task(execution_id, local_task_id),
2890 ) {
2891 continue;
2892 }
2893 event_listener.await
2894 }
2895 }
2896 }
2897}
2898
2899#[cfg(test)]
2900mod tests {
2901 use super::*;
2902
2903 #[test]
2904 fn test_inline_execution_depth_guard_restores_depth() {
2905 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2906 {
2907 let _outer = InlineExecutionDepthGuard::enter();
2908 {
2909 let _inner = InlineExecutionDepthGuard::enter();
2910 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2911 }
2912 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2913 }
2914 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2915 }
2916
2917 #[test]
2918 fn test_inline_depth_cap() {
2919 assert!(inline_execution_allowed(), "nothing is nested yet");
2920 let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2921 .map(|_| InlineExecutionDepthGuard::enter())
2922 .collect::<Vec<_>>();
2923 assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2924 assert!(
2925 !inline_execution_allowed(),
2926 "at the nesting cap reads wait for a worker instead of executing inline"
2927 );
2928
2929 guards.pop();
2931 assert!(inline_execution_allowed());
2932 }
2933
2934 #[tokio::test]
2935 async fn test_poll_once_or_spawn_completed_execution() {
2936 assert!(
2937 poll_once_or_spawn(async {}),
2938 "a future that completes on the first poll is executed inline"
2939 );
2940 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2941 }
2942
2943 #[tokio::test]
2944 async fn test_poll_once_or_spawn_pending_execution() {
2945 let (tx, rx) = tokio::sync::oneshot::channel();
2946 let done = Arc::new(AtomicBool::new(false));
2947 let done_in_task = done.clone();
2948 assert!(
2949 !poll_once_or_spawn(async move {
2950 tokio::task::yield_now().await;
2952 done_in_task.store(true, Ordering::SeqCst);
2953 let _ = tx.send(());
2954 }),
2955 "a future that yields is not completed inline"
2956 );
2957 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2958
2959 rx.await.unwrap();
2961 assert!(done.load(Ordering::SeqCst));
2962 }
2963}