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(
160 &self,
161 execution_id: ExecutionId,
162 local_task_id: LocalTaskId,
163 ) -> Result<Result<RawVc, EventListener>>;
164
165 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
166
167 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
173
174 #[cfg(feature = "inline_execution_stats")]
177 fn note_waited_for_in_progress_task(&self);
178
179 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
180 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
181 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
182
183 fn try_read_own_task_cell(
186 &self,
187 current_task: TaskId,
188 index: CellId,
189 ) -> Result<TypedCellContent>;
190
191 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
192 fn update_own_task_cell(
193 &self,
194 task: TaskId,
195 index: CellId,
196 content: CellContent,
197 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
198 content_hash: Option<CellHash>,
199 verification_mode: VerificationMode,
200 );
201 fn mark_own_task_as_finished(&self, task: TaskId);
202
203 fn pin_task_for_gc(&self, task: TaskId);
206
207 fn unpin_task_for_gc(&self, task: TaskId);
209
210 fn connect_task(&self, task: TaskId);
211
212 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
217
218 fn task_statistics(&self) -> &TaskStatisticsApi;
219
220 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
221
222 fn subscribe_to_compilation_events(
223 &self,
224 event_types: Option<Vec<String>>,
225 ) -> Receiver<Arc<dyn CompilationEvent>>;
226
227 fn is_tracking_dependencies(&self) -> bool;
229}
230
231pub struct Unused<T> {
233 inner: T,
234}
235
236impl<T> Unused<T> {
237 pub unsafe fn new_unchecked(inner: T) -> Self {
243 Self { inner }
244 }
245
246 pub unsafe fn get_unchecked(&self) -> &T {
252 &self.inner
253 }
254
255 pub fn into(self) -> T {
257 self.inner
258 }
259}
260
261#[allow(clippy::manual_non_exhaustive)]
262pub struct UpdateInfo {
263 pub duration: Duration,
264 pub tasks: usize,
265 pub reasons: InvalidationReasonSet,
266 #[allow(dead_code)]
267 placeholder_for_future_fields: (),
268}
269
270#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
271pub enum TaskPersistence {
272 Persistent,
274
275 Transient,
282}
283
284impl Display for TaskPersistence {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 match self {
287 TaskPersistence::Persistent => write!(f, "persistent"),
288 TaskPersistence::Transient => write!(f, "transient"),
289 }
290 }
291}
292
293#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
296pub enum InputResolution {
297 Resolved,
300 Unresolved,
302}
303
304impl InputResolution {
305 #[inline]
306 pub fn from_is_resolved(is_resolved: bool) -> Self {
307 if is_resolved {
308 Self::Resolved
309 } else {
310 Self::Unresolved
311 }
312 }
313
314 #[inline]
315 pub fn is_resolved(self) -> bool {
316 matches!(self, Self::Resolved)
317 }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
321pub enum ReadConsistency {
322 #[default]
325 Eventual,
326 Strong,
331}
332
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334pub enum ReadCellTracking {
335 Tracked {
337 key: Option<u64>,
339 },
340 TrackOnlyError,
345 Untracked,
350}
351
352impl ReadCellTracking {
353 pub fn should_track(&self, is_err: bool) -> bool {
354 match self {
355 ReadCellTracking::Tracked { .. } => true,
356 ReadCellTracking::TrackOnlyError => is_err,
357 ReadCellTracking::Untracked => false,
358 }
359 }
360
361 pub fn key(&self) -> Option<u64> {
362 match self {
363 ReadCellTracking::Tracked { key } => *key,
364 ReadCellTracking::TrackOnlyError => None,
365 ReadCellTracking::Untracked => None,
366 }
367 }
368}
369
370impl Default for ReadCellTracking {
371 fn default() -> Self {
372 ReadCellTracking::Tracked { key: None }
373 }
374}
375
376impl Display for ReadCellTracking {
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 match self {
379 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
380 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
381 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
382 ReadCellTracking::Untracked => write!(f, "untracked"),
383 }
384 }
385}
386
387#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
388pub enum ReadTracking {
389 #[default]
391 Tracked,
392 TrackOnlyError,
397 Untracked,
402}
403
404impl ReadTracking {
405 pub fn should_track(&self, is_err: bool) -> bool {
406 match self {
407 ReadTracking::Tracked => true,
408 ReadTracking::TrackOnlyError => is_err,
409 ReadTracking::Untracked => false,
410 }
411 }
412}
413
414impl Display for ReadTracking {
415 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416 match self {
417 ReadTracking::Tracked => write!(f, "tracked"),
418 ReadTracking::TrackOnlyError => write!(f, "track only error"),
419 ReadTracking::Untracked => write!(f, "untracked"),
420 }
421 }
422}
423
424#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
425pub enum TaskPriority {
426 #[default]
427 Initial,
428 Invalidation {
429 priority: Reverse<u32>,
430 },
431 Recomputation,
432}
433
434impl TaskPriority {
435 pub fn invalidation(priority: u32) -> Self {
436 Self::Invalidation {
437 priority: Reverse(priority),
438 }
439 }
440
441 pub fn initial() -> Self {
442 Self::Initial
443 }
444
445 pub fn leaf() -> Self {
446 Self::Invalidation {
447 priority: Reverse(0),
448 }
449 }
450
451 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
452 match self {
453 TaskPriority::Initial => parent_priority,
454 TaskPriority::Invalidation { priority } => {
455 if let TaskPriority::Invalidation {
456 priority: parent_priority,
457 } = parent_priority
458 && priority.0 < parent_priority.0
459 {
460 Self::Invalidation {
461 priority: Reverse(parent_priority.0.saturating_add(1)),
462 }
463 } else {
464 *self
465 }
466 }
467 TaskPriority::Recomputation => TaskPriority::Recomputation,
468 }
469 }
470}
471
472impl Display for TaskPriority {
473 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474 match self {
475 TaskPriority::Initial => write!(f, "initial"),
476 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
477 TaskPriority::Recomputation => write!(f, "recomputation"),
478 }
479 }
480}
481
482enum ScheduledTask {
483 Task {
484 task_id: TaskId,
485 span: Span,
486 },
487 LocalTask {
488 ty: LocalTaskSpec,
489 persistence: TaskPersistence,
490 parent_task: Option<TaskId>,
493 execution_id: ExecutionId,
494 local_task_id: LocalTaskId,
495 global_task_state: CurrentTaskStateHandle,
496 span: Span,
497 },
498}
499
500#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
504pub enum ScheduleKey {
505 Task(TaskId),
507 LocalTask(Option<TaskId>, ExecutionId, LocalTaskId),
516}
517
518impl ScheduleKey {
519 pub(crate) fn current_local_task(
521 execution_id: ExecutionId,
522 local_task_id: LocalTaskId,
523 ) -> Self {
524 let parent_task = CURRENT_TASK_STATE.with(|ts| ts.current_task_id());
525 Self::LocalTask(parent_task, execution_id, local_task_id)
526 }
527}
528
529impl Claimable for ScheduledTask {
530 type Key = ScheduleKey;
531
532 fn claim_key(&self) -> ScheduleKey {
533 match self {
534 ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
535 ScheduledTask::LocalTask {
536 parent_task,
537 execution_id,
538 local_task_id,
539 ..
540 } => ScheduleKey::LocalTask(*parent_task, *execution_id, *local_task_id),
541 }
542 }
543}
544
545#[cfg(feature = "inline_execution_stats")]
546use std::sync::atomic::AtomicU64;
547
548#[cfg(feature = "inline_execution_stats")]
551#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
552pub struct InlineExecutionStats {
553 pub queued: u64,
555 pub claim_attempted: u64,
557 pub claim_completed: u64,
559 pub claim_yielded: u64,
561 pub claim_failed: u64,
563 pub waited_in_progress: u64,
565}
566
567#[derive(Default)]
573struct InlineExecutionCounters {
574 #[cfg(feature = "inline_execution_stats")]
575 claim_attempted: AtomicU64,
576 #[cfg(feature = "inline_execution_stats")]
577 claim_completed: AtomicU64,
578 #[cfg(feature = "inline_execution_stats")]
579 claim_yielded: AtomicU64,
580 #[cfg(feature = "inline_execution_stats")]
581 claim_failed: AtomicU64,
582 #[cfg(feature = "inline_execution_stats")]
583 waited_in_progress: AtomicU64,
584}
585
586impl InlineExecutionCounters {
587 #[inline]
589 fn claim_attempted(&self) {
590 #[cfg(feature = "inline_execution_stats")]
591 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
592 }
593
594 #[inline]
596 fn claim_completed(&self) {
597 #[cfg(feature = "inline_execution_stats")]
598 self.claim_completed.fetch_add(1, Ordering::Relaxed);
599 }
600
601 #[inline]
603 fn claim_yielded(&self) {
604 #[cfg(feature = "inline_execution_stats")]
605 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
606 }
607
608 #[inline]
610 fn claim_failed(&self) {
611 #[cfg(feature = "inline_execution_stats")]
612 self.claim_failed.fetch_add(1, Ordering::Relaxed);
613 }
614
615 #[cfg(feature = "inline_execution_stats")]
617 #[inline]
618 fn waited_in_progress(&self) {
619 self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
620 }
621}
622
623#[cfg(feature = "inline_execution_stats")]
625pub(crate) fn inline_stats_requested() -> bool {
626 static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
627 std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
628 });
629 *REQUESTED
630}
631
632const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
635
636thread_local! {
637 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
639}
640
641fn inline_execution_allowed() -> bool {
643 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
644}
645
646struct InlineExecutionDepthGuard;
648
649impl InlineExecutionDepthGuard {
650 fn enter() -> Self {
651 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
652 Self
653 }
654}
655
656impl Drop for InlineExecutionDepthGuard {
657 fn drop(&mut self) {
658 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
659 }
660}
661
662fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
665 let _depth_guard = InlineExecutionDepthGuard::enter();
666 let span_slot = InlineExecutionSpanSlot::default();
667 let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
668 match future
672 .as_mut()
673 .poll(&mut Context::from_waker(Waker::noop()))
674 {
675 Poll::Ready(()) => {
676 span_slot.record("complete");
677 true
678 }
679 Poll::Pending => {
680 span_slot.record("partial");
681 tokio::task::spawn(future);
682 false
683 }
684 }
685}
686
687pub(crate) fn execute_read_target_inline(
689 turbo_tasks: &dyn TurboTasksApi,
690 key: ScheduleKey,
691) -> bool {
692 if !inline_execution_allowed() {
693 return false;
695 }
696 turbo_tasks.try_execute_scheduled_task_inline(key)
697}
698
699pub struct TurboTasks<B: Backend + 'static> {
700 this: Weak<Self>,
701 backend: B,
702 execution_id_factory: IdFactory<ExecutionId>,
703 stopped: AtomicBool,
704 currently_scheduled_foreground_jobs: AtomicUsize,
705 currently_scheduled_background_jobs: AtomicUsize,
706 scheduled_tasks: AtomicUsize,
707 inline_counters: InlineExecutionCounters,
710 priority_runner:
711 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
712 start: Mutex<Option<Instant>>,
713 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
714 event_foreground_start: Event,
716 event_foreground_done: Event,
719 event_background_done: Event,
721 compilation_events: CompilationEventQueue,
722}
723
724struct CurrentTaskState {
733 task_id: Option<TaskId>,
734 execution_id: ExecutionId,
735 priority: TaskPriority,
736
737 #[cfg(feature = "verify_determinism")]
740 stateful: bool,
741
742 has_invalidator: bool,
744
745 in_top_level_task: bool,
748
749 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
754
755 local_tasks: LocalTaskTracker,
758}
759
760impl CurrentTaskState {
761 fn new(
762 task_id: TaskId,
763 execution_id: ExecutionId,
764 priority: TaskPriority,
765 in_top_level_task: bool,
766 ) -> Self {
767 Self {
768 task_id: Some(task_id),
769 execution_id,
770 priority,
771 #[cfg(feature = "verify_determinism")]
772 stateful: false,
773 has_invalidator: false,
774 in_top_level_task,
775 cell_counters: Some(AutoMap::default()),
776 local_tasks: LocalTaskTracker::new(),
777 }
778 }
779
780 fn new_temporary(
781 execution_id: ExecutionId,
782 priority: TaskPriority,
783 in_top_level_task: bool,
784 ) -> Self {
785 Self {
786 task_id: None,
787 execution_id,
788 priority,
789 #[cfg(feature = "verify_determinism")]
790 stateful: false,
791 has_invalidator: false,
792 in_top_level_task,
793 cell_counters: None,
794 local_tasks: LocalTaskTracker::new(),
795 }
796 }
797
798 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
799 if self.execution_id != expected_execution_id {
800 panic!(
801 "Local tasks can only be scheduled/awaited within the same execution of the \
802 parent task that created them"
803 );
804 }
805 }
806}
807
808#[derive(Clone)]
812struct CurrentTaskStateHandle {
813 inner: Arc<CurrentTaskStateInner>,
814}
815
816struct CurrentTaskStateInner {
817 current_task_id: Option<TaskId>,
818 state: RwLock<CurrentTaskState>,
819}
820
821impl CurrentTaskStateHandle {
822 fn new(state: CurrentTaskState) -> Self {
823 Self {
824 inner: Arc::new(CurrentTaskStateInner {
825 current_task_id: state.task_id,
826 state: RwLock::new(state),
827 }),
828 }
829 }
830
831 fn current_task_id(&self) -> Option<TaskId> {
832 self.inner.current_task_id
833 }
834}
835
836impl Deref for CurrentTaskStateHandle {
837 type Target = RwLock<CurrentTaskState>;
838
839 fn deref(&self) -> &Self::Target {
840 &self.inner.state
841 }
842}
843
844task_local! {
846 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
848
849 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
850
851 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
856
857 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
860}
861
862#[derive(Clone, Default)]
873struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
874
875impl InlineExecutionSpanSlot {
876 fn set(span: &Span) {
878 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
879 *slot.0.lock().unwrap() = Some(span.clone());
880 });
881 }
882
883 fn record(&self, outcome: &'static str) {
887 if let Some(span) = self.0.lock().unwrap().as_ref() {
888 span.record("inline_execution", outcome);
889 }
890 }
891}
892
893impl<B: Backend + 'static> TurboTasks<B> {
894 pub fn new(backend: B) -> Arc<Self> {
900 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
901 let this = Arc::new_cyclic(|this| Self {
902 this: this.clone(),
903 backend,
904 execution_id_factory,
905 stopped: AtomicBool::new(false),
906 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
907 currently_scheduled_background_jobs: AtomicUsize::new(0),
908 scheduled_tasks: AtomicUsize::new(0),
909 inline_counters: InlineExecutionCounters::default(),
910 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
911 start: Default::default(),
912 aggregated_update: Default::default(),
913 event_foreground_done: Event::new(|| {
914 || "TurboTasks::event_foreground_done".to_string()
915 }),
916 event_foreground_start: Event::new(|| {
917 || "TurboTasks::event_foreground_start".to_string()
918 }),
919 event_background_done: Event::new(|| {
920 || "TurboTasks::event_background_done".to_string()
921 }),
922 compilation_events: CompilationEventQueue::default(),
923 });
924 this.backend.startup(&*this);
925 this
926 }
927
928 pub fn pin(&self) -> Arc<Self> {
929 self.this.upgrade().unwrap()
930 }
931
932 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
934 where
935 T: ?Sized,
936 F: Fn() -> Fut + Send + Sync + Clone + 'static,
937 Fut: Future<Output = Result<Vc<T>>> + Send,
938 {
939 let id = self.backend.create_transient_task(
940 TransientTaskType::Root(Box::new(move || {
941 let functor = functor.clone();
942 Box::pin(async move {
943 mark_top_level_task();
944 let raw_vc = functor().await?.node;
945 raw_vc.to_non_local().await
946 })
947 })),
948 self,
949 );
950 self.schedule(id, TaskPriority::initial());
951 id
952 }
953
954 pub fn dispose_root_task(&self, task_id: TaskId) {
955 self.backend.dispose_root_task(task_id, self);
956 }
957
958 pub fn pin_task_for_gc(&self, task_id: TaskId) {
962 self.backend.pin_task_for_gc(task_id, self);
963 }
964
965 pub fn unpin_task_for_gc(&self, task_id: TaskId) {
967 self.backend.unpin_task_for_gc(task_id, self);
968 }
969
970 #[track_caller]
974 fn spawn_once_task<T, Fut>(&self, future: Fut)
975 where
976 T: ?Sized,
977 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
978 {
979 let id = self.backend.create_transient_task(
980 TransientTaskType::Once(Box::pin(async move {
981 mark_top_level_task();
982 let raw_vc = future.await?.node;
983 raw_vc.to_non_local().await
984 })),
985 self,
986 );
987 self.schedule(id, TaskPriority::initial());
988 }
989
990 pub async fn run_once<T: Send + 'static>(
1013 &self,
1014 future: impl Future<Output = Result<T>> + Send + 'static,
1015 ) -> Result<T> {
1016 let (tx, rx) = tokio::sync::oneshot::channel();
1017 self.spawn_once_task(async move {
1018 mark_top_level_task();
1019 let result = future.await;
1020 tx.send(result)
1021 .map_err(|_| anyhow!("unable to send result"))?;
1022 Ok(Completion::new())
1023 });
1024
1025 rx.await?
1026 }
1027
1028 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
1034 pub async fn run<T: Send + 'static>(
1035 &self,
1036 future: impl Future<Output = Result<T>> + Send + 'static,
1037 ) -> Result<T, TurboTasksExecutionError> {
1038 self.begin_foreground_job();
1039 let execution_id = self.execution_id_factory.wrapping_get();
1041 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
1042 execution_id,
1043 TaskPriority::initial(),
1044 true, ));
1046
1047 let result = TURBO_TASKS
1048 .scope(
1049 self.pin(),
1050 CURRENT_TASK_STATE.scope(current_task_state, async {
1051 let result = CaptureFuture::new(future).await;
1052
1053 wait_for_local_tasks().await;
1055
1056 match result {
1057 Ok(Ok(value)) => Ok(value),
1058 Ok(Err(err)) => Err(err.into()),
1059 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1060 }
1061 }),
1062 )
1063 .await;
1064 self.finish_foreground_job();
1065 result
1066 }
1067
1068 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1069 let this = self.pin();
1070 tokio::spawn(async move {
1071 this.pin()
1072 .run_once(async move {
1073 this.finish_foreground_job();
1074 future.await;
1075 this.begin_foreground_job();
1076 Ok(())
1077 })
1078 .await
1079 .unwrap()
1080 });
1081 }
1082
1083 pub(crate) fn native_call(
1084 &self,
1085 native_fn: &'static NativeFunction,
1086 this: Option<RawVc>,
1087 arg: &mut dyn DynTaskInputsStorage,
1088 persistence: TaskPersistence,
1089 ) -> RawVc {
1090 RawVc::task_output(self.backend.get_or_create_task(
1091 native_fn,
1092 this,
1093 arg,
1094 current_task_if_available("turbo_function calls"),
1095 persistence,
1096 self,
1097 ))
1098 }
1099
1100 pub fn dynamic_call(
1101 &self,
1102 native_fn: &'static NativeFunction,
1103 this: Option<RawVc>,
1104 arg: &mut dyn DynTaskInputsStorage,
1105 inputs_resolved: InputResolution,
1106 persistence: TaskPersistence,
1107 ) -> RawVc {
1108 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1109 return self.native_call(native_fn, this, arg, persistence);
1110 }
1111 let arg = arg.take_box();
1113 let task_type = LocalTaskSpec {
1114 task_type: LocalTaskType::ResolveNative { native_fn },
1115 this,
1116 arg,
1117 };
1118 self.schedule_local_task(task_type, persistence)
1119 }
1120
1121 pub fn trait_call(
1122 &self,
1123 trait_method: &'static TraitMethod,
1124 this: RawVc,
1125 arg: &mut dyn DynTaskInputsStorage,
1126 inputs_resolved: InputResolution,
1127 persistence: TaskPersistence,
1128 ) -> RawVc {
1129 if let Some((_, cell_id)) = this.as_task_cell() {
1133 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1134 Some(native_fn) => {
1135 if let Some(filter) = native_fn.arg_meta.filter_owned {
1136 let (resolved, mut arg) = (filter)(arg);
1137 return self.dynamic_call(
1138 native_fn,
1139 Some(this),
1140 &mut arg,
1141 resolved,
1142 persistence,
1143 );
1144 } else {
1145 return self.dynamic_call(
1146 native_fn,
1147 Some(this),
1148 arg,
1149 inputs_resolved,
1150 persistence,
1151 );
1152 }
1153 }
1154 None => {
1155 }
1159 }
1160 }
1161
1162 let task_type = LocalTaskSpec {
1164 task_type: LocalTaskType::ResolveTrait { trait_method },
1165 this: Some(this),
1166 arg: arg.take_box(),
1167 };
1168
1169 self.schedule_local_task(task_type, persistence)
1170 }
1171
1172 #[track_caller]
1173 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1174 self.begin_foreground_job();
1175 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1176
1177 let task = ScheduledTask::Task {
1178 task_id,
1179 span: Span::current(),
1180 };
1181 self.priority_runner.schedule(&self.pin(), task, priority);
1182 }
1183
1184 #[track_caller]
1187 pub fn schedule_many(&self, tasks: &[(TaskId, TaskPriority)]) {
1188 if tasks.is_empty() {
1189 return;
1190 }
1191 self.begin_foreground_jobs(tasks.len());
1192 self.scheduled_tasks
1193 .fetch_add(tasks.len(), Ordering::AcqRel);
1194
1195 let span = Span::current();
1196 self.priority_runner.schedule_many(
1197 &self.pin(),
1198 tasks.iter().map(|&(task_id, priority)| {
1199 (
1200 ScheduledTask::Task {
1201 task_id,
1202 span: span.clone(),
1203 },
1204 priority,
1205 )
1206 }),
1207 );
1208 }
1209
1210 fn schedule_local_task(
1211 &self,
1212 ty: LocalTaskSpec,
1213 persistence: TaskPersistence,
1215 ) -> RawVc {
1216 let task_type = ty.task_type;
1217 let (global_task_state, parent_task, execution_id, priority, local_task_id) =
1218 CURRENT_TASK_STATE.with(|gts| {
1219 let mut gts_write = gts.write().unwrap();
1220 let local_task_id = gts_write.local_tasks.create(task_type);
1221 (
1222 gts.clone(),
1223 gts_write.task_id,
1224 gts_write.execution_id,
1225 gts_write.priority,
1226 local_task_id,
1227 )
1228 });
1229
1230 let task = ScheduledTask::LocalTask {
1231 ty,
1232 persistence,
1233 parent_task,
1234 execution_id,
1235 local_task_id,
1236 global_task_state,
1237 span: Span::current(),
1238 };
1239 self.priority_runner.schedule(&self.pin(), task, priority);
1240
1241 RawVc::local_output(execution_id, local_task_id, persistence)
1242 }
1243
1244 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1246 let this = self.pin();
1247 self.inline_counters.claim_attempted();
1248 if let Some(future) = self.priority_runner.claim(&this, &key) {
1249 let completed = poll_once_or_spawn(future);
1250 if completed {
1251 self.inline_counters.claim_completed();
1252 } else {
1253 self.inline_counters.claim_yielded();
1254 }
1255 return completed;
1256 }
1257 self.inline_counters.claim_failed();
1258 false
1259 }
1260
1261 #[cfg(feature = "inline_execution_stats")]
1262 fn note_waited_for_in_progress_task(&self) {
1263 self.inline_counters.waited_in_progress();
1264 }
1265
1266 fn begin_foreground_job(&self) {
1267 self.begin_foreground_jobs(1);
1268 }
1269
1270 fn begin_foreground_jobs(&self, count: usize) {
1272 debug_assert!(count > 0);
1273 if self
1274 .currently_scheduled_foreground_jobs
1275 .fetch_add(count, Ordering::AcqRel)
1276 == 0
1277 {
1278 *self.start.lock().unwrap() = Some(Instant::now());
1279 self.event_foreground_start.notify(usize::MAX);
1280 self.backend.idle_end(self);
1281 }
1282 }
1283
1284 fn finish_foreground_job(&self) {
1285 if self
1286 .currently_scheduled_foreground_jobs
1287 .fetch_sub(1, Ordering::AcqRel)
1288 == 1
1289 {
1290 self.backend.idle_start(self);
1291 let total = self.scheduled_tasks.load(Ordering::Acquire);
1294 self.scheduled_tasks.store(0, Ordering::Release);
1295 if let Some(start) = *self.start.lock().unwrap() {
1296 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1297 if let Some(update) = update.as_mut() {
1298 update.0 += start.elapsed();
1299 update.1 += total;
1300 } else {
1301 *update = Some((start.elapsed(), total));
1302 }
1303 }
1304 self.event_foreground_done.notify(usize::MAX);
1305 }
1306 }
1307
1308 fn begin_background_job(&self) {
1309 self.currently_scheduled_background_jobs
1310 .fetch_add(1, Ordering::Relaxed);
1311 }
1312
1313 fn finish_background_job(&self) {
1314 if self
1315 .currently_scheduled_background_jobs
1316 .fetch_sub(1, Ordering::Relaxed)
1317 == 1
1318 {
1319 self.event_background_done.notify(usize::MAX);
1320 }
1321 }
1322
1323 pub fn get_in_progress_count(&self) -> usize {
1324 self.currently_scheduled_foreground_jobs
1325 .load(Ordering::Acquire)
1326 }
1327
1328 #[cfg(feature = "inline_execution_stats")]
1331 #[doc(hidden)]
1332 pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1333 let counters = &self.inline_counters;
1334 InlineExecutionStats {
1335 queued: self.priority_runner.total_queued(),
1336 claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1337 claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1338 claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1339 claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1340 waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1341 }
1342 }
1343
1344 pub async fn wait_task_completion(
1356 &self,
1357 id: TaskId,
1358 consistency: ReadConsistency,
1359 ) -> Result<()> {
1360 read_task_output(
1361 self,
1362 id,
1363 ReadOutputOptions {
1364 tracking: ReadTracking::Untracked,
1366 consistency,
1367 },
1368 )
1369 .await?;
1370 Ok(())
1371 }
1372
1373 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1376 self.aggregated_update_info(aggregation, Duration::MAX)
1377 .await
1378 .unwrap()
1379 }
1380
1381 pub async fn aggregated_update_info(
1385 &self,
1386 aggregation: Duration,
1387 timeout: Duration,
1388 ) -> Option<UpdateInfo> {
1389 let listener = self
1390 .event_foreground_done
1391 .listen_with_note(|| || "wait for update info".to_string());
1392 let wait_for_finish = {
1393 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1394 if aggregation.is_zero() {
1395 if let Some((duration, tasks)) = update.take() {
1396 return Some(UpdateInfo {
1397 duration,
1398 tasks,
1399 reasons: take(reason_set),
1400 placeholder_for_future_fields: (),
1401 });
1402 } else {
1403 true
1404 }
1405 } else {
1406 update.is_none()
1407 }
1408 };
1409 if wait_for_finish {
1410 if timeout == Duration::MAX {
1411 listener.await;
1413 } else {
1414 let start_listener = self
1416 .event_foreground_start
1417 .listen_with_note(|| || "wait for update info".to_string());
1418 if self
1419 .currently_scheduled_foreground_jobs
1420 .load(Ordering::Acquire)
1421 == 0
1422 {
1423 start_listener.await;
1424 } else {
1425 drop(start_listener);
1426 }
1427 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1428 return None;
1430 }
1431 }
1432 }
1433 if !aggregation.is_zero() {
1434 loop {
1435 select! {
1436 () = tokio::time::sleep(aggregation) => {
1437 break;
1438 }
1439 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1440 }
1442 }
1443 }
1444 }
1445 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1446 if let Some((duration, tasks)) = update.take() {
1447 Some(UpdateInfo {
1448 duration,
1449 tasks,
1450 reasons: take(reason_set),
1451 placeholder_for_future_fields: (),
1452 })
1453 } else {
1454 panic!("aggregated_update_info must not called concurrently")
1455 }
1456 }
1457
1458 pub async fn wait_background_done(&self) {
1459 let listener = self.event_background_done.listen();
1460 if self
1461 .currently_scheduled_background_jobs
1462 .load(Ordering::Acquire)
1463 != 0
1464 {
1465 listener.await;
1466 }
1467 }
1468
1469 pub async fn stop_and_wait(&self) {
1470 #[cfg(feature = "inline_execution_stats")]
1471 if inline_stats_requested() {
1472 eprintln!(
1475 "turbo-tasks inline execution stats: {:#?}",
1476 self.inline_execution_stats()
1477 );
1478 }
1479 turbo_tasks_future_scope(self.pin(), async move {
1480 self.backend.stopping(self);
1481 self.stopped.store(true, Ordering::Release);
1482 {
1483 let listener = self
1484 .event_foreground_done
1485 .listen_with_note(|| || "wait for stop".to_string());
1486 if self
1487 .currently_scheduled_foreground_jobs
1488 .load(Ordering::Acquire)
1489 != 0
1490 {
1491 listener.await;
1492 }
1493 }
1494 {
1495 let listener = self.event_background_done.listen();
1496 if self
1497 .currently_scheduled_background_jobs
1498 .load(Ordering::Acquire)
1499 != 0
1500 {
1501 listener.await;
1502 }
1503 }
1504 self.backend.stop(self);
1505 self.compilation_events.flush_and_close().await;
1509 })
1510 .await;
1511 }
1512
1513 #[track_caller]
1514 pub(crate) fn schedule_background_job<T>(&self, func: T)
1515 where
1516 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1517 T::CallOnceFuture: Send,
1518 {
1519 let mut this = self.pin();
1520 self.begin_background_job();
1521 tokio::spawn(
1522 TURBO_TASKS
1523 .scope(this.clone(), async move {
1524 if !this.stopped.load(Ordering::Acquire) {
1525 this = func(this).await;
1526 }
1527 this.finish_background_job();
1528 })
1529 .in_current_span(),
1530 );
1531 }
1532
1533 fn finish_current_task_state(&self) -> FinishedTaskState {
1534 CURRENT_TASK_STATE.with(|cell| {
1535 let current_task_state = &*cell.write().unwrap();
1536 FinishedTaskState {
1537 #[cfg(feature = "verify_determinism")]
1538 stateful: current_task_state.stateful,
1539 has_invalidator: current_task_state.has_invalidator,
1540 }
1541 })
1542 }
1543
1544 pub fn backend(&self) -> &B {
1545 &self.backend
1546 }
1547
1548 pub fn get_current_task_priority(&self) -> TaskPriority {
1549 CURRENT_TASK_STATE
1550 .try_with(|task_state| task_state.read().unwrap().priority)
1551 .unwrap_or(TaskPriority::initial())
1552 }
1553
1554 pub fn is_idle(&self) -> bool {
1555 self.currently_scheduled_foreground_jobs
1556 .load(Ordering::Acquire)
1557 == 0
1558 }
1559
1560 #[track_caller]
1561 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1562 self.schedule_background_job(async move |this| {
1563 this.backend.run_backend_job(job, &*this).await;
1564 this
1565 })
1566 }
1567}
1568
1569struct TurboTasksExecutor;
1570
1571async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1576 match AssertUnwindSafe(f).catch_unwind().await {
1577 Ok(r) => r,
1578 Err(_) => {
1579 eprintln!(
1580 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1581 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1582 https://github.com/vercel/next.js/discussions and include the panic message \
1583 and stack trace above.\n\nAborting."
1584 );
1585 abort();
1586 }
1587 }
1588}
1589
1590impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1591 const LOWEST_PRIORITY: TaskPriority = TaskPriority::Initial;
1592
1593 type Future = impl Future<Output = ()> + Send + 'static;
1594
1595 fn execute(
1596 &self,
1597 this: &Arc<TurboTasks<B>>,
1598 scheduled_task: ScheduledTask,
1599 priority: TaskPriority,
1600 ) -> Self::Future {
1601 match scheduled_task {
1602 ScheduledTask::Task { task_id, span } => {
1603 let this2 = this.clone();
1604 let this = this.clone();
1605 let future = async move {
1606 abort_on_panic(async {
1607 let execution_id = this.execution_id_factory.wrapping_get();
1610 let current_task_state =
1611 CurrentTaskStateHandle::new(CurrentTaskState::new(
1612 task_id,
1613 execution_id,
1614 priority,
1615 false, ));
1617 let single_execution_future = async {
1618 if this.stopped.load(Ordering::Acquire) {
1619 this.backend.task_execution_canceled(task_id, &*this);
1620 return None;
1621 }
1622
1623 let TaskExecutionSpec { future, span } = this
1624 .backend
1625 .try_start_task_execution(task_id, priority, &*this)?;
1626
1627 InlineExecutionSpanSlot::set(&span);
1630
1631 async {
1632 let result = CaptureFuture::new(future).await;
1633
1634 wait_for_local_tasks().await;
1636
1637 let result = match result {
1638 Ok(Ok(raw_vc)) => {
1639 raw_vc
1642 .to_non_local_unchecked_sync(&*this)
1643 .map_err(|err| err.into())
1644 }
1645 Ok(Err(err)) => Err(err.into()),
1646 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1647 };
1648
1649 let finished_state = this.finish_current_task_state();
1650 let cell_counters = CURRENT_TASK_STATE
1651 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1652 this.backend.task_execution_completed(
1653 task_id,
1654 result,
1655 &cell_counters,
1656 #[cfg(feature = "verify_determinism")]
1657 finished_state.stateful,
1658 finished_state.has_invalidator,
1659 &*this,
1660 )
1661 }
1662 .instrument(span)
1663 .await
1664 };
1665 if let Some(stale_priority) = CURRENT_TASK_STATE
1666 .scope(current_task_state, single_execution_future)
1667 .await
1668 {
1669 this.schedule(task_id, stale_priority);
1672 }
1673 this.finish_foreground_job();
1674 })
1675 .await
1676 };
1677
1678 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1679 }
1680 ScheduledTask::LocalTask {
1681 ty,
1682 persistence,
1683 parent_task: _,
1684 execution_id: _,
1685 local_task_id,
1686 global_task_state,
1687 span,
1688 } => {
1689 let this2 = this.clone();
1690 let this = this.clone();
1691 let task_type = ty.task_type;
1692 let future = async move {
1693 let span = match &ty.task_type {
1694 LocalTaskType::ResolveNative { native_fn } => {
1695 native_fn.resolve_span(priority)
1696 }
1697 LocalTaskType::ResolveTrait { trait_method } => {
1698 trait_method.resolve_span(priority)
1699 }
1700 };
1701 InlineExecutionSpanSlot::set(&span);
1704 abort_on_panic(
1705 async move {
1706 let result = match ty.task_type {
1707 LocalTaskType::ResolveNative { native_fn } => {
1708 LocalTaskType::run_resolve_native(
1709 native_fn,
1710 ty.this,
1711 &*ty.arg,
1712 persistence,
1713 this,
1714 )
1715 .await
1716 }
1717 LocalTaskType::ResolveTrait { trait_method } => {
1718 LocalTaskType::run_resolve_trait(
1719 trait_method,
1720 ty.this.unwrap(),
1721 &*ty.arg,
1722 persistence,
1723 this,
1724 )
1725 .await
1726 }
1727 };
1728
1729 let output = match result {
1730 Ok(raw_vc) => OutputContent::Link(raw_vc),
1731 Err(err) => OutputContent::Error(
1732 TurboTasksExecutionError::from(err)
1733 .with_local_task_context(task_type.to_string()),
1734 ),
1735 };
1736
1737 CURRENT_TASK_STATE.with(move |gts| {
1738 gts.write()
1739 .unwrap()
1740 .local_tasks
1741 .complete(local_task_id, output);
1742 });
1743 }
1744 .instrument(span),
1745 )
1746 .await
1747 };
1748 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1749
1750 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1751 }
1752 }
1753 }
1754
1755 fn discard(
1756 &self,
1757 this: &Arc<TurboTasks<B>>,
1758 scheduled_task: ScheduledTask,
1759 _priority: TaskPriority,
1760 ) {
1761 match scheduled_task {
1762 ScheduledTask::Task { .. } => this.finish_foreground_job(),
1765 ScheduledTask::LocalTask { .. } => {}
1768 }
1769 }
1770}
1771
1772struct FinishedTaskState {
1773 #[cfg(feature = "verify_determinism")]
1776 stateful: bool,
1777
1778 has_invalidator: bool,
1780}
1781
1782impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1783 fn dynamic_call(
1784 &self,
1785 native_fn: &'static NativeFunction,
1786 this: Option<RawVc>,
1787 arg: &mut dyn DynTaskInputsStorage,
1788 inputs_resolved: InputResolution,
1789 persistence: TaskPersistence,
1790 ) -> RawVc {
1791 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1792 }
1793 fn native_call(
1794 &self,
1795 native_fn: &'static NativeFunction,
1796 this: Option<RawVc>,
1797 arg: &mut dyn DynTaskInputsStorage,
1798 persistence: TaskPersistence,
1799 ) -> RawVc {
1800 self.native_call(native_fn, this, arg, persistence)
1801 }
1802 fn trait_call(
1803 &self,
1804 trait_method: &'static TraitMethod,
1805 this: RawVc,
1806 arg: &mut dyn DynTaskInputsStorage,
1807 inputs_resolved: InputResolution,
1808 persistence: TaskPersistence,
1809 ) -> RawVc {
1810 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1811 }
1812
1813 #[track_caller]
1814 fn run(
1815 &self,
1816 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1817 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1818 let this = self.pin();
1819 Box::pin(async move { this.run(future).await })
1820 }
1821
1822 #[track_caller]
1823 fn run_once(
1824 &self,
1825 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1826 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1827 let this = self.pin();
1828 Box::pin(async move { this.run_once(future).await })
1829 }
1830
1831 #[track_caller]
1832 fn run_once_with_reason(
1833 &self,
1834 reason: StaticOrArc<dyn InvalidationReason>,
1835 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1836 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1837 {
1838 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1839 reason_set.insert(reason);
1840 }
1841 let this = self.pin();
1842 Box::pin(async move { this.run_once(future).await })
1843 }
1844
1845 #[track_caller]
1846 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1847 self.start_once_process(future)
1848 }
1849
1850 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1851 if let Err(e) = self.compilation_events.send(event) {
1852 tracing::warn!("Failed to send compilation event: {e}");
1853 }
1854 }
1855
1856 fn get_task_name(&self, task: TaskId) -> String {
1857 self.backend.get_task_name(task, self)
1858 }
1859}
1860
1861impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1862 #[instrument(level = "info", skip_all, name = "invalidate")]
1863 fn invalidate(&self, task: TaskId) {
1864 self.backend.invalidate_task(task, self);
1865 }
1866
1867 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1868 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1869 {
1870 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1871 reason_set.insert(reason);
1872 }
1873 self.backend.invalidate_task(task, self);
1874 }
1875
1876 fn invalidate_serialization(&self, task: TaskId) {
1877 self.backend.invalidate_serialization(task, self);
1878 }
1879
1880 #[track_caller]
1881 fn try_read_task_output(
1882 &self,
1883 task: TaskId,
1884 options: ReadOutputOptions,
1885 ) -> Result<ReadOutcome<RawVc>> {
1886 if options.consistency == ReadConsistency::Eventual {
1887 debug_assert_not_in_top_level_task("read_task_output");
1888 }
1889 self.backend.try_read_task_output(
1890 task,
1891 current_task_if_available("reading Vcs"),
1892 options,
1893 self,
1894 )
1895 }
1896
1897 #[track_caller]
1898 fn try_read_task_cell(
1899 &self,
1900 task: TaskId,
1901 index: CellId,
1902 options: ReadCellOptions,
1903 ) -> Result<ReadOutcome<TypedCellContent>> {
1904 let reader = current_task_if_available("reading Vcs");
1905 self.backend
1906 .try_read_task_cell(task, index, reader, options, self)
1907 }
1908
1909 fn try_read_own_task_cell(
1910 &self,
1911 current_task: TaskId,
1912 index: CellId,
1913 ) -> Result<TypedCellContent> {
1914 self.backend
1915 .try_read_own_task_cell(current_task, index, self)
1916 }
1917
1918 #[track_caller]
1919 fn try_read_local_output(
1920 &self,
1921 execution_id: ExecutionId,
1922 local_task_id: LocalTaskId,
1923 ) -> Result<Result<RawVc, EventListener>> {
1924 debug_assert_not_in_top_level_task("read_local_output");
1925 CURRENT_TASK_STATE.with(|gts| {
1926 let gts_read = gts.read().unwrap();
1927
1928 gts_read.assert_execution_id(execution_id);
1933
1934 match gts_read.local_tasks.get(local_task_id) {
1935 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1936 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1937 }
1938 })
1939 }
1940
1941 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1942 self.backend.read_task_collectibles(
1945 task,
1946 trait_id,
1947 current_task_if_available("reading collectibles"),
1948 self,
1949 )
1950 }
1951
1952 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1953 self.try_execute_scheduled_task_inline(key)
1954 }
1955
1956 #[cfg(feature = "inline_execution_stats")]
1957 fn note_waited_for_in_progress_task(&self) {
1958 self.note_waited_for_in_progress_task()
1959 }
1960
1961 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1962 self.backend.emit_collectible(
1963 trait_type,
1964 collectible,
1965 current_task("emitting collectible"),
1966 self,
1967 );
1968 }
1969
1970 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1971 self.backend.unemit_collectible(
1972 trait_type,
1973 collectible,
1974 count,
1975 current_task("emitting collectible"),
1976 self,
1977 );
1978 }
1979
1980 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1981 for (&collectible, &count) in collectibles {
1982 if count > 0 {
1983 self.backend.unemit_collectible(
1984 trait_type,
1985 collectible,
1986 count as u32,
1987 current_task("emitting collectible"),
1988 self,
1989 );
1990 }
1991 }
1992 }
1993
1994 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1995 self.try_read_own_task_cell(task, index)
1996 }
1997
1998 fn update_own_task_cell(
1999 &self,
2000 task: TaskId,
2001 index: CellId,
2002 content: CellContent,
2003 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
2004 content_hash: Option<CellHash>,
2005 verification_mode: VerificationMode,
2006 ) {
2007 self.backend.update_task_cell(
2008 task,
2009 index,
2010 content,
2011 updated_key_hashes,
2012 content_hash,
2013 verification_mode,
2014 self,
2015 );
2016 }
2017
2018 fn connect_task(&self, task: TaskId) {
2019 self.backend
2020 .connect_task(task, current_task_if_available("connecting task"), self);
2021 }
2022
2023 fn mark_own_task_as_finished(&self, task: TaskId) {
2024 self.backend.mark_own_task_as_finished(task, self);
2025 }
2026
2027 fn pin_task_for_gc(&self, task: TaskId) {
2028 self.backend.pin_task_for_gc(task, self);
2029 }
2030
2031 fn unpin_task_for_gc(&self, task: TaskId) {
2032 self.backend.unpin_task_for_gc(task, self);
2033 }
2034
2035 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
2038 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
2041 global_task_state
2042 .write()
2043 .unwrap()
2044 .local_tasks
2045 .register_detached();
2046 let wrapped = async move {
2047 struct DropGuard;
2049 impl Drop for DropGuard {
2050 fn drop(&mut self) {
2051 CURRENT_TASK_STATE
2052 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
2053 }
2054 }
2055 let _guard = DropGuard;
2056 fut.await;
2057 };
2058 tokio::spawn(TURBO_TASKS.scope(
2059 turbo_tasks(),
2060 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
2061 ));
2062 }
2063
2064 fn task_statistics(&self) -> &TaskStatisticsApi {
2065 self.backend.task_statistics()
2066 }
2067
2068 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
2069 let this = self.pin();
2070 Box::pin(async move {
2071 this.stop_and_wait().await;
2072 })
2073 }
2074
2075 fn subscribe_to_compilation_events(
2076 &self,
2077 event_types: Option<Vec<String>>,
2078 ) -> Receiver<Arc<dyn CompilationEvent>> {
2079 self.compilation_events.subscribe(event_types)
2080 }
2081
2082 fn is_tracking_dependencies(&self) -> bool {
2083 self.backend.is_tracking_dependencies()
2084 }
2085}
2086
2087async fn wait_for_local_tasks() {
2088 let listener =
2089 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
2090 let Some(listener) = listener else {
2091 return;
2092 };
2093 listener.await;
2094}
2095
2096pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
2097 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2098 Ok(id) => id,
2099 Err(_) => panic!(
2100 "{from} can only be used in the context of a turbo_tasks task execution or \
2101 turbo_tasks run"
2102 ),
2103 }
2104}
2105
2106pub(crate) fn current_task(from: &str) -> TaskId {
2107 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2108 Ok(Some(id)) => id,
2109 Ok(None) | Err(_) => {
2110 panic!("{from} can only be used in the context of a turbo_tasks task execution")
2111 }
2112 }
2113}
2114
2115#[track_caller]
2118pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2119 if !cfg!(debug_assertions) {
2120 return;
2121 }
2122
2123 let in_top_level = CURRENT_TASK_STATE
2124 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2125 .unwrap_or(true);
2126 if !in_top_level {
2127 panic!("{message}");
2128 }
2129}
2130
2131#[track_caller]
2132pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2133 if !cfg!(debug_assertions) {
2134 return;
2135 }
2136
2137 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2140 .try_with(|&suppressed| suppressed)
2141 .unwrap_or(false);
2142 if suppressed {
2143 return;
2144 }
2145
2146 let in_top_level = CURRENT_TASK_STATE
2147 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2148 .unwrap_or(false);
2149 if in_top_level {
2150 panic!(
2151 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2152 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2153 reads to avoid leaking inconsistent return values."
2154 );
2155 }
2156}
2157
2158pub async fn run<T: Send + 'static>(
2159 tt: Arc<dyn TurboTasksApi>,
2160 future: impl Future<Output = Result<T>> + Send + 'static,
2161) -> Result<T> {
2162 let (tx, rx) = tokio::sync::oneshot::channel();
2163
2164 tt.run(Box::pin(async move {
2165 let result = future.await?;
2166 tx.send(result)
2167 .map_err(|_| anyhow!("unable to send result"))?;
2168 Ok(())
2169 }))
2170 .await?;
2171
2172 Ok(rx.await?)
2173}
2174
2175pub async fn run_once<T: Send + 'static>(
2176 tt: Arc<dyn TurboTasksApi>,
2177 future: impl Future<Output = Result<T>> + Send + 'static,
2178) -> Result<T> {
2179 let (tx, rx) = tokio::sync::oneshot::channel();
2180
2181 tt.run_once(Box::pin(async move {
2182 let result = future.await?;
2183 tx.send(result)
2184 .map_err(|_| anyhow!("unable to send result"))?;
2185 Ok(())
2186 }))
2187 .await?;
2188
2189 Ok(rx.await?)
2190}
2191
2192pub async fn run_once_with_reason<T: Send + 'static>(
2193 tt: Arc<dyn TurboTasksApi>,
2194 reason: impl InvalidationReason,
2195 future: impl Future<Output = Result<T>> + Send + 'static,
2196) -> Result<T> {
2197 let (tx, rx) = tokio::sync::oneshot::channel();
2198
2199 tt.run_once_with_reason(
2200 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2201 Box::pin(async move {
2202 let result = future.await?;
2203 tx.send(result)
2204 .map_err(|_| anyhow!("unable to send result"))?;
2205 Ok(())
2206 }),
2207 )
2208 .await?;
2209
2210 Ok(rx.await?)
2211}
2212
2213pub fn dynamic_call(
2215 func: &'static NativeFunction,
2216 this: Option<RawVc>,
2217 arg: &mut dyn DynTaskInputsStorage,
2218 inputs_resolved: InputResolution,
2219 persistence: TaskPersistence,
2220) -> RawVc {
2221 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2222}
2223
2224pub fn trait_call(
2226 trait_method: &'static TraitMethod,
2227 this: RawVc,
2228 arg: &mut dyn DynTaskInputsStorage,
2229 inputs_resolved: InputResolution,
2230 persistence: TaskPersistence,
2231) -> RawVc {
2232 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2233}
2234
2235pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2236 TURBO_TASKS.with(|arc| arc.clone())
2237}
2238
2239pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2240 TURBO_TASKS.with(Arc::downgrade)
2241}
2242
2243pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2244 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2245}
2246
2247pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2248 TURBO_TASKS.with(|arc| func(arc))
2249}
2250
2251pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2252 TURBO_TASKS.sync_scope(tt, f)
2253}
2254
2255pub fn turbo_tasks_future_scope<T>(
2256 tt: Arc<dyn TurboTasksApi>,
2257 f: impl Future<Output = T>,
2258) -> impl Future<Output = T> {
2259 TURBO_TASKS.scope(tt, f)
2260}
2261
2262pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2267 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2268}
2269
2270pub fn mark_finished() {
2273 with_turbo_tasks(|tt| {
2274 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2275 });
2276}
2277
2278pub fn get_serialization_invalidator() -> SerializationInvalidator {
2284 CURRENT_TASK_STATE.with(|cell| {
2285 let CurrentTaskState {
2286 task_id,
2287 #[cfg(feature = "verify_determinism")]
2288 stateful,
2289 ..
2290 } = &mut *cell.write().unwrap();
2291 #[cfg(feature = "verify_determinism")]
2292 {
2293 *stateful = true;
2294 }
2295 let Some(task_id) = *task_id else {
2296 panic!(
2297 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2298 task execution"
2299 );
2300 };
2301 SerializationInvalidator::new(task_id)
2302 })
2303}
2304
2305pub fn mark_invalidator() {
2306 CURRENT_TASK_STATE.with(|cell| {
2307 let CurrentTaskState {
2308 has_invalidator, ..
2309 } = &mut *cell.write().unwrap();
2310 *has_invalidator = true;
2311 })
2312}
2313
2314pub fn mark_stateful() {
2320 #[cfg(feature = "verify_determinism")]
2321 {
2322 CURRENT_TASK_STATE.with(|cell| {
2323 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2324 *stateful = true;
2325 })
2326 }
2327 }
2329
2330pub fn mark_top_level_task() {
2334 if cfg!(debug_assertions) {
2335 CURRENT_TASK_STATE.with(|cell| {
2336 cell.write().unwrap().in_top_level_task = true;
2337 })
2338 }
2339}
2340
2341pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2352 if cfg!(debug_assertions) {
2353 CURRENT_TASK_STATE.with(|cell| {
2354 cell.write().unwrap().in_top_level_task = false;
2355 })
2356 }
2357}
2358
2359pub fn prevent_gc() {
2367 if let Some(task) = current_task_if_available("prevent_gc") {
2368 with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2369 }
2370}
2371
2372pub struct GcRoot<T: ?Sized> {
2374 tt: Arc<dyn TurboTasksApi>,
2375 vc: OperationVc<T>,
2376}
2377
2378impl<T: ?Sized> GcRoot<T> {
2379 pub fn pin(tt: Arc<dyn TurboTasksApi>, vc: OperationVc<T>) -> Self {
2381 tt.pin_task_for_gc(vc.task_id());
2382 Self { tt, vc }
2383 }
2384}
2385
2386impl<T: ?Sized> Deref for GcRoot<T> {
2389 type Target = OperationVc<T>;
2390
2391 fn deref(&self) -> &Self::Target {
2392 &self.vc
2393 }
2394}
2395
2396impl<T: ?Sized> Clone for GcRoot<T> {
2397 fn clone(&self) -> Self {
2398 Self::pin(self.tt.clone(), self.vc)
2399 }
2400}
2401
2402impl<T: ?Sized> Drop for GcRoot<T> {
2403 fn drop(&mut self) {
2404 self.tt.unpin_task_for_gc(self.vc.task_id());
2405 }
2406}
2407
2408impl<T: ?Sized> Debug for GcRoot<T> {
2409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2410 f.debug_struct("GcRoot").field("vc", &self.vc).finish()
2411 }
2412}
2413
2414impl<T: ?Sized> PartialEq for GcRoot<T> {
2415 fn eq(&self, other: &Self) -> bool {
2418 self.vc == other.vc
2419 }
2420}
2421
2422impl<T: ?Sized> Eq for GcRoot<T> {}
2423
2424impl<T: ?Sized> Hash for GcRoot<T> {
2425 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2428 self.vc.hash(state);
2429 }
2430}
2431
2432impl<T: ?Sized> Borrow<OperationVc<T>> for GcRoot<T> {
2436 fn borrow(&self) -> &OperationVc<T> {
2437 &self.vc
2438 }
2439}
2440
2441unsafe impl<T: ?Sized + Send> OperationValue for GcRoot<T> {}
2444
2445unsafe impl<T: NonLocalValue + ?Sized> NonLocalValue for GcRoot<T> {}
2448
2449pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2450 with_turbo_tasks(|tt| {
2451 let raw_vc = collectible.node.node;
2452 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2453 })
2454}
2455
2456pub(crate) async fn read_task_output(
2457 this: &dyn TurboTasksApi,
2458 id: TaskId,
2459 options: ReadOutputOptions,
2460) -> Result<RawVc> {
2461 loop {
2462 match this.try_read_task_output(id, options)? {
2463 ReadOutcome::Value(result) => return Ok(result),
2464 ReadOutcome::Scheduled(listener) => {
2465 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2467 continue;
2468 }
2469 listener.await
2470 }
2471 ReadOutcome::InProgress(listener) => {
2472 #[cfg(feature = "inline_execution_stats")]
2474 this.note_waited_for_in_progress_task();
2475 listener.await
2476 }
2477 }
2478 }
2479}
2480
2481#[derive(Clone, Copy)]
2487pub struct CurrentCellRef {
2488 current_task: TaskId,
2489 index: CellId,
2490}
2491
2492type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2493
2494type CellUpdate = (
2497 SharedReference,
2498 Option<SmallVec<[u64; 2]>>,
2499 Option<CellHash>,
2500);
2501
2502type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2505
2506impl CurrentCellRef {
2507 fn conditional_update<T>(
2509 &self,
2510 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2511 ) where
2512 T: VcValueType,
2513 {
2514 let mut functor = Some(functor);
2517 self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2518 let functor = functor.take().expect("functor is called at most once");
2519 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2520 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2521 Some((
2522 SharedReference::new(triomphe::Arc::new(new_value)),
2523 updated_key_hashes,
2524 content_hash,
2525 ))
2526 })
2527 }
2528
2529 fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2536 let tt = turbo_tasks();
2537 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2538 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2539 if let Some((update, updated_key_hashes, content_hash)) = update {
2540 tt.update_own_task_cell(
2541 self.current_task,
2542 self.index,
2543 CellContent(Some(update)),
2544 updated_key_hashes,
2545 content_hash,
2546 VerificationMode::EqualityCheck,
2547 )
2548 }
2549 }
2550
2551 pub fn compare_and_update<T>(&self, new_value: T)
2586 where
2587 T: PartialEq + VcValueType,
2588 {
2589 self.conditional_update(|old_value| {
2590 if let Some(old_value) = old_value
2591 && old_value == &new_value
2592 {
2593 return None;
2594 }
2595 Some((new_value, None, None))
2596 });
2597 }
2598
2599 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2607 where
2608 T: VcValueType + PartialEq,
2609 {
2610 let mut new_shared_reference = Some(new_shared_reference);
2611 self.conditional_update_with_shared_reference(&mut |old_sr| {
2612 let new_shared_reference = new_shared_reference
2613 .take()
2614 .expect("functor is called at most once");
2615 if let Some(old_sr) = old_sr {
2616 let old_value = extract_sr_value::<T>(old_sr);
2617 let new_value = extract_sr_value::<T>(&new_shared_reference);
2618 if old_value == new_value {
2619 return None;
2620 }
2621 }
2622 Some((new_shared_reference, None, None))
2623 });
2624 }
2625
2626 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2635 where
2636 T: PartialEq + DeterministicHash + VcValueType,
2637 {
2638 self.conditional_update(|old_value| {
2639 if let Some(old_value) = old_value
2640 && old_value == &new_value
2641 {
2642 return None;
2643 }
2644 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2645
2646 Some((new_value, None, Some(content_hash)))
2647 });
2648 }
2649
2650 pub fn hashed_compare_and_update_with_shared_reference<T>(
2656 &self,
2657 new_shared_reference: SharedReference,
2658 ) where
2659 T: VcValueType + PartialEq + DeterministicHash,
2660 {
2661 let mut new_shared_reference = Some(new_shared_reference);
2662 self.conditional_update_with_shared_reference(&mut move |old_sr| {
2663 let new_shared_reference = new_shared_reference
2664 .take()
2665 .expect("functor is called at most once");
2666 if let Some(old_sr) = old_sr {
2667 let old_value = extract_sr_value::<T>(old_sr);
2668 let new_value = extract_sr_value::<T>(&new_shared_reference);
2669 if old_value == new_value {
2670 return None;
2671 }
2672 }
2673 let content_hash =
2674 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2675 Some((new_shared_reference, None, Some(content_hash)))
2676 });
2677 }
2678
2679 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2681 where
2682 T: PartialEq + VcValueType,
2683 VcReadTarget<T>: KeyedEq,
2684 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2685 {
2686 self.conditional_update(|old_value| {
2687 let Some(old_value) = old_value else {
2688 return Some((new_value, None, None));
2689 };
2690 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2691 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2692 let updated_keys = old_value.different_keys(new_value_ref);
2693 if updated_keys.is_empty() {
2694 return None;
2695 }
2696 let updated_key_hashes = updated_keys
2698 .into_iter()
2699 .map(|key| FxBuildHasher.hash_one(key))
2700 .collect();
2701 Some((new_value, Some(updated_key_hashes), None))
2702 });
2703 }
2704
2705 pub fn keyed_compare_and_update_with_shared_reference<T>(
2708 &self,
2709 new_shared_reference: SharedReference,
2710 ) where
2711 T: VcValueType + PartialEq,
2712 VcReadTarget<T>: KeyedEq,
2713 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2714 {
2715 let mut new_shared_reference = Some(new_shared_reference);
2716 self.conditional_update_with_shared_reference(&mut |old_sr| {
2717 let new_shared_reference = new_shared_reference
2718 .take()
2719 .expect("functor is called at most once");
2720 let Some(old_sr) = old_sr else {
2721 return Some((new_shared_reference, None, None));
2722 };
2723 let old_value = extract_sr_value::<T>(old_sr);
2724 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2725 let new_value = extract_sr_value::<T>(&new_shared_reference);
2726 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2727 let updated_keys = old_value.different_keys(new_value);
2728 if updated_keys.is_empty() {
2729 return None;
2730 }
2731 let updated_key_hashes = updated_keys
2733 .into_iter()
2734 .map(|key| FxBuildHasher.hash_one(key))
2735 .collect();
2736 Some((new_shared_reference, Some(updated_key_hashes), None))
2737 });
2738 }
2739
2740 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2742 where
2743 T: VcValueType,
2744 {
2745 let tt = turbo_tasks();
2746 tt.update_own_task_cell(
2747 self.current_task,
2748 self.index,
2749 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2750 None,
2751 None,
2752 verification_mode,
2753 )
2754 }
2755
2756 pub fn update_with_shared_reference(
2764 &self,
2765 shared_ref: SharedReference,
2766 verification_mode: VerificationMode,
2767 ) {
2768 let tt = turbo_tasks();
2769 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2770 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2771 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2772 shared_ref_exp != shared_ref
2774 } else {
2775 true
2776 }
2777 } else {
2778 true
2779 };
2780 if update {
2781 tt.update_own_task_cell(
2782 self.current_task,
2783 self.index,
2784 CellContent(Some(shared_ref)),
2785 None,
2786 None,
2787 verification_mode,
2788 )
2789 }
2790 }
2791}
2792
2793impl From<CurrentCellRef> for RawVc {
2794 fn from(cell: CurrentCellRef) -> Self {
2795 RawVc::task_cell(cell.current_task, cell.index)
2796 }
2797}
2798
2799fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2800 sr.0.downcast_ref::<T>()
2801 .expect("cannot update SharedReference of different type")
2802}
2803
2804pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2805 find_cell_by_id(T::get_value_type_id())
2806}
2807
2808pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2809 CURRENT_TASK_STATE.with(|ts| {
2810 let current_task = current_task("celling turbo_tasks values");
2811 let mut ts = ts.write().unwrap();
2812 let map = ts.cell_counters.as_mut().unwrap();
2813 let current_index = map.entry(ty).or_default();
2814 let index = *current_index;
2815 assert!(
2816 index <= CellId::MAX_CELL_INDEX,
2817 "task allocated more than {} cells of a single type",
2818 CellId::MAX_CELL_INDEX as u64 + 1,
2819 );
2820 *current_index += 1;
2821 CurrentCellRef {
2822 current_task,
2823 index: CellId::new(ty, index),
2824 }
2825 })
2826}
2827
2828pub(crate) async fn read_local_output(
2829 this: &dyn TurboTasksApi,
2830 execution_id: ExecutionId,
2831 local_task_id: LocalTaskId,
2832) -> Result<RawVc> {
2833 loop {
2834 match this.try_read_local_output(execution_id, local_task_id)? {
2835 Ok(raw_vc) => return Ok(raw_vc),
2836 Err(event_listener) => {
2837 if execute_read_target_inline(
2840 this,
2841 ScheduleKey::current_local_task(execution_id, local_task_id),
2842 ) {
2843 continue;
2844 }
2845 event_listener.await
2846 }
2847 }
2848 }
2849}
2850
2851#[cfg(test)]
2852mod tests {
2853 use super::*;
2854
2855 #[test]
2856 fn test_inline_execution_depth_guard_restores_depth() {
2857 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2858 {
2859 let _outer = InlineExecutionDepthGuard::enter();
2860 {
2861 let _inner = InlineExecutionDepthGuard::enter();
2862 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2863 }
2864 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2865 }
2866 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2867 }
2868
2869 #[test]
2870 fn test_inline_depth_cap() {
2871 assert!(inline_execution_allowed(), "nothing is nested yet");
2872 let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2873 .map(|_| InlineExecutionDepthGuard::enter())
2874 .collect::<Vec<_>>();
2875 assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2876 assert!(
2877 !inline_execution_allowed(),
2878 "at the nesting cap reads wait for a worker instead of executing inline"
2879 );
2880
2881 guards.pop();
2883 assert!(inline_execution_allowed());
2884 }
2885
2886 #[tokio::test]
2887 async fn test_poll_once_or_spawn_completed_execution() {
2888 assert!(
2889 poll_once_or_spawn(async {}),
2890 "a future that completes on the first poll is executed inline"
2891 );
2892 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2893 }
2894
2895 #[tokio::test]
2896 async fn test_poll_once_or_spawn_pending_execution() {
2897 let (tx, rx) = tokio::sync::oneshot::channel();
2898 let done = Arc::new(AtomicBool::new(false));
2899 let done_in_task = done.clone();
2900 assert!(
2901 !poll_once_or_spawn(async move {
2902 tokio::task::yield_now().await;
2904 done_in_task.store(true, Ordering::SeqCst);
2905 let _ = tx.send(());
2906 }),
2907 "a future that yields is not completed inline"
2908 );
2909 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2910
2911 rx.await.unwrap();
2913 assert!(done.load(Ordering::SeqCst));
2914 }
2915}