1use std::{
2 cell::Cell,
3 cmp::Reverse,
4 fmt::{Debug, Display},
5 future::Future,
6 hash::{BuildHasher, BuildHasherDefault},
7 mem::take,
8 ops::Deref,
9 panic::AssertUnwindSafe,
10 pin::Pin,
11 process::abort,
12 sync::{
13 Arc, Mutex, RwLock, Weak,
14 atomic::{AtomicBool, AtomicUsize, Ordering},
15 },
16 task::{Context, Poll, Waker},
17 time::{Duration, Instant},
18};
19
20use anyhow::{Result, anyhow};
21use auto_hash_map::AutoMap;
22use bincode::{Decode, Encode};
23use either::Either;
24use futures::FutureExt;
25use rustc_hash::{FxBuildHasher, FxHasher};
26use serde::{Deserialize, Serialize};
27use smallvec::SmallVec;
28use tokio::{select, sync::mpsc::Receiver, task_local};
29use tracing::{Instrument, Span, instrument};
30use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash128};
31
32use crate::{
33 CellId, Completion, InvalidationReason, InvalidationReasonSet, OutputContent, RawVc,
34 ReadCellOptions, ReadOutcome, ReadOutputOptions, ResolvedVc, SharedReference, TaskId,
35 TraitMethod, ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType,
36 backend::{
37 Backend, CellContent, CellHash, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType,
38 TurboTasksExecutionError, TypedCellContent, VerificationMode,
39 },
40 capture_future::CaptureFuture,
41 dyn_task_inputs::DynTaskInputsStorage,
42 event::{Event, EventListener},
43 id::{ExecutionId, LocalTaskId, TraitTypeId},
44 keyed::KeyedEq,
45 local_task_tracker::LocalTaskTracker,
46 macro_helpers::NativeFunction,
47 message_queue::{CompilationEvent, CompilationEventQueue},
48 priority_runner::{Claimable, Executor, PriorityRunner},
49 registry,
50 serialization_invalidation::SerializationInvalidator,
51 task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
52 task_statistics::TaskStatisticsApi,
53 trace::TraceRawVcs,
54 util::{IdFactory, StaticOrArc},
55};
56
57pub trait TurboTasksCallApi: Sync + Send {
60 fn dynamic_call(
67 &self,
68 native_fn: &'static NativeFunction,
69 this: Option<RawVc>,
70 arg: &mut dyn DynTaskInputsStorage,
71 inputs_resolved: InputResolution,
72 persistence: TaskPersistence,
73 ) -> RawVc;
74 fn native_call(
77 &self,
78 native_fn: &'static NativeFunction,
79 this: Option<RawVc>,
80 arg: &mut dyn DynTaskInputsStorage,
81 persistence: TaskPersistence,
82 ) -> RawVc;
83 fn trait_call(
90 &self,
91 trait_method: &'static TraitMethod,
92 this: RawVc,
93 arg: &mut dyn DynTaskInputsStorage,
94 inputs_resolved: InputResolution,
95 persistence: TaskPersistence,
96 ) -> RawVc;
97
98 fn run(
99 &self,
100 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
101 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>>;
102 fn run_once(
103 &self,
104 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
105 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
106 fn run_once_with_reason(
107 &self,
108 reason: StaticOrArc<dyn InvalidationReason>,
109 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
110 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
111 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
112
113 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
115
116 fn get_task_name(&self, task: TaskId) -> String;
118}
119
120pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
126 fn invalidate(&self, task: TaskId);
127 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>);
128
129 fn invalidate_serialization(&self, task: TaskId);
130
131 fn try_read_task_output(
132 &self,
133 task: TaskId,
134 options: ReadOutputOptions,
135 ) -> Result<ReadOutcome<RawVc>>;
136
137 fn try_read_task_cell(
138 &self,
139 task: TaskId,
140 index: CellId,
141 options: ReadCellOptions,
142 ) -> Result<ReadOutcome<TypedCellContent>>;
143
144 fn try_read_local_output(
159 &self,
160 execution_id: ExecutionId,
161 local_task_id: LocalTaskId,
162 ) -> Result<Result<RawVc, EventListener>>;
163
164 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap;
165
166 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool;
172
173 #[cfg(feature = "inline_execution_stats")]
176 fn note_waited_for_in_progress_task(&self);
177
178 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc);
179 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32);
180 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap);
181
182 fn try_read_own_task_cell(
185 &self,
186 current_task: TaskId,
187 index: CellId,
188 ) -> Result<TypedCellContent>;
189
190 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent>;
191 fn update_own_task_cell(
192 &self,
193 task: TaskId,
194 index: CellId,
195 content: CellContent,
196 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
197 content_hash: Option<CellHash>,
198 verification_mode: VerificationMode,
199 );
200 fn mark_own_task_as_finished(&self, task: TaskId);
201
202 fn connect_task(&self, task: TaskId);
203
204 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
209
210 fn task_statistics(&self) -> &TaskStatisticsApi;
211
212 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
213
214 fn subscribe_to_compilation_events(
215 &self,
216 event_types: Option<Vec<String>>,
217 ) -> Receiver<Arc<dyn CompilationEvent>>;
218
219 fn is_tracking_dependencies(&self) -> bool;
221}
222
223pub struct Unused<T> {
225 inner: T,
226}
227
228impl<T> Unused<T> {
229 pub unsafe fn new_unchecked(inner: T) -> Self {
235 Self { inner }
236 }
237
238 pub unsafe fn get_unchecked(&self) -> &T {
244 &self.inner
245 }
246
247 pub fn into(self) -> T {
249 self.inner
250 }
251}
252
253#[allow(clippy::manual_non_exhaustive)]
254pub struct UpdateInfo {
255 pub duration: Duration,
256 pub tasks: usize,
257 pub reasons: InvalidationReasonSet,
258 #[allow(dead_code)]
259 placeholder_for_future_fields: (),
260}
261
262#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
263pub enum TaskPersistence {
264 Persistent,
266
267 Transient,
274}
275
276impl Display for TaskPersistence {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 TaskPersistence::Persistent => write!(f, "persistent"),
280 TaskPersistence::Transient => write!(f, "transient"),
281 }
282 }
283}
284
285#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
288pub enum InputResolution {
289 Resolved,
292 Unresolved,
294}
295
296impl InputResolution {
297 #[inline]
298 pub fn from_is_resolved(is_resolved: bool) -> Self {
299 if is_resolved {
300 Self::Resolved
301 } else {
302 Self::Unresolved
303 }
304 }
305
306 #[inline]
307 pub fn is_resolved(self) -> bool {
308 matches!(self, Self::Resolved)
309 }
310}
311
312#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
313pub enum ReadConsistency {
314 #[default]
317 Eventual,
318 Strong,
323}
324
325#[derive(Clone, Copy, Debug, Eq, PartialEq)]
326pub enum ReadCellTracking {
327 Tracked {
329 key: Option<u64>,
331 },
332 TrackOnlyError,
337 Untracked,
342}
343
344impl ReadCellTracking {
345 pub fn should_track(&self, is_err: bool) -> bool {
346 match self {
347 ReadCellTracking::Tracked { .. } => true,
348 ReadCellTracking::TrackOnlyError => is_err,
349 ReadCellTracking::Untracked => false,
350 }
351 }
352
353 pub fn key(&self) -> Option<u64> {
354 match self {
355 ReadCellTracking::Tracked { key } => *key,
356 ReadCellTracking::TrackOnlyError => None,
357 ReadCellTracking::Untracked => None,
358 }
359 }
360}
361
362impl Default for ReadCellTracking {
363 fn default() -> Self {
364 ReadCellTracking::Tracked { key: None }
365 }
366}
367
368impl Display for ReadCellTracking {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 match self {
371 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
372 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
373 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
374 ReadCellTracking::Untracked => write!(f, "untracked"),
375 }
376 }
377}
378
379#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
380pub enum ReadTracking {
381 #[default]
383 Tracked,
384 TrackOnlyError,
389 Untracked,
394}
395
396impl ReadTracking {
397 pub fn should_track(&self, is_err: bool) -> bool {
398 match self {
399 ReadTracking::Tracked => true,
400 ReadTracking::TrackOnlyError => is_err,
401 ReadTracking::Untracked => false,
402 }
403 }
404}
405
406impl Display for ReadTracking {
407 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
408 match self {
409 ReadTracking::Tracked => write!(f, "tracked"),
410 ReadTracking::TrackOnlyError => write!(f, "track only error"),
411 ReadTracking::Untracked => write!(f, "untracked"),
412 }
413 }
414}
415
416#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
417pub enum TaskPriority {
418 #[default]
419 Initial,
420 Invalidation {
421 priority: Reverse<u32>,
422 },
423 Recomputation,
424}
425
426impl TaskPriority {
427 pub fn invalidation(priority: u32) -> Self {
428 Self::Invalidation {
429 priority: Reverse(priority),
430 }
431 }
432
433 pub fn initial() -> Self {
434 Self::Initial
435 }
436
437 pub fn leaf() -> Self {
438 Self::Invalidation {
439 priority: Reverse(0),
440 }
441 }
442
443 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
444 match self {
445 TaskPriority::Initial => parent_priority,
446 TaskPriority::Invalidation { priority } => {
447 if let TaskPriority::Invalidation {
448 priority: parent_priority,
449 } = parent_priority
450 && priority.0 < parent_priority.0
451 {
452 Self::Invalidation {
453 priority: Reverse(parent_priority.0.saturating_add(1)),
454 }
455 } else {
456 *self
457 }
458 }
459 TaskPriority::Recomputation => TaskPriority::Recomputation,
460 }
461 }
462}
463
464impl Display for TaskPriority {
465 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
466 match self {
467 TaskPriority::Initial => write!(f, "initial"),
468 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
469 TaskPriority::Recomputation => write!(f, "recomputation"),
470 }
471 }
472}
473
474enum ScheduledTask {
475 Task {
476 task_id: TaskId,
477 span: Span,
478 },
479 LocalTask {
480 ty: LocalTaskSpec,
481 persistence: TaskPersistence,
482 execution_id: ExecutionId,
483 local_task_id: LocalTaskId,
484 global_task_state: CurrentTaskStateHandle,
485 span: Span,
486 },
487}
488
489#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
493pub enum ScheduleKey {
494 Task(TaskId),
496 LocalTask(ExecutionId, LocalTaskId),
498}
499
500impl Claimable for ScheduledTask {
501 type Key = ScheduleKey;
502
503 fn claim_key(&self) -> Option<ScheduleKey> {
504 Some(match self {
505 ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
506 ScheduledTask::LocalTask {
507 execution_id,
508 local_task_id,
509 ..
510 } => ScheduleKey::LocalTask(*execution_id, *local_task_id),
511 })
512 }
513}
514
515#[cfg(feature = "inline_execution_stats")]
516use std::sync::atomic::AtomicU64;
517
518#[cfg(feature = "inline_execution_stats")]
521#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
522pub struct InlineExecutionStats {
523 pub queued: u64,
525 pub claim_attempted: u64,
527 pub claim_completed: u64,
529 pub claim_yielded: u64,
531 pub claim_failed: u64,
533 pub waited_in_progress: u64,
535}
536
537#[derive(Default)]
543struct InlineExecutionCounters {
544 #[cfg(feature = "inline_execution_stats")]
545 claim_attempted: AtomicU64,
546 #[cfg(feature = "inline_execution_stats")]
547 claim_completed: AtomicU64,
548 #[cfg(feature = "inline_execution_stats")]
549 claim_yielded: AtomicU64,
550 #[cfg(feature = "inline_execution_stats")]
551 claim_failed: AtomicU64,
552 #[cfg(feature = "inline_execution_stats")]
553 waited_in_progress: AtomicU64,
554}
555
556impl InlineExecutionCounters {
557 #[inline]
559 fn claim_attempted(&self) {
560 #[cfg(feature = "inline_execution_stats")]
561 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
562 }
563
564 #[inline]
566 fn claim_completed(&self) {
567 #[cfg(feature = "inline_execution_stats")]
568 self.claim_completed.fetch_add(1, Ordering::Relaxed);
569 }
570
571 #[inline]
573 fn claim_yielded(&self) {
574 #[cfg(feature = "inline_execution_stats")]
575 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
576 }
577
578 #[inline]
580 fn claim_failed(&self) {
581 #[cfg(feature = "inline_execution_stats")]
582 self.claim_failed.fetch_add(1, Ordering::Relaxed);
583 }
584
585 #[cfg(feature = "inline_execution_stats")]
587 #[inline]
588 fn waited_in_progress(&self) {
589 self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
590 }
591}
592
593#[cfg(feature = "inline_execution_stats")]
595pub(crate) fn inline_stats_requested() -> bool {
596 static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
597 std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
598 });
599 *REQUESTED
600}
601
602const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
605
606thread_local! {
607 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
609}
610
611fn inline_execution_allowed() -> bool {
613 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
614}
615
616struct InlineExecutionDepthGuard;
618
619impl InlineExecutionDepthGuard {
620 fn enter() -> Self {
621 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
622 Self
623 }
624}
625
626impl Drop for InlineExecutionDepthGuard {
627 fn drop(&mut self) {
628 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
629 }
630}
631
632fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
635 let _depth_guard = InlineExecutionDepthGuard::enter();
636 let span_slot = InlineExecutionSpanSlot::default();
637 let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
638 match future
642 .as_mut()
643 .poll(&mut Context::from_waker(Waker::noop()))
644 {
645 Poll::Ready(()) => {
646 span_slot.record("complete");
647 true
648 }
649 Poll::Pending => {
650 span_slot.record("partial");
651 tokio::task::spawn(future);
652 false
653 }
654 }
655}
656
657pub(crate) fn execute_read_target_inline(
659 turbo_tasks: &dyn TurboTasksApi,
660 key: ScheduleKey,
661) -> bool {
662 if !inline_execution_allowed() {
663 return false;
665 }
666 turbo_tasks.try_execute_scheduled_task_inline(key)
667}
668
669pub struct TurboTasks<B: Backend + 'static> {
670 this: Weak<Self>,
671 backend: B,
672 execution_id_factory: IdFactory<ExecutionId>,
673 stopped: AtomicBool,
674 currently_scheduled_foreground_jobs: AtomicUsize,
675 currently_scheduled_background_jobs: AtomicUsize,
676 scheduled_tasks: AtomicUsize,
677 inline_counters: InlineExecutionCounters,
680 priority_runner:
681 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
682 start: Mutex<Option<Instant>>,
683 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
684 event_foreground_start: Event,
686 event_foreground_done: Event,
689 event_background_done: Event,
691 compilation_events: CompilationEventQueue,
692}
693
694struct CurrentTaskState {
703 task_id: Option<TaskId>,
704 execution_id: ExecutionId,
705 priority: TaskPriority,
706
707 #[cfg(feature = "verify_determinism")]
710 stateful: bool,
711
712 has_invalidator: bool,
714
715 in_top_level_task: bool,
718
719 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
724
725 local_tasks: LocalTaskTracker,
728}
729
730impl CurrentTaskState {
731 fn new(
732 task_id: TaskId,
733 execution_id: ExecutionId,
734 priority: TaskPriority,
735 in_top_level_task: bool,
736 ) -> Self {
737 Self {
738 task_id: Some(task_id),
739 execution_id,
740 priority,
741 #[cfg(feature = "verify_determinism")]
742 stateful: false,
743 has_invalidator: false,
744 in_top_level_task,
745 cell_counters: Some(AutoMap::default()),
746 local_tasks: LocalTaskTracker::new(),
747 }
748 }
749
750 fn new_temporary(
751 execution_id: ExecutionId,
752 priority: TaskPriority,
753 in_top_level_task: bool,
754 ) -> Self {
755 Self {
756 task_id: None,
757 execution_id,
758 priority,
759 #[cfg(feature = "verify_determinism")]
760 stateful: false,
761 has_invalidator: false,
762 in_top_level_task,
763 cell_counters: None,
764 local_tasks: LocalTaskTracker::new(),
765 }
766 }
767
768 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
769 if self.execution_id != expected_execution_id {
770 panic!(
771 "Local tasks can only be scheduled/awaited within the same execution of the \
772 parent task that created them"
773 );
774 }
775 }
776}
777
778#[derive(Clone)]
782struct CurrentTaskStateHandle {
783 inner: Arc<CurrentTaskStateInner>,
784}
785
786struct CurrentTaskStateInner {
787 current_task_id: Option<TaskId>,
788 state: RwLock<CurrentTaskState>,
789}
790
791impl CurrentTaskStateHandle {
792 fn new(state: CurrentTaskState) -> Self {
793 Self {
794 inner: Arc::new(CurrentTaskStateInner {
795 current_task_id: state.task_id,
796 state: RwLock::new(state),
797 }),
798 }
799 }
800
801 fn current_task_id(&self) -> Option<TaskId> {
802 self.inner.current_task_id
803 }
804}
805
806impl Deref for CurrentTaskStateHandle {
807 type Target = RwLock<CurrentTaskState>;
808
809 fn deref(&self) -> &Self::Target {
810 &self.inner.state
811 }
812}
813
814task_local! {
816 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
818
819 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
820
821 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
826
827 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
830}
831
832#[derive(Clone, Default)]
843struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
844
845impl InlineExecutionSpanSlot {
846 fn set(span: &Span) {
848 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
849 *slot.0.lock().unwrap() = Some(span.clone());
850 });
851 }
852
853 fn record(&self, outcome: &'static str) {
857 if let Some(span) = self.0.lock().unwrap().as_ref() {
858 span.record("inline_execution", outcome);
859 }
860 }
861}
862
863impl<B: Backend + 'static> TurboTasks<B> {
864 pub fn new(backend: B) -> Arc<Self> {
870 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
871 let this = Arc::new_cyclic(|this| Self {
872 this: this.clone(),
873 backend,
874 execution_id_factory,
875 stopped: AtomicBool::new(false),
876 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
877 currently_scheduled_background_jobs: AtomicUsize::new(0),
878 scheduled_tasks: AtomicUsize::new(0),
879 inline_counters: InlineExecutionCounters::default(),
880 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
881 start: Default::default(),
882 aggregated_update: Default::default(),
883 event_foreground_done: Event::new(|| {
884 || "TurboTasks::event_foreground_done".to_string()
885 }),
886 event_foreground_start: Event::new(|| {
887 || "TurboTasks::event_foreground_start".to_string()
888 }),
889 event_background_done: Event::new(|| {
890 || "TurboTasks::event_background_done".to_string()
891 }),
892 compilation_events: CompilationEventQueue::default(),
893 });
894 this.backend.startup(&*this);
895 this
896 }
897
898 pub fn pin(&self) -> Arc<Self> {
899 self.this.upgrade().unwrap()
900 }
901
902 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
904 where
905 T: ?Sized,
906 F: Fn() -> Fut + Send + Sync + Clone + 'static,
907 Fut: Future<Output = Result<Vc<T>>> + Send,
908 {
909 let id = self.backend.create_transient_task(
910 TransientTaskType::Root(Box::new(move || {
911 let functor = functor.clone();
912 Box::pin(async move {
913 mark_top_level_task();
914 let raw_vc = functor().await?.node;
915 raw_vc.to_non_local().await
916 })
917 })),
918 self,
919 );
920 self.schedule(id, TaskPriority::initial());
921 id
922 }
923
924 pub fn dispose_root_task(&self, task_id: TaskId) {
925 self.backend.dispose_root_task(task_id, self);
926 }
927
928 #[track_caller]
932 fn spawn_once_task<T, Fut>(&self, future: Fut)
933 where
934 T: ?Sized,
935 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
936 {
937 let id = self.backend.create_transient_task(
938 TransientTaskType::Once(Box::pin(async move {
939 mark_top_level_task();
940 let raw_vc = future.await?.node;
941 raw_vc.to_non_local().await
942 })),
943 self,
944 );
945 self.schedule(id, TaskPriority::initial());
946 }
947
948 pub async fn run_once<T: TraceRawVcs + Send + 'static>(
949 &self,
950 future: impl Future<Output = Result<T>> + Send + 'static,
951 ) -> Result<T> {
952 let (tx, rx) = tokio::sync::oneshot::channel();
953 self.spawn_once_task(async move {
954 mark_top_level_task();
955 let result = future.await;
956 tx.send(result)
957 .map_err(|_| anyhow!("unable to send result"))?;
958 Ok(Completion::new())
959 });
960
961 rx.await?
962 }
963
964 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
965 pub async fn run<T: TraceRawVcs + Send + 'static>(
966 &self,
967 future: impl Future<Output = Result<T>> + Send + 'static,
968 ) -> Result<T, TurboTasksExecutionError> {
969 self.begin_foreground_job();
970 let execution_id = self.execution_id_factory.wrapping_get();
972 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
973 execution_id,
974 TaskPriority::initial(),
975 true, ));
977
978 let result = TURBO_TASKS
979 .scope(
980 self.pin(),
981 CURRENT_TASK_STATE.scope(current_task_state, async {
982 let result = CaptureFuture::new(future).await;
983
984 wait_for_local_tasks().await;
986
987 match result {
988 Ok(Ok(value)) => Ok(value),
989 Ok(Err(err)) => Err(err.into()),
990 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
991 }
992 }),
993 )
994 .await;
995 self.finish_foreground_job();
996 result
997 }
998
999 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1000 let this = self.pin();
1001 tokio::spawn(async move {
1002 this.pin()
1003 .run_once(async move {
1004 this.finish_foreground_job();
1005 future.await;
1006 this.begin_foreground_job();
1007 Ok(())
1008 })
1009 .await
1010 .unwrap()
1011 });
1012 }
1013
1014 pub(crate) fn native_call(
1015 &self,
1016 native_fn: &'static NativeFunction,
1017 this: Option<RawVc>,
1018 arg: &mut dyn DynTaskInputsStorage,
1019 persistence: TaskPersistence,
1020 ) -> RawVc {
1021 RawVc::task_output(self.backend.get_or_create_task(
1022 native_fn,
1023 this,
1024 arg,
1025 current_task_if_available("turbo_function calls"),
1026 persistence,
1027 self,
1028 ))
1029 }
1030
1031 pub fn dynamic_call(
1032 &self,
1033 native_fn: &'static NativeFunction,
1034 this: Option<RawVc>,
1035 arg: &mut dyn DynTaskInputsStorage,
1036 inputs_resolved: InputResolution,
1037 persistence: TaskPersistence,
1038 ) -> RawVc {
1039 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1040 return self.native_call(native_fn, this, arg, persistence);
1041 }
1042 let arg = arg.take_box();
1044 let task_type = LocalTaskSpec {
1045 task_type: LocalTaskType::ResolveNative { native_fn },
1046 this,
1047 arg,
1048 };
1049 self.schedule_local_task(task_type, persistence)
1050 }
1051
1052 pub fn trait_call(
1053 &self,
1054 trait_method: &'static TraitMethod,
1055 this: RawVc,
1056 arg: &mut dyn DynTaskInputsStorage,
1057 inputs_resolved: InputResolution,
1058 persistence: TaskPersistence,
1059 ) -> RawVc {
1060 if let Some((_, cell_id)) = this.as_task_cell() {
1064 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1065 Some(native_fn) => {
1066 if let Some(filter) = native_fn.arg_meta.filter_owned {
1067 let (resolved, mut arg) = (filter)(arg);
1068 return self.dynamic_call(
1069 native_fn,
1070 Some(this),
1071 &mut arg,
1072 resolved,
1073 persistence,
1074 );
1075 } else {
1076 return self.dynamic_call(
1077 native_fn,
1078 Some(this),
1079 arg,
1080 inputs_resolved,
1081 persistence,
1082 );
1083 }
1084 }
1085 None => {
1086 }
1090 }
1091 }
1092
1093 let task_type = LocalTaskSpec {
1095 task_type: LocalTaskType::ResolveTrait { trait_method },
1096 this: Some(this),
1097 arg: arg.take_box(),
1098 };
1099
1100 self.schedule_local_task(task_type, persistence)
1101 }
1102
1103 #[track_caller]
1104 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1105 self.begin_foreground_job();
1106 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1107
1108 let task = ScheduledTask::Task {
1109 task_id,
1110 span: Span::current(),
1111 };
1112 self.priority_runner.schedule(&self.pin(), task, priority);
1113 }
1114
1115 fn schedule_local_task(
1116 &self,
1117 ty: LocalTaskSpec,
1118 persistence: TaskPersistence,
1120 ) -> RawVc {
1121 let task_type = ty.task_type;
1122 let (global_task_state, execution_id, priority, local_task_id) =
1123 CURRENT_TASK_STATE.with(|gts| {
1124 let mut gts_write = gts.write().unwrap();
1125 let local_task_id = gts_write.local_tasks.create(task_type);
1126 (
1127 gts.clone(),
1128 gts_write.execution_id,
1129 gts_write.priority,
1130 local_task_id,
1131 )
1132 });
1133
1134 let task = ScheduledTask::LocalTask {
1135 ty,
1136 persistence,
1137 execution_id,
1138 local_task_id,
1139 global_task_state,
1140 span: Span::current(),
1141 };
1142 self.priority_runner.schedule(&self.pin(), task, priority);
1143
1144 RawVc::local_output(execution_id, local_task_id, persistence)
1145 }
1146
1147 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1149 let this = self.pin();
1150 self.inline_counters.claim_attempted();
1151 if let Some(future) = self.priority_runner.claim(&this, &key) {
1152 let completed = poll_once_or_spawn(future);
1153 if completed {
1154 self.inline_counters.claim_completed();
1155 } else {
1156 self.inline_counters.claim_yielded();
1157 }
1158 return completed;
1159 }
1160 self.inline_counters.claim_failed();
1161 false
1162 }
1163
1164 #[cfg(feature = "inline_execution_stats")]
1165 fn note_waited_for_in_progress_task(&self) {
1166 self.inline_counters.waited_in_progress();
1167 }
1168
1169 fn begin_foreground_job(&self) {
1170 if self
1171 .currently_scheduled_foreground_jobs
1172 .fetch_add(1, Ordering::AcqRel)
1173 == 0
1174 {
1175 *self.start.lock().unwrap() = Some(Instant::now());
1176 self.event_foreground_start.notify(usize::MAX);
1177 self.backend.idle_end(self);
1178 }
1179 }
1180
1181 fn finish_foreground_job(&self) {
1182 if self
1183 .currently_scheduled_foreground_jobs
1184 .fetch_sub(1, Ordering::AcqRel)
1185 == 1
1186 {
1187 self.backend.idle_start(self);
1188 let total = self.scheduled_tasks.load(Ordering::Acquire);
1191 self.scheduled_tasks.store(0, Ordering::Release);
1192 if let Some(start) = *self.start.lock().unwrap() {
1193 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1194 if let Some(update) = update.as_mut() {
1195 update.0 += start.elapsed();
1196 update.1 += total;
1197 } else {
1198 *update = Some((start.elapsed(), total));
1199 }
1200 }
1201 self.event_foreground_done.notify(usize::MAX);
1202 }
1203 }
1204
1205 fn begin_background_job(&self) {
1206 self.currently_scheduled_background_jobs
1207 .fetch_add(1, Ordering::Relaxed);
1208 }
1209
1210 fn finish_background_job(&self) {
1211 if self
1212 .currently_scheduled_background_jobs
1213 .fetch_sub(1, Ordering::Relaxed)
1214 == 1
1215 {
1216 self.event_background_done.notify(usize::MAX);
1217 }
1218 }
1219
1220 pub fn get_in_progress_count(&self) -> usize {
1221 self.currently_scheduled_foreground_jobs
1222 .load(Ordering::Acquire)
1223 }
1224
1225 #[cfg(feature = "inline_execution_stats")]
1228 #[doc(hidden)]
1229 pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1230 let counters = &self.inline_counters;
1231 InlineExecutionStats {
1232 queued: self.priority_runner.total_queued(),
1233 claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1234 claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1235 claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1236 claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1237 waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1238 }
1239 }
1240
1241 pub async fn wait_task_completion(
1253 &self,
1254 id: TaskId,
1255 consistency: ReadConsistency,
1256 ) -> Result<()> {
1257 read_task_output(
1258 self,
1259 id,
1260 ReadOutputOptions {
1261 tracking: ReadTracking::Untracked,
1263 consistency,
1264 },
1265 )
1266 .await?;
1267 Ok(())
1268 }
1269
1270 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1273 self.aggregated_update_info(aggregation, Duration::MAX)
1274 .await
1275 .unwrap()
1276 }
1277
1278 pub async fn aggregated_update_info(
1282 &self,
1283 aggregation: Duration,
1284 timeout: Duration,
1285 ) -> Option<UpdateInfo> {
1286 let listener = self
1287 .event_foreground_done
1288 .listen_with_note(|| || "wait for update info".to_string());
1289 let wait_for_finish = {
1290 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1291 if aggregation.is_zero() {
1292 if let Some((duration, tasks)) = update.take() {
1293 return Some(UpdateInfo {
1294 duration,
1295 tasks,
1296 reasons: take(reason_set),
1297 placeholder_for_future_fields: (),
1298 });
1299 } else {
1300 true
1301 }
1302 } else {
1303 update.is_none()
1304 }
1305 };
1306 if wait_for_finish {
1307 if timeout == Duration::MAX {
1308 listener.await;
1310 } else {
1311 let start_listener = self
1313 .event_foreground_start
1314 .listen_with_note(|| || "wait for update info".to_string());
1315 if self
1316 .currently_scheduled_foreground_jobs
1317 .load(Ordering::Acquire)
1318 == 0
1319 {
1320 start_listener.await;
1321 } else {
1322 drop(start_listener);
1323 }
1324 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1325 return None;
1327 }
1328 }
1329 }
1330 if !aggregation.is_zero() {
1331 loop {
1332 select! {
1333 () = tokio::time::sleep(aggregation) => {
1334 break;
1335 }
1336 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1337 }
1339 }
1340 }
1341 }
1342 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1343 if let Some((duration, tasks)) = update.take() {
1344 Some(UpdateInfo {
1345 duration,
1346 tasks,
1347 reasons: take(reason_set),
1348 placeholder_for_future_fields: (),
1349 })
1350 } else {
1351 panic!("aggregated_update_info must not called concurrently")
1352 }
1353 }
1354
1355 pub async fn wait_background_done(&self) {
1356 let listener = self.event_background_done.listen();
1357 if self
1358 .currently_scheduled_background_jobs
1359 .load(Ordering::Acquire)
1360 != 0
1361 {
1362 listener.await;
1363 }
1364 }
1365
1366 pub async fn stop_and_wait(&self) {
1367 #[cfg(feature = "inline_execution_stats")]
1368 if inline_stats_requested() {
1369 eprintln!(
1372 "turbo-tasks inline execution stats: {:#?}",
1373 self.inline_execution_stats()
1374 );
1375 }
1376 turbo_tasks_future_scope(self.pin(), async move {
1377 self.backend.stopping(self);
1378 self.stopped.store(true, Ordering::Release);
1379 {
1380 let listener = self
1381 .event_foreground_done
1382 .listen_with_note(|| || "wait for stop".to_string());
1383 if self
1384 .currently_scheduled_foreground_jobs
1385 .load(Ordering::Acquire)
1386 != 0
1387 {
1388 listener.await;
1389 }
1390 }
1391 {
1392 let listener = self.event_background_done.listen();
1393 if self
1394 .currently_scheduled_background_jobs
1395 .load(Ordering::Acquire)
1396 != 0
1397 {
1398 listener.await;
1399 }
1400 }
1401 self.backend.stop(self);
1402 })
1403 .await;
1404 }
1405
1406 #[track_caller]
1407 pub(crate) fn schedule_background_job<T>(&self, func: T)
1408 where
1409 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1410 T::CallOnceFuture: Send,
1411 {
1412 let mut this = self.pin();
1413 self.begin_background_job();
1414 tokio::spawn(
1415 TURBO_TASKS
1416 .scope(this.clone(), async move {
1417 if !this.stopped.load(Ordering::Acquire) {
1418 this = func(this).await;
1419 }
1420 this.finish_background_job();
1421 })
1422 .in_current_span(),
1423 );
1424 }
1425
1426 fn finish_current_task_state(&self) -> FinishedTaskState {
1427 CURRENT_TASK_STATE.with(|cell| {
1428 let current_task_state = &*cell.write().unwrap();
1429 FinishedTaskState {
1430 #[cfg(feature = "verify_determinism")]
1431 stateful: current_task_state.stateful,
1432 has_invalidator: current_task_state.has_invalidator,
1433 }
1434 })
1435 }
1436
1437 pub fn backend(&self) -> &B {
1438 &self.backend
1439 }
1440
1441 pub fn get_current_task_priority(&self) -> TaskPriority {
1442 CURRENT_TASK_STATE
1443 .try_with(|task_state| task_state.read().unwrap().priority)
1444 .unwrap_or(TaskPriority::initial())
1445 }
1446
1447 pub fn is_idle(&self) -> bool {
1448 self.currently_scheduled_foreground_jobs
1449 .load(Ordering::Acquire)
1450 == 0
1451 }
1452
1453 #[track_caller]
1454 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1455 self.schedule_background_job(async move |this| {
1456 this.backend.run_backend_job(job, &*this).await;
1457 this
1458 })
1459 }
1460}
1461
1462struct TurboTasksExecutor;
1463
1464async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1469 match AssertUnwindSafe(f).catch_unwind().await {
1470 Ok(r) => r,
1471 Err(_) => {
1472 eprintln!(
1473 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1474 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1475 https://github.com/vercel/next.js/discussions and include the panic message \
1476 and stack trace above.\n\nAborting."
1477 );
1478 abort();
1479 }
1480 }
1481}
1482
1483impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1484 type Future = impl Future<Output = ()> + Send + 'static;
1485
1486 fn execute(
1487 &self,
1488 this: &Arc<TurboTasks<B>>,
1489 scheduled_task: ScheduledTask,
1490 priority: TaskPriority,
1491 ) -> Self::Future {
1492 match scheduled_task {
1493 ScheduledTask::Task { task_id, span } => {
1494 let this2 = this.clone();
1495 let this = this.clone();
1496 let future = async move {
1497 abort_on_panic(async {
1498 let execution_id = this.execution_id_factory.wrapping_get();
1501 let current_task_state =
1502 CurrentTaskStateHandle::new(CurrentTaskState::new(
1503 task_id,
1504 execution_id,
1505 priority,
1506 false, ));
1508 let single_execution_future = async {
1509 if this.stopped.load(Ordering::Acquire) {
1510 this.backend.task_execution_canceled(task_id, &*this);
1511 return None;
1512 }
1513
1514 let TaskExecutionSpec { future, span } = this
1515 .backend
1516 .try_start_task_execution(task_id, priority, &*this)?;
1517
1518 InlineExecutionSpanSlot::set(&span);
1521
1522 async {
1523 let result = CaptureFuture::new(future).await;
1524
1525 wait_for_local_tasks().await;
1527
1528 let result = match result {
1529 Ok(Ok(raw_vc)) => {
1530 raw_vc
1533 .to_non_local_unchecked_sync(&*this)
1534 .map_err(|err| err.into())
1535 }
1536 Ok(Err(err)) => Err(err.into()),
1537 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1538 };
1539
1540 let finished_state = this.finish_current_task_state();
1541 let cell_counters = CURRENT_TASK_STATE
1542 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1543 this.backend.task_execution_completed(
1544 task_id,
1545 result,
1546 &cell_counters,
1547 #[cfg(feature = "verify_determinism")]
1548 finished_state.stateful,
1549 finished_state.has_invalidator,
1550 &*this,
1551 )
1552 }
1553 .instrument(span)
1554 .await
1555 };
1556 if let Some(stale_priority) = CURRENT_TASK_STATE
1557 .scope(current_task_state, single_execution_future)
1558 .await
1559 {
1560 this.schedule(task_id, stale_priority);
1563 }
1564 this.finish_foreground_job();
1565 })
1566 .await
1567 };
1568
1569 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1570 }
1571 ScheduledTask::LocalTask {
1572 ty,
1573 persistence,
1574 execution_id: _,
1575 local_task_id,
1576 global_task_state,
1577 span,
1578 } => {
1579 let this2 = this.clone();
1580 let this = this.clone();
1581 let task_type = ty.task_type;
1582 let future = async move {
1583 let span = match &ty.task_type {
1584 LocalTaskType::ResolveNative { native_fn } => {
1585 native_fn.resolve_span(priority)
1586 }
1587 LocalTaskType::ResolveTrait { trait_method } => {
1588 trait_method.resolve_span(priority)
1589 }
1590 };
1591 InlineExecutionSpanSlot::set(&span);
1594 abort_on_panic(
1595 async move {
1596 let result = match ty.task_type {
1597 LocalTaskType::ResolveNative { native_fn } => {
1598 LocalTaskType::run_resolve_native(
1599 native_fn,
1600 ty.this,
1601 &*ty.arg,
1602 persistence,
1603 this,
1604 )
1605 .await
1606 }
1607 LocalTaskType::ResolveTrait { trait_method } => {
1608 LocalTaskType::run_resolve_trait(
1609 trait_method,
1610 ty.this.unwrap(),
1611 &*ty.arg,
1612 persistence,
1613 this,
1614 )
1615 .await
1616 }
1617 };
1618
1619 let output = match result {
1620 Ok(raw_vc) => OutputContent::Link(raw_vc),
1621 Err(err) => OutputContent::Error(
1622 TurboTasksExecutionError::from(err)
1623 .with_local_task_context(task_type.to_string()),
1624 ),
1625 };
1626
1627 CURRENT_TASK_STATE.with(move |gts| {
1628 gts.write()
1629 .unwrap()
1630 .local_tasks
1631 .complete(local_task_id, output);
1632 });
1633 }
1634 .instrument(span),
1635 )
1636 .await
1637 };
1638 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1639
1640 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1641 }
1642 }
1643 }
1644}
1645
1646struct FinishedTaskState {
1647 #[cfg(feature = "verify_determinism")]
1650 stateful: bool,
1651
1652 has_invalidator: bool,
1654}
1655
1656impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1657 fn dynamic_call(
1658 &self,
1659 native_fn: &'static NativeFunction,
1660 this: Option<RawVc>,
1661 arg: &mut dyn DynTaskInputsStorage,
1662 inputs_resolved: InputResolution,
1663 persistence: TaskPersistence,
1664 ) -> RawVc {
1665 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1666 }
1667 fn native_call(
1668 &self,
1669 native_fn: &'static NativeFunction,
1670 this: Option<RawVc>,
1671 arg: &mut dyn DynTaskInputsStorage,
1672 persistence: TaskPersistence,
1673 ) -> RawVc {
1674 self.native_call(native_fn, this, arg, persistence)
1675 }
1676 fn trait_call(
1677 &self,
1678 trait_method: &'static TraitMethod,
1679 this: RawVc,
1680 arg: &mut dyn DynTaskInputsStorage,
1681 inputs_resolved: InputResolution,
1682 persistence: TaskPersistence,
1683 ) -> RawVc {
1684 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1685 }
1686
1687 #[track_caller]
1688 fn run(
1689 &self,
1690 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1691 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1692 let this = self.pin();
1693 Box::pin(async move { this.run(future).await })
1694 }
1695
1696 #[track_caller]
1697 fn run_once(
1698 &self,
1699 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1700 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1701 let this = self.pin();
1702 Box::pin(async move { this.run_once(future).await })
1703 }
1704
1705 #[track_caller]
1706 fn run_once_with_reason(
1707 &self,
1708 reason: StaticOrArc<dyn InvalidationReason>,
1709 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1710 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1711 {
1712 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1713 reason_set.insert(reason);
1714 }
1715 let this = self.pin();
1716 Box::pin(async move { this.run_once(future).await })
1717 }
1718
1719 #[track_caller]
1720 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1721 self.start_once_process(future)
1722 }
1723
1724 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1725 if let Err(e) = self.compilation_events.send(event) {
1726 tracing::warn!("Failed to send compilation event: {e}");
1727 }
1728 }
1729
1730 fn get_task_name(&self, task: TaskId) -> String {
1731 self.backend.get_task_name(task, self)
1732 }
1733}
1734
1735impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1736 #[instrument(level = "info", skip_all, name = "invalidate")]
1737 fn invalidate(&self, task: TaskId) {
1738 self.backend.invalidate_task(task, self);
1739 }
1740
1741 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1742 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1743 {
1744 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1745 reason_set.insert(reason);
1746 }
1747 self.backend.invalidate_task(task, self);
1748 }
1749
1750 fn invalidate_serialization(&self, task: TaskId) {
1751 self.backend.invalidate_serialization(task, self);
1752 }
1753
1754 #[track_caller]
1755 fn try_read_task_output(
1756 &self,
1757 task: TaskId,
1758 options: ReadOutputOptions,
1759 ) -> Result<ReadOutcome<RawVc>> {
1760 if options.consistency == ReadConsistency::Eventual {
1761 debug_assert_not_in_top_level_task("read_task_output");
1762 }
1763 self.backend.try_read_task_output(
1764 task,
1765 current_task_if_available("reading Vcs"),
1766 options,
1767 self,
1768 )
1769 }
1770
1771 #[track_caller]
1772 fn try_read_task_cell(
1773 &self,
1774 task: TaskId,
1775 index: CellId,
1776 options: ReadCellOptions,
1777 ) -> Result<ReadOutcome<TypedCellContent>> {
1778 let reader = current_task_if_available("reading Vcs");
1779 self.backend
1780 .try_read_task_cell(task, index, reader, options, self)
1781 }
1782
1783 fn try_read_own_task_cell(
1784 &self,
1785 current_task: TaskId,
1786 index: CellId,
1787 ) -> Result<TypedCellContent> {
1788 self.backend
1789 .try_read_own_task_cell(current_task, index, self)
1790 }
1791
1792 #[track_caller]
1793 fn try_read_local_output(
1794 &self,
1795 execution_id: ExecutionId,
1796 local_task_id: LocalTaskId,
1797 ) -> Result<Result<RawVc, EventListener>> {
1798 debug_assert_not_in_top_level_task("read_local_output");
1799 CURRENT_TASK_STATE.with(|gts| {
1800 let gts_read = gts.read().unwrap();
1801
1802 gts_read.assert_execution_id(execution_id);
1807
1808 match gts_read.local_tasks.get(local_task_id) {
1809 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1810 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1811 }
1812 })
1813 }
1814
1815 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1816 self.backend.read_task_collectibles(
1819 task,
1820 trait_id,
1821 current_task_if_available("reading collectibles"),
1822 self,
1823 )
1824 }
1825
1826 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1827 self.try_execute_scheduled_task_inline(key)
1828 }
1829
1830 #[cfg(feature = "inline_execution_stats")]
1831 fn note_waited_for_in_progress_task(&self) {
1832 self.note_waited_for_in_progress_task()
1833 }
1834
1835 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1836 self.backend.emit_collectible(
1837 trait_type,
1838 collectible,
1839 current_task("emitting collectible"),
1840 self,
1841 );
1842 }
1843
1844 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1845 self.backend.unemit_collectible(
1846 trait_type,
1847 collectible,
1848 count,
1849 current_task("emitting collectible"),
1850 self,
1851 );
1852 }
1853
1854 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1855 for (&collectible, &count) in collectibles {
1856 if count > 0 {
1857 self.backend.unemit_collectible(
1858 trait_type,
1859 collectible,
1860 count as u32,
1861 current_task("emitting collectible"),
1862 self,
1863 );
1864 }
1865 }
1866 }
1867
1868 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1869 self.try_read_own_task_cell(task, index)
1870 }
1871
1872 fn update_own_task_cell(
1873 &self,
1874 task: TaskId,
1875 index: CellId,
1876 content: CellContent,
1877 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1878 content_hash: Option<CellHash>,
1879 verification_mode: VerificationMode,
1880 ) {
1881 self.backend.update_task_cell(
1882 task,
1883 index,
1884 content,
1885 updated_key_hashes,
1886 content_hash,
1887 verification_mode,
1888 self,
1889 );
1890 }
1891
1892 fn connect_task(&self, task: TaskId) {
1893 self.backend
1894 .connect_task(task, current_task_if_available("connecting task"), self);
1895 }
1896
1897 fn mark_own_task_as_finished(&self, task: TaskId) {
1898 self.backend.mark_own_task_as_finished(task, self);
1899 }
1900
1901 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1904 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1907 global_task_state
1908 .write()
1909 .unwrap()
1910 .local_tasks
1911 .register_detached();
1912 let wrapped = async move {
1913 struct DropGuard;
1915 impl Drop for DropGuard {
1916 fn drop(&mut self) {
1917 CURRENT_TASK_STATE
1918 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1919 }
1920 }
1921 let _guard = DropGuard;
1922 fut.await;
1923 };
1924 tokio::spawn(TURBO_TASKS.scope(
1925 turbo_tasks(),
1926 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1927 ));
1928 }
1929
1930 fn task_statistics(&self) -> &TaskStatisticsApi {
1931 self.backend.task_statistics()
1932 }
1933
1934 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1935 let this = self.pin();
1936 Box::pin(async move {
1937 this.stop_and_wait().await;
1938 })
1939 }
1940
1941 fn subscribe_to_compilation_events(
1942 &self,
1943 event_types: Option<Vec<String>>,
1944 ) -> Receiver<Arc<dyn CompilationEvent>> {
1945 self.compilation_events.subscribe(event_types)
1946 }
1947
1948 fn is_tracking_dependencies(&self) -> bool {
1949 self.backend.is_tracking_dependencies()
1950 }
1951}
1952
1953async fn wait_for_local_tasks() {
1954 let listener =
1955 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
1956 let Some(listener) = listener else {
1957 return;
1958 };
1959 listener.await;
1960}
1961
1962pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
1963 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1964 Ok(id) => id,
1965 Err(_) => panic!(
1966 "{from} can only be used in the context of a turbo_tasks task execution or \
1967 turbo_tasks run"
1968 ),
1969 }
1970}
1971
1972pub(crate) fn current_task(from: &str) -> TaskId {
1973 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1974 Ok(Some(id)) => id,
1975 Ok(None) | Err(_) => {
1976 panic!("{from} can only be used in the context of a turbo_tasks task execution")
1977 }
1978 }
1979}
1980
1981#[track_caller]
1984pub(crate) fn debug_assert_in_top_level_task(message: &str) {
1985 if !cfg!(debug_assertions) {
1986 return;
1987 }
1988
1989 let in_top_level = CURRENT_TASK_STATE
1990 .try_with(|ts| ts.read().unwrap().in_top_level_task)
1991 .unwrap_or(true);
1992 if !in_top_level {
1993 panic!("{message}");
1994 }
1995}
1996
1997#[track_caller]
1998pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
1999 if !cfg!(debug_assertions) {
2000 return;
2001 }
2002
2003 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2006 .try_with(|&suppressed| suppressed)
2007 .unwrap_or(false);
2008 if suppressed {
2009 return;
2010 }
2011
2012 let in_top_level = CURRENT_TASK_STATE
2013 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2014 .unwrap_or(false);
2015 if in_top_level {
2016 panic!(
2017 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2018 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2019 reads to avoid leaking inconsistent return values."
2020 );
2021 }
2022}
2023
2024pub async fn run<T: Send + 'static>(
2025 tt: Arc<dyn TurboTasksApi>,
2026 future: impl Future<Output = Result<T>> + Send + 'static,
2027) -> Result<T> {
2028 let (tx, rx) = tokio::sync::oneshot::channel();
2029
2030 tt.run(Box::pin(async move {
2031 let result = future.await?;
2032 tx.send(result)
2033 .map_err(|_| anyhow!("unable to send result"))?;
2034 Ok(())
2035 }))
2036 .await?;
2037
2038 Ok(rx.await?)
2039}
2040
2041pub async fn run_once<T: Send + 'static>(
2042 tt: Arc<dyn TurboTasksApi>,
2043 future: impl Future<Output = Result<T>> + Send + 'static,
2044) -> Result<T> {
2045 let (tx, rx) = tokio::sync::oneshot::channel();
2046
2047 tt.run_once(Box::pin(async move {
2048 let result = future.await?;
2049 tx.send(result)
2050 .map_err(|_| anyhow!("unable to send result"))?;
2051 Ok(())
2052 }))
2053 .await?;
2054
2055 Ok(rx.await?)
2056}
2057
2058pub async fn run_once_with_reason<T: Send + 'static>(
2059 tt: Arc<dyn TurboTasksApi>,
2060 reason: impl InvalidationReason,
2061 future: impl Future<Output = Result<T>> + Send + 'static,
2062) -> Result<T> {
2063 let (tx, rx) = tokio::sync::oneshot::channel();
2064
2065 tt.run_once_with_reason(
2066 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2067 Box::pin(async move {
2068 let result = future.await?;
2069 tx.send(result)
2070 .map_err(|_| anyhow!("unable to send result"))?;
2071 Ok(())
2072 }),
2073 )
2074 .await?;
2075
2076 Ok(rx.await?)
2077}
2078
2079pub fn dynamic_call(
2081 func: &'static NativeFunction,
2082 this: Option<RawVc>,
2083 arg: &mut dyn DynTaskInputsStorage,
2084 inputs_resolved: InputResolution,
2085 persistence: TaskPersistence,
2086) -> RawVc {
2087 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2088}
2089
2090pub fn trait_call(
2092 trait_method: &'static TraitMethod,
2093 this: RawVc,
2094 arg: &mut dyn DynTaskInputsStorage,
2095 inputs_resolved: InputResolution,
2096 persistence: TaskPersistence,
2097) -> RawVc {
2098 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2099}
2100
2101pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2102 TURBO_TASKS.with(|arc| arc.clone())
2103}
2104
2105pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2106 TURBO_TASKS.with(Arc::downgrade)
2107}
2108
2109pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2110 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2111}
2112
2113pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2114 TURBO_TASKS.with(|arc| func(arc))
2115}
2116
2117pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2118 TURBO_TASKS.sync_scope(tt, f)
2119}
2120
2121pub fn turbo_tasks_future_scope<T>(
2122 tt: Arc<dyn TurboTasksApi>,
2123 f: impl Future<Output = T>,
2124) -> impl Future<Output = T> {
2125 TURBO_TASKS.scope(tt, f)
2126}
2127
2128pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2133 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2134}
2135
2136pub fn mark_finished() {
2139 with_turbo_tasks(|tt| {
2140 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2141 });
2142}
2143
2144pub fn get_serialization_invalidator() -> SerializationInvalidator {
2150 CURRENT_TASK_STATE.with(|cell| {
2151 let CurrentTaskState {
2152 task_id,
2153 #[cfg(feature = "verify_determinism")]
2154 stateful,
2155 ..
2156 } = &mut *cell.write().unwrap();
2157 #[cfg(feature = "verify_determinism")]
2158 {
2159 *stateful = true;
2160 }
2161 let Some(task_id) = *task_id else {
2162 panic!(
2163 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2164 task execution"
2165 );
2166 };
2167 SerializationInvalidator::new(task_id)
2168 })
2169}
2170
2171pub fn mark_invalidator() {
2172 CURRENT_TASK_STATE.with(|cell| {
2173 let CurrentTaskState {
2174 has_invalidator, ..
2175 } = &mut *cell.write().unwrap();
2176 *has_invalidator = true;
2177 })
2178}
2179
2180pub fn mark_stateful() {
2186 #[cfg(feature = "verify_determinism")]
2187 {
2188 CURRENT_TASK_STATE.with(|cell| {
2189 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2190 *stateful = true;
2191 })
2192 }
2193 }
2195
2196pub fn mark_top_level_task() {
2200 if cfg!(debug_assertions) {
2201 CURRENT_TASK_STATE.with(|cell| {
2202 cell.write().unwrap().in_top_level_task = true;
2203 })
2204 }
2205}
2206
2207pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2218 if cfg!(debug_assertions) {
2219 CURRENT_TASK_STATE.with(|cell| {
2220 cell.write().unwrap().in_top_level_task = false;
2221 })
2222 }
2223}
2224
2225pub fn prevent_gc() {
2226 }
2228
2229pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2230 with_turbo_tasks(|tt| {
2231 let raw_vc = collectible.node.node;
2232 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2233 })
2234}
2235
2236pub(crate) async fn read_task_output(
2237 this: &dyn TurboTasksApi,
2238 id: TaskId,
2239 options: ReadOutputOptions,
2240) -> Result<RawVc> {
2241 loop {
2242 match this.try_read_task_output(id, options)? {
2243 ReadOutcome::Value(result) => return Ok(result),
2244 ReadOutcome::Scheduled(listener) => {
2245 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2247 continue;
2248 }
2249 listener.await
2250 }
2251 ReadOutcome::InProgress(listener) => {
2252 #[cfg(feature = "inline_execution_stats")]
2254 this.note_waited_for_in_progress_task();
2255 listener.await
2256 }
2257 }
2258 }
2259}
2260
2261#[derive(Clone, Copy)]
2267pub struct CurrentCellRef {
2268 current_task: TaskId,
2269 index: CellId,
2270}
2271
2272type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2273
2274type CellUpdate = (
2277 SharedReference,
2278 Option<SmallVec<[u64; 2]>>,
2279 Option<CellHash>,
2280);
2281
2282type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2285
2286impl CurrentCellRef {
2287 fn conditional_update<T>(
2289 &self,
2290 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2291 ) where
2292 T: VcValueType,
2293 {
2294 let mut functor = Some(functor);
2297 self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2298 let functor = functor.take().expect("functor is called at most once");
2299 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2300 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2301 Some((
2302 SharedReference::new(triomphe::Arc::new(new_value)),
2303 updated_key_hashes,
2304 content_hash,
2305 ))
2306 })
2307 }
2308
2309 fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2316 let tt = turbo_tasks();
2317 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2318 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2319 if let Some((update, updated_key_hashes, content_hash)) = update {
2320 tt.update_own_task_cell(
2321 self.current_task,
2322 self.index,
2323 CellContent(Some(update)),
2324 updated_key_hashes,
2325 content_hash,
2326 VerificationMode::EqualityCheck,
2327 )
2328 }
2329 }
2330
2331 pub fn compare_and_update<T>(&self, new_value: T)
2366 where
2367 T: PartialEq + VcValueType,
2368 {
2369 self.conditional_update(|old_value| {
2370 if let Some(old_value) = old_value
2371 && old_value == &new_value
2372 {
2373 return None;
2374 }
2375 Some((new_value, None, None))
2376 });
2377 }
2378
2379 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2387 where
2388 T: VcValueType + PartialEq,
2389 {
2390 let mut new_shared_reference = Some(new_shared_reference);
2391 self.conditional_update_with_shared_reference(&mut |old_sr| {
2392 let new_shared_reference = new_shared_reference
2393 .take()
2394 .expect("functor is called at most once");
2395 if let Some(old_sr) = old_sr {
2396 let old_value = extract_sr_value::<T>(old_sr);
2397 let new_value = extract_sr_value::<T>(&new_shared_reference);
2398 if old_value == new_value {
2399 return None;
2400 }
2401 }
2402 Some((new_shared_reference, None, None))
2403 });
2404 }
2405
2406 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2415 where
2416 T: PartialEq + DeterministicHash + VcValueType,
2417 {
2418 self.conditional_update(|old_value| {
2419 if let Some(old_value) = old_value
2420 && old_value == &new_value
2421 {
2422 return None;
2423 }
2424 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2425
2426 Some((new_value, None, Some(content_hash)))
2427 });
2428 }
2429
2430 pub fn hashed_compare_and_update_with_shared_reference<T>(
2436 &self,
2437 new_shared_reference: SharedReference,
2438 ) where
2439 T: VcValueType + PartialEq + DeterministicHash,
2440 {
2441 let mut new_shared_reference = Some(new_shared_reference);
2442 self.conditional_update_with_shared_reference(&mut move |old_sr| {
2443 let new_shared_reference = new_shared_reference
2444 .take()
2445 .expect("functor is called at most once");
2446 if let Some(old_sr) = old_sr {
2447 let old_value = extract_sr_value::<T>(old_sr);
2448 let new_value = extract_sr_value::<T>(&new_shared_reference);
2449 if old_value == new_value {
2450 return None;
2451 }
2452 }
2453 let content_hash =
2454 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2455 Some((new_shared_reference, None, Some(content_hash)))
2456 });
2457 }
2458
2459 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2461 where
2462 T: PartialEq + VcValueType,
2463 VcReadTarget<T>: KeyedEq,
2464 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2465 {
2466 self.conditional_update(|old_value| {
2467 let Some(old_value) = old_value else {
2468 return Some((new_value, None, None));
2469 };
2470 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2471 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2472 let updated_keys = old_value.different_keys(new_value_ref);
2473 if updated_keys.is_empty() {
2474 return None;
2475 }
2476 let updated_key_hashes = updated_keys
2478 .into_iter()
2479 .map(|key| FxBuildHasher.hash_one(key))
2480 .collect();
2481 Some((new_value, Some(updated_key_hashes), None))
2482 });
2483 }
2484
2485 pub fn keyed_compare_and_update_with_shared_reference<T>(
2488 &self,
2489 new_shared_reference: SharedReference,
2490 ) where
2491 T: VcValueType + PartialEq,
2492 VcReadTarget<T>: KeyedEq,
2493 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2494 {
2495 let mut new_shared_reference = Some(new_shared_reference);
2496 self.conditional_update_with_shared_reference(&mut |old_sr| {
2497 let new_shared_reference = new_shared_reference
2498 .take()
2499 .expect("functor is called at most once");
2500 let Some(old_sr) = old_sr else {
2501 return Some((new_shared_reference, None, None));
2502 };
2503 let old_value = extract_sr_value::<T>(old_sr);
2504 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2505 let new_value = extract_sr_value::<T>(&new_shared_reference);
2506 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2507 let updated_keys = old_value.different_keys(new_value);
2508 if updated_keys.is_empty() {
2509 return None;
2510 }
2511 let updated_key_hashes = updated_keys
2513 .into_iter()
2514 .map(|key| FxBuildHasher.hash_one(key))
2515 .collect();
2516 Some((new_shared_reference, Some(updated_key_hashes), None))
2517 });
2518 }
2519
2520 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2522 where
2523 T: VcValueType,
2524 {
2525 let tt = turbo_tasks();
2526 tt.update_own_task_cell(
2527 self.current_task,
2528 self.index,
2529 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2530 None,
2531 None,
2532 verification_mode,
2533 )
2534 }
2535
2536 pub fn update_with_shared_reference(
2544 &self,
2545 shared_ref: SharedReference,
2546 verification_mode: VerificationMode,
2547 ) {
2548 let tt = turbo_tasks();
2549 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2550 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2551 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2552 shared_ref_exp != shared_ref
2554 } else {
2555 true
2556 }
2557 } else {
2558 true
2559 };
2560 if update {
2561 tt.update_own_task_cell(
2562 self.current_task,
2563 self.index,
2564 CellContent(Some(shared_ref)),
2565 None,
2566 None,
2567 verification_mode,
2568 )
2569 }
2570 }
2571}
2572
2573impl From<CurrentCellRef> for RawVc {
2574 fn from(cell: CurrentCellRef) -> Self {
2575 RawVc::task_cell(cell.current_task, cell.index)
2576 }
2577}
2578
2579fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2580 sr.0.downcast_ref::<T>()
2581 .expect("cannot update SharedReference of different type")
2582}
2583
2584pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2585 find_cell_by_id(T::get_value_type_id())
2586}
2587
2588pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2589 CURRENT_TASK_STATE.with(|ts| {
2590 let current_task = current_task("celling turbo_tasks values");
2591 let mut ts = ts.write().unwrap();
2592 let map = ts.cell_counters.as_mut().unwrap();
2593 let current_index = map.entry(ty).or_default();
2594 let index = *current_index;
2595 assert!(
2596 index <= CellId::MAX_CELL_INDEX,
2597 "task allocated more than {} cells of a single type",
2598 CellId::MAX_CELL_INDEX as u64 + 1,
2599 );
2600 *current_index += 1;
2601 CurrentCellRef {
2602 current_task,
2603 index: CellId::new(ty, index),
2604 }
2605 })
2606}
2607
2608pub(crate) async fn read_local_output(
2609 this: &dyn TurboTasksApi,
2610 execution_id: ExecutionId,
2611 local_task_id: LocalTaskId,
2612) -> Result<RawVc> {
2613 loop {
2614 match this.try_read_local_output(execution_id, local_task_id)? {
2615 Ok(raw_vc) => return Ok(raw_vc),
2616 Err(event_listener) => {
2617 if execute_read_target_inline(
2620 this,
2621 ScheduleKey::LocalTask(execution_id, local_task_id),
2622 ) {
2623 continue;
2624 }
2625 event_listener.await
2626 }
2627 }
2628 }
2629}
2630
2631#[cfg(test)]
2632mod tests {
2633 use super::*;
2634
2635 #[test]
2636 fn test_inline_execution_depth_guard_restores_depth() {
2637 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2638 {
2639 let _outer = InlineExecutionDepthGuard::enter();
2640 {
2641 let _inner = InlineExecutionDepthGuard::enter();
2642 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2643 }
2644 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2645 }
2646 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2647 }
2648
2649 #[test]
2650 fn test_inline_depth_cap() {
2651 assert!(inline_execution_allowed(), "nothing is nested yet");
2652 let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2653 .map(|_| InlineExecutionDepthGuard::enter())
2654 .collect::<Vec<_>>();
2655 assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2656 assert!(
2657 !inline_execution_allowed(),
2658 "at the nesting cap reads wait for a worker instead of executing inline"
2659 );
2660
2661 guards.pop();
2663 assert!(inline_execution_allowed());
2664 }
2665
2666 #[tokio::test]
2667 async fn test_poll_once_or_spawn_completed_execution() {
2668 assert!(
2669 poll_once_or_spawn(async {}),
2670 "a future that completes on the first poll is executed inline"
2671 );
2672 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2673 }
2674
2675 #[tokio::test]
2676 async fn test_poll_once_or_spawn_pending_execution() {
2677 let (tx, rx) = tokio::sync::oneshot::channel();
2678 let done = Arc::new(AtomicBool::new(false));
2679 let done_in_task = done.clone();
2680 assert!(
2681 !poll_once_or_spawn(async move {
2682 tokio::task::yield_now().await;
2684 done_in_task.store(true, Ordering::SeqCst);
2685 let _ = tx.send(());
2686 }),
2687 "a future that yields is not completed inline"
2688 );
2689 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2690
2691 rx.await.unwrap();
2693 assert!(done.load(Ordering::SeqCst));
2694 }
2695}