1use std::{
2 cmp::Reverse,
3 fmt::{Debug, Display},
4 future::Future,
5 hash::{BuildHasher, BuildHasherDefault},
6 mem::take,
7 ops::Deref,
8 panic::AssertUnwindSafe,
9 pin::Pin,
10 process::abort,
11 sync::{
12 Arc, Mutex, RwLock, Weak,
13 atomic::{AtomicBool, AtomicUsize, Ordering},
14 },
15 time::{Duration, Instant},
16};
17
18use anyhow::{Result, anyhow};
19use auto_hash_map::AutoMap;
20use bincode::{Decode, Encode};
21use either::Either;
22use futures::FutureExt;
23use rustc_hash::{FxBuildHasher, FxHasher};
24use serde::{Deserialize, Serialize};
25use smallvec::SmallVec;
26use tokio::{select, sync::mpsc::Receiver, task_local};
27use tracing::{Instrument, Span, instrument};
28use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash128};
29
30use crate::{
31 CellId, Completion, InvalidationReason, InvalidationReasonSet, OutputContent, RawVc,
32 ReadCellOptions, ReadOutputOptions, ResolvedVc, SharedReference, TaskId, TraitMethod,
33 ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType,
34 backend::{
35 Backend, CellContent, CellHash, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType,
36 TurboTasksExecutionError, TypedCellContent, VerificationMode,
37 },
38 capture_future::CaptureFuture,
39 dyn_task_inputs::DynTaskInputsStorage,
40 event::{Event, EventListener},
41 id::{ExecutionId, LocalTaskId, TraitTypeId},
42 keyed::KeyedEq,
43 local_task_tracker::LocalTaskTracker,
44 macro_helpers::NativeFunction,
45 message_queue::{CompilationEvent, CompilationEventQueue},
46 priority_runner::{Executor, PriorityRunner},
47 registry,
48 serialization_invalidation::SerializationInvalidator,
49 task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
50 task_statistics::TaskStatisticsApi,
51 trace::TraceRawVcs,
52 util::{IdFactory, StaticOrArc},
53};
54
55pub trait TurboTasksCallApi: Sync + Send {
58 fn dynamic_call(
65 &self,
66 native_fn: &'static NativeFunction,
67 this: Option<RawVc>,
68 arg: &mut dyn DynTaskInputsStorage,
69 inputs_resolved: InputResolution,
70 persistence: TaskPersistence,
71 ) -> RawVc;
72 fn native_call(
75 &self,
76 native_fn: &'static NativeFunction,
77 this: Option<RawVc>,
78 arg: &mut dyn DynTaskInputsStorage,
79 persistence: TaskPersistence,
80 ) -> RawVc;
81 fn trait_call(
88 &self,
89 trait_method: &'static TraitMethod,
90 this: RawVc,
91 arg: &mut dyn DynTaskInputsStorage,
92 inputs_resolved: InputResolution,
93 persistence: TaskPersistence,
94 ) -> RawVc;
95
96 fn run(
97 &self,
98 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
99 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>>;
100 fn run_once(
101 &self,
102 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
103 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
104 fn run_once_with_reason(
105 &self,
106 reason: StaticOrArc<dyn InvalidationReason>,
107 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
108 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
109 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
110
111 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
113
114 fn get_task_name(&self, task: TaskId) -> String;
116}
117
118pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
124 fn invalidate(&self, task: TaskId);
125 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>);
126
127 fn invalidate_serialization(&self, task: TaskId);
128
129 fn try_read_task_output(
130 &self,
131 task: TaskId,
132 options: ReadOutputOptions,
133 ) -> Result<Result<RawVc, EventListener>>;
134
135 fn try_read_task_cell(
136 &self,
137 task: TaskId,
138 index: CellId,
139 options: ReadCellOptions,
140 ) -> Result<Result<TypedCellContent, EventListener>>;
141
142 fn try_read_local_output(
157 &self,
158 execution_id: ExecutionId,
159 local_task_id: LocalTaskId,
160 ) -> Result<Result<RawVc, EventListener>>;
161
162 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
163
164 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
165 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
166 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
167
168 fn try_read_own_task_cell(
171 &self,
172 current_task: TaskId,
173 index: CellId,
174 ) -> Result<TypedCellContent>;
175
176 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
177 fn update_own_task_cell(
178 &self,
179 task: TaskId,
180 index: CellId,
181 content: CellContent,
182 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
183 content_hash: Option<CellHash>,
184 verification_mode: VerificationMode,
185 );
186 fn mark_own_task_as_finished(&self, task: TaskId);
187
188 fn connect_task(&self, task: TaskId);
189
190 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
195
196 fn task_statistics(&self) -> &TaskStatisticsApi;
197
198 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
199
200 fn subscribe_to_compilation_events(
201 &self,
202 event_types: Option<Vec<String>>,
203 ) -> Receiver<Arc<dyn CompilationEvent>>;
204
205 fn is_tracking_dependencies(&self) -> bool;
207}
208
209pub struct Unused<T> {
211 inner: T,
212}
213
214impl<T> Unused<T> {
215 pub unsafe fn new_unchecked(inner: T) -> Self {
221 Self { inner }
222 }
223
224 pub unsafe fn get_unchecked(&self) -> &T {
230 &self.inner
231 }
232
233 pub fn into(self) -> T {
235 self.inner
236 }
237}
238
239#[allow(clippy::manual_non_exhaustive)]
240pub struct UpdateInfo {
241 pub duration: Duration,
242 pub tasks: usize,
243 pub reasons: InvalidationReasonSet,
244 #[allow(dead_code)]
245 placeholder_for_future_fields: (),
246}
247
248#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
249pub enum TaskPersistence {
250 Persistent,
252
253 Transient,
260}
261
262impl Display for TaskPersistence {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 match self {
265 TaskPersistence::Persistent => write!(f, "persistent"),
266 TaskPersistence::Transient => write!(f, "transient"),
267 }
268 }
269}
270
271#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
274pub enum InputResolution {
275 Resolved,
278 Unresolved,
280}
281
282impl InputResolution {
283 #[inline]
284 pub fn from_is_resolved(is_resolved: bool) -> Self {
285 if is_resolved {
286 Self::Resolved
287 } else {
288 Self::Unresolved
289 }
290 }
291
292 #[inline]
293 pub fn is_resolved(self) -> bool {
294 matches!(self, Self::Resolved)
295 }
296}
297
298#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
299pub enum ReadConsistency {
300 #[default]
303 Eventual,
304 Strong,
309}
310
311#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub enum ReadCellTracking {
313 Tracked {
315 key: Option<u64>,
317 },
318 TrackOnlyError,
323 Untracked,
328}
329
330impl ReadCellTracking {
331 pub fn should_track(&self, is_err: bool) -> bool {
332 match self {
333 ReadCellTracking::Tracked { .. } => true,
334 ReadCellTracking::TrackOnlyError => is_err,
335 ReadCellTracking::Untracked => false,
336 }
337 }
338
339 pub fn key(&self) -> Option<u64> {
340 match self {
341 ReadCellTracking::Tracked { key } => *key,
342 ReadCellTracking::TrackOnlyError => None,
343 ReadCellTracking::Untracked => None,
344 }
345 }
346}
347
348impl Default for ReadCellTracking {
349 fn default() -> Self {
350 ReadCellTracking::Tracked { key: None }
351 }
352}
353
354impl Display for ReadCellTracking {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 match self {
357 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
358 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
359 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
360 ReadCellTracking::Untracked => write!(f, "untracked"),
361 }
362 }
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
366pub enum ReadTracking {
367 #[default]
369 Tracked,
370 TrackOnlyError,
375 Untracked,
380}
381
382impl ReadTracking {
383 pub fn should_track(&self, is_err: bool) -> bool {
384 match self {
385 ReadTracking::Tracked => true,
386 ReadTracking::TrackOnlyError => is_err,
387 ReadTracking::Untracked => false,
388 }
389 }
390}
391
392impl Display for ReadTracking {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 match self {
395 ReadTracking::Tracked => write!(f, "tracked"),
396 ReadTracking::TrackOnlyError => write!(f, "track only error"),
397 ReadTracking::Untracked => write!(f, "untracked"),
398 }
399 }
400}
401
402#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
403pub enum TaskPriority {
404 #[default]
405 Initial,
406 Invalidation {
407 priority: Reverse<u32>,
408 },
409 Recomputation,
410}
411
412impl TaskPriority {
413 pub fn invalidation(priority: u32) -> Self {
414 Self::Invalidation {
415 priority: Reverse(priority),
416 }
417 }
418
419 pub fn initial() -> Self {
420 Self::Initial
421 }
422
423 pub fn leaf() -> Self {
424 Self::Invalidation {
425 priority: Reverse(0),
426 }
427 }
428
429 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
430 match self {
431 TaskPriority::Initial => parent_priority,
432 TaskPriority::Invalidation { priority } => {
433 if let TaskPriority::Invalidation {
434 priority: parent_priority,
435 } = parent_priority
436 && priority.0 < parent_priority.0
437 {
438 Self::Invalidation {
439 priority: Reverse(parent_priority.0.saturating_add(1)),
440 }
441 } else {
442 *self
443 }
444 }
445 TaskPriority::Recomputation => TaskPriority::Recomputation,
446 }
447 }
448}
449
450impl Display for TaskPriority {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 match self {
453 TaskPriority::Initial => write!(f, "initial"),
454 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
455 TaskPriority::Recomputation => write!(f, "recomputation"),
456 }
457 }
458}
459
460enum ScheduledTask {
461 Task {
462 task_id: TaskId,
463 span: Span,
464 },
465 LocalTask {
466 ty: LocalTaskSpec,
467 persistence: TaskPersistence,
468 local_task_id: LocalTaskId,
469 global_task_state: CurrentTaskStateHandle,
470 span: Span,
471 },
472}
473
474pub struct TurboTasks<B: Backend + 'static> {
475 this: Weak<Self>,
476 backend: B,
477 execution_id_factory: IdFactory<ExecutionId>,
478 stopped: AtomicBool,
479 currently_scheduled_foreground_jobs: AtomicUsize,
480 currently_scheduled_background_jobs: AtomicUsize,
481 scheduled_tasks: AtomicUsize,
482 priority_runner:
483 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
484 start: Mutex<Option<Instant>>,
485 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
486 event_foreground_start: Event,
488 event_foreground_done: Event,
491 event_background_done: Event,
493 compilation_events: CompilationEventQueue,
494}
495
496struct CurrentTaskState {
505 task_id: Option<TaskId>,
506 execution_id: ExecutionId,
507 priority: TaskPriority,
508
509 #[cfg(feature = "verify_determinism")]
512 stateful: bool,
513
514 has_invalidator: bool,
516
517 in_top_level_task: bool,
520
521 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
526
527 local_tasks: LocalTaskTracker,
530}
531
532impl CurrentTaskState {
533 fn new(
534 task_id: TaskId,
535 execution_id: ExecutionId,
536 priority: TaskPriority,
537 in_top_level_task: bool,
538 ) -> Self {
539 Self {
540 task_id: Some(task_id),
541 execution_id,
542 priority,
543 #[cfg(feature = "verify_determinism")]
544 stateful: false,
545 has_invalidator: false,
546 in_top_level_task,
547 cell_counters: Some(AutoMap::default()),
548 local_tasks: LocalTaskTracker::new(),
549 }
550 }
551
552 fn new_temporary(
553 execution_id: ExecutionId,
554 priority: TaskPriority,
555 in_top_level_task: bool,
556 ) -> Self {
557 Self {
558 task_id: None,
559 execution_id,
560 priority,
561 #[cfg(feature = "verify_determinism")]
562 stateful: false,
563 has_invalidator: false,
564 in_top_level_task,
565 cell_counters: None,
566 local_tasks: LocalTaskTracker::new(),
567 }
568 }
569
570 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
571 if self.execution_id != expected_execution_id {
572 panic!(
573 "Local tasks can only be scheduled/awaited within the same execution of the \
574 parent task that created them"
575 );
576 }
577 }
578}
579
580#[derive(Clone)]
584struct CurrentTaskStateHandle {
585 inner: Arc<CurrentTaskStateInner>,
586}
587
588struct CurrentTaskStateInner {
589 current_task_id: Option<TaskId>,
590 state: RwLock<CurrentTaskState>,
591}
592
593impl CurrentTaskStateHandle {
594 fn new(state: CurrentTaskState) -> Self {
595 Self {
596 inner: Arc::new(CurrentTaskStateInner {
597 current_task_id: state.task_id,
598 state: RwLock::new(state),
599 }),
600 }
601 }
602
603 fn current_task_id(&self) -> Option<TaskId> {
604 self.inner.current_task_id
605 }
606}
607
608impl Deref for CurrentTaskStateHandle {
609 type Target = RwLock<CurrentTaskState>;
610
611 fn deref(&self) -> &Self::Target {
612 &self.inner.state
613 }
614}
615
616task_local! {
618 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
620
621 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
622
623 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
628}
629
630impl<B: Backend + 'static> TurboTasks<B> {
631 pub fn new(backend: B) -> Arc<Self> {
637 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
638 let this = Arc::new_cyclic(|this| Self {
639 this: this.clone(),
640 backend,
641 execution_id_factory,
642 stopped: AtomicBool::new(false),
643 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
644 currently_scheduled_background_jobs: AtomicUsize::new(0),
645 scheduled_tasks: AtomicUsize::new(0),
646 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
647 start: Default::default(),
648 aggregated_update: Default::default(),
649 event_foreground_done: Event::new(|| {
650 || "TurboTasks::event_foreground_done".to_string()
651 }),
652 event_foreground_start: Event::new(|| {
653 || "TurboTasks::event_foreground_start".to_string()
654 }),
655 event_background_done: Event::new(|| {
656 || "TurboTasks::event_background_done".to_string()
657 }),
658 compilation_events: CompilationEventQueue::default(),
659 });
660 this.backend.startup(&*this);
661 this
662 }
663
664 pub fn pin(&self) -> Arc<Self> {
665 self.this.upgrade().unwrap()
666 }
667
668 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
670 where
671 T: ?Sized,
672 F: Fn() -> Fut + Send + Sync + Clone + 'static,
673 Fut: Future<Output = Result<Vc<T>>> + Send,
674 {
675 let id = self.backend.create_transient_task(
676 TransientTaskType::Root(Box::new(move || {
677 let functor = functor.clone();
678 Box::pin(async move {
679 mark_top_level_task();
680 let raw_vc = functor().await?.node;
681 raw_vc.to_non_local().await
682 })
683 })),
684 self,
685 );
686 self.schedule(id, TaskPriority::initial());
687 id
688 }
689
690 pub fn dispose_root_task(&self, task_id: TaskId) {
691 self.backend.dispose_root_task(task_id, self);
692 }
693
694 #[track_caller]
698 fn spawn_once_task<T, Fut>(&self, future: Fut)
699 where
700 T: ?Sized,
701 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
702 {
703 let id = self.backend.create_transient_task(
704 TransientTaskType::Once(Box::pin(async move {
705 mark_top_level_task();
706 let raw_vc = future.await?.node;
707 raw_vc.to_non_local().await
708 })),
709 self,
710 );
711 self.schedule(id, TaskPriority::initial());
712 }
713
714 pub async fn run_once<T: TraceRawVcs + Send + 'static>(
715 &self,
716 future: impl Future<Output = Result<T>> + Send + 'static,
717 ) -> Result<T> {
718 let (tx, rx) = tokio::sync::oneshot::channel();
719 self.spawn_once_task(async move {
720 mark_top_level_task();
721 let result = future.await;
722 tx.send(result)
723 .map_err(|_| anyhow!("unable to send result"))?;
724 Ok(Completion::new())
725 });
726
727 rx.await?
728 }
729
730 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
731 pub async fn run<T: TraceRawVcs + Send + 'static>(
732 &self,
733 future: impl Future<Output = Result<T>> + Send + 'static,
734 ) -> Result<T, TurboTasksExecutionError> {
735 self.begin_foreground_job();
736 let execution_id = self.execution_id_factory.wrapping_get();
738 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
739 execution_id,
740 TaskPriority::initial(),
741 true, ));
743
744 let result = TURBO_TASKS
745 .scope(
746 self.pin(),
747 CURRENT_TASK_STATE.scope(current_task_state, async {
748 let result = CaptureFuture::new(future).await;
749
750 wait_for_local_tasks().await;
752
753 match result {
754 Ok(Ok(value)) => Ok(value),
755 Ok(Err(err)) => Err(err.into()),
756 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
757 }
758 }),
759 )
760 .await;
761 self.finish_foreground_job();
762 result
763 }
764
765 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
766 let this = self.pin();
767 tokio::spawn(async move {
768 this.pin()
769 .run_once(async move {
770 this.finish_foreground_job();
771 future.await;
772 this.begin_foreground_job();
773 Ok(())
774 })
775 .await
776 .unwrap()
777 });
778 }
779
780 pub(crate) fn native_call(
781 &self,
782 native_fn: &'static NativeFunction,
783 this: Option<RawVc>,
784 arg: &mut dyn DynTaskInputsStorage,
785 persistence: TaskPersistence,
786 ) -> RawVc {
787 RawVc::task_output(self.backend.get_or_create_task(
788 native_fn,
789 this,
790 arg,
791 current_task_if_available("turbo_function calls"),
792 persistence,
793 self,
794 ))
795 }
796
797 pub fn dynamic_call(
798 &self,
799 native_fn: &'static NativeFunction,
800 this: Option<RawVc>,
801 arg: &mut dyn DynTaskInputsStorage,
802 inputs_resolved: InputResolution,
803 persistence: TaskPersistence,
804 ) -> RawVc {
805 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
806 return self.native_call(native_fn, this, arg, persistence);
807 }
808 let arg = arg.take_box();
810 let task_type = LocalTaskSpec {
811 task_type: LocalTaskType::ResolveNative { native_fn },
812 this,
813 arg,
814 };
815 self.schedule_local_task(task_type, persistence)
816 }
817
818 pub fn trait_call(
819 &self,
820 trait_method: &'static TraitMethod,
821 this: RawVc,
822 arg: &mut dyn DynTaskInputsStorage,
823 inputs_resolved: InputResolution,
824 persistence: TaskPersistence,
825 ) -> RawVc {
826 if let Some((_, cell_id)) = this.as_task_cell() {
830 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
831 Some(native_fn) => {
832 if let Some(filter) = native_fn.arg_meta.filter_owned {
833 let (resolved, mut arg) = (filter)(arg);
834 return self.dynamic_call(
835 native_fn,
836 Some(this),
837 &mut arg,
838 resolved,
839 persistence,
840 );
841 } else {
842 return self.dynamic_call(
843 native_fn,
844 Some(this),
845 arg,
846 inputs_resolved,
847 persistence,
848 );
849 }
850 }
851 None => {
852 }
856 }
857 }
858
859 let task_type = LocalTaskSpec {
861 task_type: LocalTaskType::ResolveTrait { trait_method },
862 this: Some(this),
863 arg: arg.take_box(),
864 };
865
866 self.schedule_local_task(task_type, persistence)
867 }
868
869 #[track_caller]
870 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
871 self.begin_foreground_job();
872 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
873
874 self.priority_runner.schedule(
875 &self.pin(),
876 ScheduledTask::Task {
877 task_id,
878 span: Span::current(),
879 },
880 priority,
881 );
882 }
883
884 fn schedule_local_task(
885 &self,
886 ty: LocalTaskSpec,
887 persistence: TaskPersistence,
889 ) -> RawVc {
890 let task_type = ty.task_type;
891 let (global_task_state, execution_id, priority, local_task_id) =
892 CURRENT_TASK_STATE.with(|gts| {
893 let mut gts_write = gts.write().unwrap();
894 let local_task_id = gts_write.local_tasks.create(task_type);
895 (
896 gts.clone(),
897 gts_write.execution_id,
898 gts_write.priority,
899 local_task_id,
900 )
901 });
902
903 self.priority_runner.schedule(
904 &self.pin(),
905 ScheduledTask::LocalTask {
906 ty,
907 persistence,
908 local_task_id,
909 global_task_state,
910 span: Span::current(),
911 },
912 priority,
913 );
914
915 RawVc::local_output(execution_id, local_task_id, persistence)
916 }
917
918 fn begin_foreground_job(&self) {
919 if self
920 .currently_scheduled_foreground_jobs
921 .fetch_add(1, Ordering::AcqRel)
922 == 0
923 {
924 *self.start.lock().unwrap() = Some(Instant::now());
925 self.event_foreground_start.notify(usize::MAX);
926 self.backend.idle_end(self);
927 }
928 }
929
930 fn finish_foreground_job(&self) {
931 if self
932 .currently_scheduled_foreground_jobs
933 .fetch_sub(1, Ordering::AcqRel)
934 == 1
935 {
936 self.backend.idle_start(self);
937 let total = self.scheduled_tasks.load(Ordering::Acquire);
940 self.scheduled_tasks.store(0, Ordering::Release);
941 if let Some(start) = *self.start.lock().unwrap() {
942 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
943 if let Some(update) = update.as_mut() {
944 update.0 += start.elapsed();
945 update.1 += total;
946 } else {
947 *update = Some((start.elapsed(), total));
948 }
949 }
950 self.event_foreground_done.notify(usize::MAX);
951 }
952 }
953
954 fn begin_background_job(&self) {
955 self.currently_scheduled_background_jobs
956 .fetch_add(1, Ordering::Relaxed);
957 }
958
959 fn finish_background_job(&self) {
960 if self
961 .currently_scheduled_background_jobs
962 .fetch_sub(1, Ordering::Relaxed)
963 == 1
964 {
965 self.event_background_done.notify(usize::MAX);
966 }
967 }
968
969 pub fn get_in_progress_count(&self) -> usize {
970 self.currently_scheduled_foreground_jobs
971 .load(Ordering::Acquire)
972 }
973
974 pub async fn wait_task_completion(
986 &self,
987 id: TaskId,
988 consistency: ReadConsistency,
989 ) -> Result<()> {
990 read_task_output(
991 self,
992 id,
993 ReadOutputOptions {
994 tracking: ReadTracking::Untracked,
996 consistency,
997 },
998 )
999 .await?;
1000 Ok(())
1001 }
1002
1003 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1006 self.aggregated_update_info(aggregation, Duration::MAX)
1007 .await
1008 .unwrap()
1009 }
1010
1011 pub async fn aggregated_update_info(
1015 &self,
1016 aggregation: Duration,
1017 timeout: Duration,
1018 ) -> Option<UpdateInfo> {
1019 let listener = self
1020 .event_foreground_done
1021 .listen_with_note(|| || "wait for update info".to_string());
1022 let wait_for_finish = {
1023 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1024 if aggregation.is_zero() {
1025 if let Some((duration, tasks)) = update.take() {
1026 return Some(UpdateInfo {
1027 duration,
1028 tasks,
1029 reasons: take(reason_set),
1030 placeholder_for_future_fields: (),
1031 });
1032 } else {
1033 true
1034 }
1035 } else {
1036 update.is_none()
1037 }
1038 };
1039 if wait_for_finish {
1040 if timeout == Duration::MAX {
1041 listener.await;
1043 } else {
1044 let start_listener = self
1046 .event_foreground_start
1047 .listen_with_note(|| || "wait for update info".to_string());
1048 if self
1049 .currently_scheduled_foreground_jobs
1050 .load(Ordering::Acquire)
1051 == 0
1052 {
1053 start_listener.await;
1054 } else {
1055 drop(start_listener);
1056 }
1057 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1058 return None;
1060 }
1061 }
1062 }
1063 if !aggregation.is_zero() {
1064 loop {
1065 select! {
1066 () = tokio::time::sleep(aggregation) => {
1067 break;
1068 }
1069 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1070 }
1072 }
1073 }
1074 }
1075 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1076 if let Some((duration, tasks)) = update.take() {
1077 Some(UpdateInfo {
1078 duration,
1079 tasks,
1080 reasons: take(reason_set),
1081 placeholder_for_future_fields: (),
1082 })
1083 } else {
1084 panic!("aggregated_update_info must not called concurrently")
1085 }
1086 }
1087
1088 pub async fn wait_background_done(&self) {
1089 let listener = self.event_background_done.listen();
1090 if self
1091 .currently_scheduled_background_jobs
1092 .load(Ordering::Acquire)
1093 != 0
1094 {
1095 listener.await;
1096 }
1097 }
1098
1099 pub async fn stop_and_wait(&self) {
1100 turbo_tasks_future_scope(self.pin(), async move {
1101 self.backend.stopping(self);
1102 self.stopped.store(true, Ordering::Release);
1103 {
1104 let listener = self
1105 .event_foreground_done
1106 .listen_with_note(|| || "wait for stop".to_string());
1107 if self
1108 .currently_scheduled_foreground_jobs
1109 .load(Ordering::Acquire)
1110 != 0
1111 {
1112 listener.await;
1113 }
1114 }
1115 {
1116 let listener = self.event_background_done.listen();
1117 if self
1118 .currently_scheduled_background_jobs
1119 .load(Ordering::Acquire)
1120 != 0
1121 {
1122 listener.await;
1123 }
1124 }
1125 self.backend.stop(self);
1126 })
1127 .await;
1128 }
1129
1130 #[track_caller]
1131 pub(crate) fn schedule_background_job<T>(&self, func: T)
1132 where
1133 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1134 T::CallOnceFuture: Send,
1135 {
1136 let mut this = self.pin();
1137 self.begin_background_job();
1138 tokio::spawn(
1139 TURBO_TASKS
1140 .scope(this.clone(), async move {
1141 if !this.stopped.load(Ordering::Acquire) {
1142 this = func(this).await;
1143 }
1144 this.finish_background_job();
1145 })
1146 .in_current_span(),
1147 );
1148 }
1149
1150 fn finish_current_task_state(&self) -> FinishedTaskState {
1151 CURRENT_TASK_STATE.with(|cell| {
1152 let current_task_state = &*cell.write().unwrap();
1153 FinishedTaskState {
1154 #[cfg(feature = "verify_determinism")]
1155 stateful: current_task_state.stateful,
1156 has_invalidator: current_task_state.has_invalidator,
1157 }
1158 })
1159 }
1160
1161 pub fn backend(&self) -> &B {
1162 &self.backend
1163 }
1164
1165 pub fn get_current_task_priority(&self) -> TaskPriority {
1166 CURRENT_TASK_STATE
1167 .try_with(|task_state| task_state.read().unwrap().priority)
1168 .unwrap_or(TaskPriority::initial())
1169 }
1170
1171 pub fn is_idle(&self) -> bool {
1172 self.currently_scheduled_foreground_jobs
1173 .load(Ordering::Acquire)
1174 == 0
1175 }
1176
1177 #[track_caller]
1178 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1179 self.schedule_background_job(async move |this| {
1180 this.backend.run_backend_job(job, &*this).await;
1181 this
1182 })
1183 }
1184}
1185
1186struct TurboTasksExecutor;
1187
1188async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1193 match AssertUnwindSafe(f).catch_unwind().await {
1194 Ok(r) => r,
1195 Err(_) => {
1196 eprintln!(
1197 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1198 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1199 https://github.com/vercel/next.js/discussions and include the panic message \
1200 and stack trace above.\n\nAborting."
1201 );
1202 abort();
1203 }
1204 }
1205}
1206
1207impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1208 type Future = impl Future<Output = ()> + Send + 'static;
1209
1210 fn execute(
1211 &self,
1212 this: &Arc<TurboTasks<B>>,
1213 scheduled_task: ScheduledTask,
1214 priority: TaskPriority,
1215 ) -> Self::Future {
1216 match scheduled_task {
1217 ScheduledTask::Task { task_id, span } => {
1218 let this2 = this.clone();
1219 let this = this.clone();
1220 let future = async move {
1221 abort_on_panic(async {
1222 let execution_id = this.execution_id_factory.wrapping_get();
1225 let current_task_state =
1226 CurrentTaskStateHandle::new(CurrentTaskState::new(
1227 task_id,
1228 execution_id,
1229 priority,
1230 false, ));
1232 let single_execution_future = async {
1233 if this.stopped.load(Ordering::Acquire) {
1234 this.backend.task_execution_canceled(task_id, &*this);
1235 return None;
1236 }
1237
1238 let TaskExecutionSpec { future, span } = this
1239 .backend
1240 .try_start_task_execution(task_id, priority, &*this)?;
1241
1242 async {
1243 let result = CaptureFuture::new(future).await;
1244
1245 wait_for_local_tasks().await;
1247
1248 let result = match result {
1249 Ok(Ok(raw_vc)) => {
1250 raw_vc
1253 .to_non_local_unchecked_sync(&*this)
1254 .map_err(|err| err.into())
1255 }
1256 Ok(Err(err)) => Err(err.into()),
1257 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1258 };
1259
1260 let finished_state = this.finish_current_task_state();
1261 let cell_counters = CURRENT_TASK_STATE
1262 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1263 this.backend.task_execution_completed(
1264 task_id,
1265 result,
1266 &cell_counters,
1267 #[cfg(feature = "verify_determinism")]
1268 finished_state.stateful,
1269 finished_state.has_invalidator,
1270 &*this,
1271 )
1272 }
1273 .instrument(span)
1274 .await
1275 };
1276 if let Some(stale_priority) = CURRENT_TASK_STATE
1277 .scope(current_task_state, single_execution_future)
1278 .await
1279 {
1280 this.schedule(task_id, stale_priority);
1283 }
1284 this.finish_foreground_job();
1285 })
1286 .await
1287 };
1288
1289 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1290 }
1291 ScheduledTask::LocalTask {
1292 ty,
1293 persistence,
1294 local_task_id,
1295 global_task_state,
1296 span,
1297 } => {
1298 let this2 = this.clone();
1299 let this = this.clone();
1300 let task_type = ty.task_type;
1301 let future = async move {
1302 let span = match &ty.task_type {
1303 LocalTaskType::ResolveNative { native_fn } => {
1304 native_fn.resolve_span(priority)
1305 }
1306 LocalTaskType::ResolveTrait { trait_method } => {
1307 trait_method.resolve_span(priority)
1308 }
1309 };
1310 abort_on_panic(
1311 async move {
1312 let result = match ty.task_type {
1313 LocalTaskType::ResolveNative { native_fn } => {
1314 LocalTaskType::run_resolve_native(
1315 native_fn,
1316 ty.this,
1317 &*ty.arg,
1318 persistence,
1319 this,
1320 )
1321 .await
1322 }
1323 LocalTaskType::ResolveTrait { trait_method } => {
1324 LocalTaskType::run_resolve_trait(
1325 trait_method,
1326 ty.this.unwrap(),
1327 &*ty.arg,
1328 persistence,
1329 this,
1330 )
1331 .await
1332 }
1333 };
1334
1335 let output = match result {
1336 Ok(raw_vc) => OutputContent::Link(raw_vc),
1337 Err(err) => OutputContent::Error(
1338 TurboTasksExecutionError::from(err)
1339 .with_local_task_context(task_type.to_string()),
1340 ),
1341 };
1342
1343 CURRENT_TASK_STATE.with(move |gts| {
1344 gts.write()
1345 .unwrap()
1346 .local_tasks
1347 .complete(local_task_id, output);
1348 });
1349 }
1350 .instrument(span),
1351 )
1352 .await
1353 };
1354 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1355
1356 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1357 }
1358 }
1359 }
1360}
1361
1362struct FinishedTaskState {
1363 #[cfg(feature = "verify_determinism")]
1366 stateful: bool,
1367
1368 has_invalidator: bool,
1370}
1371
1372impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1373 fn dynamic_call(
1374 &self,
1375 native_fn: &'static NativeFunction,
1376 this: Option<RawVc>,
1377 arg: &mut dyn DynTaskInputsStorage,
1378 inputs_resolved: InputResolution,
1379 persistence: TaskPersistence,
1380 ) -> RawVc {
1381 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1382 }
1383 fn native_call(
1384 &self,
1385 native_fn: &'static NativeFunction,
1386 this: Option<RawVc>,
1387 arg: &mut dyn DynTaskInputsStorage,
1388 persistence: TaskPersistence,
1389 ) -> RawVc {
1390 self.native_call(native_fn, this, arg, persistence)
1391 }
1392 fn trait_call(
1393 &self,
1394 trait_method: &'static TraitMethod,
1395 this: RawVc,
1396 arg: &mut dyn DynTaskInputsStorage,
1397 inputs_resolved: InputResolution,
1398 persistence: TaskPersistence,
1399 ) -> RawVc {
1400 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1401 }
1402
1403 #[track_caller]
1404 fn run(
1405 &self,
1406 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1407 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1408 let this = self.pin();
1409 Box::pin(async move { this.run(future).await })
1410 }
1411
1412 #[track_caller]
1413 fn run_once(
1414 &self,
1415 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1416 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1417 let this = self.pin();
1418 Box::pin(async move { this.run_once(future).await })
1419 }
1420
1421 #[track_caller]
1422 fn run_once_with_reason(
1423 &self,
1424 reason: StaticOrArc<dyn InvalidationReason>,
1425 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1426 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1427 {
1428 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1429 reason_set.insert(reason);
1430 }
1431 let this = self.pin();
1432 Box::pin(async move { this.run_once(future).await })
1433 }
1434
1435 #[track_caller]
1436 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1437 self.start_once_process(future)
1438 }
1439
1440 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1441 if let Err(e) = self.compilation_events.send(event) {
1442 tracing::warn!("Failed to send compilation event: {e}");
1443 }
1444 }
1445
1446 fn get_task_name(&self, task: TaskId) -> String {
1447 self.backend.get_task_name(task, self)
1448 }
1449}
1450
1451impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1452 #[instrument(level = "info", skip_all, name = "invalidate")]
1453 fn invalidate(&self, task: TaskId) {
1454 self.backend.invalidate_task(task, self);
1455 }
1456
1457 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1458 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1459 {
1460 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1461 reason_set.insert(reason);
1462 }
1463 self.backend.invalidate_task(task, self);
1464 }
1465
1466 fn invalidate_serialization(&self, task: TaskId) {
1467 self.backend.invalidate_serialization(task, self);
1468 }
1469
1470 #[track_caller]
1471 fn try_read_task_output(
1472 &self,
1473 task: TaskId,
1474 options: ReadOutputOptions,
1475 ) -> Result<Result<RawVc, EventListener>> {
1476 if options.consistency == ReadConsistency::Eventual {
1477 debug_assert_not_in_top_level_task("read_task_output");
1478 }
1479 self.backend.try_read_task_output(
1480 task,
1481 current_task_if_available("reading Vcs"),
1482 options,
1483 self,
1484 )
1485 }
1486
1487 #[track_caller]
1488 fn try_read_task_cell(
1489 &self,
1490 task: TaskId,
1491 index: CellId,
1492 options: ReadCellOptions,
1493 ) -> Result<Result<TypedCellContent, EventListener>> {
1494 let reader = current_task_if_available("reading Vcs");
1495 self.backend
1496 .try_read_task_cell(task, index, reader, options, self)
1497 }
1498
1499 fn try_read_own_task_cell(
1500 &self,
1501 current_task: TaskId,
1502 index: CellId,
1503 ) -> Result<TypedCellContent> {
1504 self.backend
1505 .try_read_own_task_cell(current_task, index, self)
1506 }
1507
1508 #[track_caller]
1509 fn try_read_local_output(
1510 &self,
1511 execution_id: ExecutionId,
1512 local_task_id: LocalTaskId,
1513 ) -> Result<Result<RawVc, EventListener>> {
1514 debug_assert_not_in_top_level_task("read_local_output");
1515 CURRENT_TASK_STATE.with(|gts| {
1516 let gts_read = gts.read().unwrap();
1517
1518 gts_read.assert_execution_id(execution_id);
1523
1524 match gts_read.local_tasks.get(local_task_id) {
1525 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1526 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1527 }
1528 })
1529 }
1530
1531 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1532 self.backend.read_task_collectibles(
1535 task,
1536 trait_id,
1537 current_task_if_available("reading collectibles"),
1538 self,
1539 )
1540 }
1541
1542 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1543 self.backend.emit_collectible(
1544 trait_type,
1545 collectible,
1546 current_task("emitting collectible"),
1547 self,
1548 );
1549 }
1550
1551 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1552 self.backend.unemit_collectible(
1553 trait_type,
1554 collectible,
1555 count,
1556 current_task("emitting collectible"),
1557 self,
1558 );
1559 }
1560
1561 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1562 for (&collectible, &count) in collectibles {
1563 if count > 0 {
1564 self.backend.unemit_collectible(
1565 trait_type,
1566 collectible,
1567 count as u32,
1568 current_task("emitting collectible"),
1569 self,
1570 );
1571 }
1572 }
1573 }
1574
1575 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1576 self.try_read_own_task_cell(task, index)
1577 }
1578
1579 fn update_own_task_cell(
1580 &self,
1581 task: TaskId,
1582 index: CellId,
1583 content: CellContent,
1584 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1585 content_hash: Option<CellHash>,
1586 verification_mode: VerificationMode,
1587 ) {
1588 self.backend.update_task_cell(
1589 task,
1590 index,
1591 content,
1592 updated_key_hashes,
1593 content_hash,
1594 verification_mode,
1595 self,
1596 );
1597 }
1598
1599 fn connect_task(&self, task: TaskId) {
1600 self.backend
1601 .connect_task(task, current_task_if_available("connecting task"), self);
1602 }
1603
1604 fn mark_own_task_as_finished(&self, task: TaskId) {
1605 self.backend.mark_own_task_as_finished(task, self);
1606 }
1607
1608 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1611 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1614 global_task_state
1615 .write()
1616 .unwrap()
1617 .local_tasks
1618 .register_detached();
1619 let wrapped = async move {
1620 struct DropGuard;
1622 impl Drop for DropGuard {
1623 fn drop(&mut self) {
1624 CURRENT_TASK_STATE
1625 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1626 }
1627 }
1628 let _guard = DropGuard;
1629 fut.await;
1630 };
1631 tokio::spawn(TURBO_TASKS.scope(
1632 turbo_tasks(),
1633 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1634 ));
1635 }
1636
1637 fn task_statistics(&self) -> &TaskStatisticsApi {
1638 self.backend.task_statistics()
1639 }
1640
1641 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1642 let this = self.pin();
1643 Box::pin(async move {
1644 this.stop_and_wait().await;
1645 })
1646 }
1647
1648 fn subscribe_to_compilation_events(
1649 &self,
1650 event_types: Option<Vec<String>>,
1651 ) -> Receiver<Arc<dyn CompilationEvent>> {
1652 self.compilation_events.subscribe(event_types)
1653 }
1654
1655 fn is_tracking_dependencies(&self) -> bool {
1656 self.backend.is_tracking_dependencies()
1657 }
1658}
1659
1660async fn wait_for_local_tasks() {
1661 let listener =
1662 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
1663 let Some(listener) = listener else {
1664 return;
1665 };
1666 listener.await;
1667}
1668
1669pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
1670 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1671 Ok(id) => id,
1672 Err(_) => panic!(
1673 "{from} can only be used in the context of a turbo_tasks task execution or \
1674 turbo_tasks run"
1675 ),
1676 }
1677}
1678
1679pub(crate) fn current_task(from: &str) -> TaskId {
1680 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1681 Ok(Some(id)) => id,
1682 Ok(None) | Err(_) => {
1683 panic!("{from} can only be used in the context of a turbo_tasks task execution")
1684 }
1685 }
1686}
1687
1688#[track_caller]
1691pub(crate) fn debug_assert_in_top_level_task(message: &str) {
1692 if !cfg!(debug_assertions) {
1693 return;
1694 }
1695
1696 let in_top_level = CURRENT_TASK_STATE
1697 .try_with(|ts| ts.read().unwrap().in_top_level_task)
1698 .unwrap_or(true);
1699 if !in_top_level {
1700 panic!("{message}");
1701 }
1702}
1703
1704#[track_caller]
1705pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
1706 if !cfg!(debug_assertions) {
1707 return;
1708 }
1709
1710 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
1713 .try_with(|&suppressed| suppressed)
1714 .unwrap_or(false);
1715 if suppressed {
1716 return;
1717 }
1718
1719 let in_top_level = CURRENT_TASK_STATE
1720 .try_with(|ts| ts.read().unwrap().in_top_level_task)
1721 .unwrap_or(false);
1722 if in_top_level {
1723 panic!(
1724 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
1725 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
1726 reads to avoid leaking inconsistent return values."
1727 );
1728 }
1729}
1730
1731pub async fn run<T: Send + 'static>(
1732 tt: Arc<dyn TurboTasksApi>,
1733 future: impl Future<Output = Result<T>> + Send + 'static,
1734) -> Result<T> {
1735 let (tx, rx) = tokio::sync::oneshot::channel();
1736
1737 tt.run(Box::pin(async move {
1738 let result = future.await?;
1739 tx.send(result)
1740 .map_err(|_| anyhow!("unable to send result"))?;
1741 Ok(())
1742 }))
1743 .await?;
1744
1745 Ok(rx.await?)
1746}
1747
1748pub async fn run_once<T: Send + 'static>(
1749 tt: Arc<dyn TurboTasksApi>,
1750 future: impl Future<Output = Result<T>> + Send + 'static,
1751) -> Result<T> {
1752 let (tx, rx) = tokio::sync::oneshot::channel();
1753
1754 tt.run_once(Box::pin(async move {
1755 let result = future.await?;
1756 tx.send(result)
1757 .map_err(|_| anyhow!("unable to send result"))?;
1758 Ok(())
1759 }))
1760 .await?;
1761
1762 Ok(rx.await?)
1763}
1764
1765pub async fn run_once_with_reason<T: Send + 'static>(
1766 tt: Arc<dyn TurboTasksApi>,
1767 reason: impl InvalidationReason,
1768 future: impl Future<Output = Result<T>> + Send + 'static,
1769) -> Result<T> {
1770 let (tx, rx) = tokio::sync::oneshot::channel();
1771
1772 tt.run_once_with_reason(
1773 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
1774 Box::pin(async move {
1775 let result = future.await?;
1776 tx.send(result)
1777 .map_err(|_| anyhow!("unable to send result"))?;
1778 Ok(())
1779 }),
1780 )
1781 .await?;
1782
1783 Ok(rx.await?)
1784}
1785
1786pub fn dynamic_call(
1788 func: &'static NativeFunction,
1789 this: Option<RawVc>,
1790 arg: &mut dyn DynTaskInputsStorage,
1791 inputs_resolved: InputResolution,
1792 persistence: TaskPersistence,
1793) -> RawVc {
1794 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
1795}
1796
1797pub fn trait_call(
1799 trait_method: &'static TraitMethod,
1800 this: RawVc,
1801 arg: &mut dyn DynTaskInputsStorage,
1802 inputs_resolved: InputResolution,
1803 persistence: TaskPersistence,
1804) -> RawVc {
1805 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
1806}
1807
1808pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
1809 TURBO_TASKS.with(|arc| arc.clone())
1810}
1811
1812pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
1813 TURBO_TASKS.with(Arc::downgrade)
1814}
1815
1816pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
1817 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
1818}
1819
1820pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
1821 TURBO_TASKS.with(|arc| func(arc))
1822}
1823
1824pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
1825 TURBO_TASKS.sync_scope(tt, f)
1826}
1827
1828pub fn turbo_tasks_future_scope<T>(
1829 tt: Arc<dyn TurboTasksApi>,
1830 f: impl Future<Output = T>,
1831) -> impl Future<Output = T> {
1832 TURBO_TASKS.scope(tt, f)
1833}
1834
1835pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
1840 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
1841}
1842
1843pub fn mark_finished() {
1846 with_turbo_tasks(|tt| {
1847 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
1848 });
1849}
1850
1851pub fn get_serialization_invalidator() -> SerializationInvalidator {
1857 CURRENT_TASK_STATE.with(|cell| {
1858 let CurrentTaskState {
1859 task_id,
1860 #[cfg(feature = "verify_determinism")]
1861 stateful,
1862 ..
1863 } = &mut *cell.write().unwrap();
1864 #[cfg(feature = "verify_determinism")]
1865 {
1866 *stateful = true;
1867 }
1868 let Some(task_id) = *task_id else {
1869 panic!(
1870 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
1871 task execution"
1872 );
1873 };
1874 SerializationInvalidator::new(task_id)
1875 })
1876}
1877
1878pub fn mark_invalidator() {
1879 CURRENT_TASK_STATE.with(|cell| {
1880 let CurrentTaskState {
1881 has_invalidator, ..
1882 } = &mut *cell.write().unwrap();
1883 *has_invalidator = true;
1884 })
1885}
1886
1887pub fn mark_stateful() {
1893 #[cfg(feature = "verify_determinism")]
1894 {
1895 CURRENT_TASK_STATE.with(|cell| {
1896 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
1897 *stateful = true;
1898 })
1899 }
1900 }
1902
1903pub fn mark_top_level_task() {
1907 if cfg!(debug_assertions) {
1908 CURRENT_TASK_STATE.with(|cell| {
1909 cell.write().unwrap().in_top_level_task = true;
1910 })
1911 }
1912}
1913
1914pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
1925 if cfg!(debug_assertions) {
1926 CURRENT_TASK_STATE.with(|cell| {
1927 cell.write().unwrap().in_top_level_task = false;
1928 })
1929 }
1930}
1931
1932pub fn prevent_gc() {
1933 }
1935
1936pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
1937 with_turbo_tasks(|tt| {
1938 let raw_vc = collectible.node.node;
1939 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
1940 })
1941}
1942
1943pub(crate) async fn read_task_output(
1944 this: &dyn TurboTasksApi,
1945 id: TaskId,
1946 options: ReadOutputOptions,
1947) -> Result<RawVc> {
1948 loop {
1949 match this.try_read_task_output(id, options)? {
1950 Ok(result) => return Ok(result),
1951 Err(listener) => listener.await,
1952 }
1953 }
1954}
1955
1956#[derive(Clone, Copy)]
1962pub struct CurrentCellRef {
1963 current_task: TaskId,
1964 index: CellId,
1965}
1966
1967type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
1968
1969impl CurrentCellRef {
1970 fn conditional_update<T>(
1972 &self,
1973 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
1974 ) where
1975 T: VcValueType,
1976 {
1977 self.conditional_update_with_shared_reference(|old_shared_reference| {
1978 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
1979 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
1980 Some((
1981 SharedReference::new(triomphe::Arc::new(new_value)),
1982 updated_key_hashes,
1983 content_hash,
1984 ))
1985 })
1986 }
1987
1988 fn conditional_update_with_shared_reference(
1990 &self,
1991 functor: impl FnOnce(
1992 Option<&SharedReference>,
1993 ) -> Option<(
1994 SharedReference,
1995 Option<SmallVec<[u64; 2]>>,
1996 Option<CellHash>,
1997 )>,
1998 ) {
1999 let tt = turbo_tasks();
2000 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2001 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2002 if let Some((update, updated_key_hashes, content_hash)) = update {
2003 tt.update_own_task_cell(
2004 self.current_task,
2005 self.index,
2006 CellContent(Some(update)),
2007 updated_key_hashes,
2008 content_hash,
2009 VerificationMode::EqualityCheck,
2010 )
2011 }
2012 }
2013
2014 pub fn compare_and_update<T>(&self, new_value: T)
2049 where
2050 T: PartialEq + VcValueType,
2051 {
2052 self.conditional_update(|old_value| {
2053 if let Some(old_value) = old_value
2054 && old_value == &new_value
2055 {
2056 return None;
2057 }
2058 Some((new_value, None, None))
2059 });
2060 }
2061
2062 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2070 where
2071 T: VcValueType + PartialEq,
2072 {
2073 self.conditional_update_with_shared_reference(|old_sr| {
2074 if let Some(old_sr) = old_sr {
2075 let old_value = extract_sr_value::<T>(old_sr);
2076 let new_value = extract_sr_value::<T>(&new_shared_reference);
2077 if old_value == new_value {
2078 return None;
2079 }
2080 }
2081 Some((new_shared_reference, None, None))
2082 });
2083 }
2084
2085 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2094 where
2095 T: PartialEq + DeterministicHash + VcValueType,
2096 {
2097 self.conditional_update(|old_value| {
2098 if let Some(old_value) = old_value
2099 && old_value == &new_value
2100 {
2101 return None;
2102 }
2103 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2104
2105 Some((new_value, None, Some(content_hash)))
2106 });
2107 }
2108
2109 pub fn hashed_compare_and_update_with_shared_reference<T>(
2115 &self,
2116 new_shared_reference: SharedReference,
2117 ) where
2118 T: VcValueType + PartialEq + DeterministicHash,
2119 {
2120 self.conditional_update_with_shared_reference(move |old_sr| {
2121 if let Some(old_sr) = old_sr {
2122 let old_value = extract_sr_value::<T>(old_sr);
2123 let new_value = extract_sr_value::<T>(&new_shared_reference);
2124 if old_value == new_value {
2125 return None;
2126 }
2127 }
2128 let content_hash =
2129 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2130 Some((new_shared_reference, None, Some(content_hash)))
2131 });
2132 }
2133
2134 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2136 where
2137 T: PartialEq + VcValueType,
2138 VcReadTarget<T>: KeyedEq,
2139 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2140 {
2141 self.conditional_update(|old_value| {
2142 let Some(old_value) = old_value else {
2143 return Some((new_value, None, None));
2144 };
2145 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2146 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2147 let updated_keys = old_value.different_keys(new_value_ref);
2148 if updated_keys.is_empty() {
2149 return None;
2150 }
2151 let updated_key_hashes = updated_keys
2153 .into_iter()
2154 .map(|key| FxBuildHasher.hash_one(key))
2155 .collect();
2156 Some((new_value, Some(updated_key_hashes), None))
2157 });
2158 }
2159
2160 pub fn keyed_compare_and_update_with_shared_reference<T>(
2163 &self,
2164 new_shared_reference: SharedReference,
2165 ) where
2166 T: VcValueType + PartialEq,
2167 VcReadTarget<T>: KeyedEq,
2168 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2169 {
2170 self.conditional_update_with_shared_reference(|old_sr| {
2171 let Some(old_sr) = old_sr else {
2172 return Some((new_shared_reference, None, None));
2173 };
2174 let old_value = extract_sr_value::<T>(old_sr);
2175 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2176 let new_value = extract_sr_value::<T>(&new_shared_reference);
2177 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2178 let updated_keys = old_value.different_keys(new_value);
2179 if updated_keys.is_empty() {
2180 return None;
2181 }
2182 let updated_key_hashes = updated_keys
2184 .into_iter()
2185 .map(|key| FxBuildHasher.hash_one(key))
2186 .collect();
2187 Some((new_shared_reference, Some(updated_key_hashes), None))
2188 });
2189 }
2190
2191 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2193 where
2194 T: VcValueType,
2195 {
2196 let tt = turbo_tasks();
2197 tt.update_own_task_cell(
2198 self.current_task,
2199 self.index,
2200 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2201 None,
2202 None,
2203 verification_mode,
2204 )
2205 }
2206
2207 pub fn update_with_shared_reference(
2215 &self,
2216 shared_ref: SharedReference,
2217 verification_mode: VerificationMode,
2218 ) {
2219 let tt = turbo_tasks();
2220 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2221 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2222 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2223 shared_ref_exp != shared_ref
2225 } else {
2226 true
2227 }
2228 } else {
2229 true
2230 };
2231 if update {
2232 tt.update_own_task_cell(
2233 self.current_task,
2234 self.index,
2235 CellContent(Some(shared_ref)),
2236 None,
2237 None,
2238 verification_mode,
2239 )
2240 }
2241 }
2242}
2243
2244impl From<CurrentCellRef> for RawVc {
2245 fn from(cell: CurrentCellRef) -> Self {
2246 RawVc::task_cell(cell.current_task, cell.index)
2247 }
2248}
2249
2250fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2251 sr.0.downcast_ref::<T>()
2252 .expect("cannot update SharedReference of different type")
2253}
2254
2255pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2256 find_cell_by_id(T::get_value_type_id())
2257}
2258
2259pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2260 CURRENT_TASK_STATE.with(|ts| {
2261 let current_task = current_task("celling turbo_tasks values");
2262 let mut ts = ts.write().unwrap();
2263 let map = ts.cell_counters.as_mut().unwrap();
2264 let current_index = map.entry(ty).or_default();
2265 let index = *current_index;
2266 assert!(
2267 index <= CellId::MAX_CELL_INDEX,
2268 "task allocated more than {} cells of a single type",
2269 CellId::MAX_CELL_INDEX as u64 + 1,
2270 );
2271 *current_index += 1;
2272 CurrentCellRef {
2273 current_task,
2274 index: CellId::new(ty, index),
2275 }
2276 })
2277}
2278
2279pub(crate) async fn read_local_output(
2280 this: &dyn TurboTasksApi,
2281 execution_id: ExecutionId,
2282 local_task_id: LocalTaskId,
2283) -> Result<RawVc> {
2284 loop {
2285 match this.try_read_local_output(execution_id, local_task_id)? {
2286 Ok(raw_vc) => return Ok(raw_vc),
2287 Err(event_listener) => event_listener.await,
2288 }
2289 }
2290}