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 pin_task_for_gc(&self, task: TaskId);
205
206 fn unpin_task_for_gc(&self, task: TaskId);
208
209 fn connect_task(&self, task: TaskId);
210
211 fn spawn_detached_for_testing(&self, f: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
216
217 fn task_statistics(&self) -> &TaskStatisticsApi;
218
219 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send>>;
220
221 fn subscribe_to_compilation_events(
222 &self,
223 event_types: Option<Vec<String>>,
224 ) -> Receiver<Arc<dyn CompilationEvent>>;
225
226 fn is_tracking_dependencies(&self) -> bool;
228}
229
230pub struct Unused<T> {
232 inner: T,
233}
234
235impl<T> Unused<T> {
236 pub unsafe fn new_unchecked(inner: T) -> Self {
242 Self { inner }
243 }
244
245 pub unsafe fn get_unchecked(&self) -> &T {
251 &self.inner
252 }
253
254 pub fn into(self) -> T {
256 self.inner
257 }
258}
259
260#[allow(clippy::manual_non_exhaustive)]
261pub struct UpdateInfo {
262 pub duration: Duration,
263 pub tasks: usize,
264 pub reasons: InvalidationReasonSet,
265 #[allow(dead_code)]
266 placeholder_for_future_fields: (),
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encode, Decode)]
270pub enum TaskPersistence {
271 Persistent,
273
274 Transient,
281}
282
283impl Display for TaskPersistence {
284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 match self {
286 TaskPersistence::Persistent => write!(f, "persistent"),
287 TaskPersistence::Transient => write!(f, "transient"),
288 }
289 }
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
295pub enum InputResolution {
296 Resolved,
299 Unresolved,
301}
302
303impl InputResolution {
304 #[inline]
305 pub fn from_is_resolved(is_resolved: bool) -> Self {
306 if is_resolved {
307 Self::Resolved
308 } else {
309 Self::Unresolved
310 }
311 }
312
313 #[inline]
314 pub fn is_resolved(self) -> bool {
315 matches!(self, Self::Resolved)
316 }
317}
318
319#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
320pub enum ReadConsistency {
321 #[default]
324 Eventual,
325 Strong,
330}
331
332#[derive(Clone, Copy, Debug, Eq, PartialEq)]
333pub enum ReadCellTracking {
334 Tracked {
336 key: Option<u64>,
338 },
339 TrackOnlyError,
344 Untracked,
349}
350
351impl ReadCellTracking {
352 pub fn should_track(&self, is_err: bool) -> bool {
353 match self {
354 ReadCellTracking::Tracked { .. } => true,
355 ReadCellTracking::TrackOnlyError => is_err,
356 ReadCellTracking::Untracked => false,
357 }
358 }
359
360 pub fn key(&self) -> Option<u64> {
361 match self {
362 ReadCellTracking::Tracked { key } => *key,
363 ReadCellTracking::TrackOnlyError => None,
364 ReadCellTracking::Untracked => None,
365 }
366 }
367}
368
369impl Default for ReadCellTracking {
370 fn default() -> Self {
371 ReadCellTracking::Tracked { key: None }
372 }
373}
374
375impl Display for ReadCellTracking {
376 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377 match self {
378 ReadCellTracking::Tracked { key: None } => write!(f, "tracked"),
379 ReadCellTracking::Tracked { key: Some(key) } => write!(f, "tracked with key {key}"),
380 ReadCellTracking::TrackOnlyError => write!(f, "track only error"),
381 ReadCellTracking::Untracked => write!(f, "untracked"),
382 }
383 }
384}
385
386#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
387pub enum ReadTracking {
388 #[default]
390 Tracked,
391 TrackOnlyError,
396 Untracked,
401}
402
403impl ReadTracking {
404 pub fn should_track(&self, is_err: bool) -> bool {
405 match self {
406 ReadTracking::Tracked => true,
407 ReadTracking::TrackOnlyError => is_err,
408 ReadTracking::Untracked => false,
409 }
410 }
411}
412
413impl Display for ReadTracking {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 match self {
416 ReadTracking::Tracked => write!(f, "tracked"),
417 ReadTracking::TrackOnlyError => write!(f, "track only error"),
418 ReadTracking::Untracked => write!(f, "untracked"),
419 }
420 }
421}
422
423#[derive(Encode, Decode, Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
424pub enum TaskPriority {
425 #[default]
426 Initial,
427 Invalidation {
428 priority: Reverse<u32>,
429 },
430 Recomputation,
431}
432
433impl TaskPriority {
434 pub fn invalidation(priority: u32) -> Self {
435 Self::Invalidation {
436 priority: Reverse(priority),
437 }
438 }
439
440 pub fn initial() -> Self {
441 Self::Initial
442 }
443
444 pub fn leaf() -> Self {
445 Self::Invalidation {
446 priority: Reverse(0),
447 }
448 }
449
450 pub fn in_parent(&self, parent_priority: TaskPriority) -> Self {
451 match self {
452 TaskPriority::Initial => parent_priority,
453 TaskPriority::Invalidation { priority } => {
454 if let TaskPriority::Invalidation {
455 priority: parent_priority,
456 } = parent_priority
457 && priority.0 < parent_priority.0
458 {
459 Self::Invalidation {
460 priority: Reverse(parent_priority.0.saturating_add(1)),
461 }
462 } else {
463 *self
464 }
465 }
466 TaskPriority::Recomputation => TaskPriority::Recomputation,
467 }
468 }
469}
470
471impl Display for TaskPriority {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 match self {
474 TaskPriority::Initial => write!(f, "initial"),
475 TaskPriority::Invalidation { priority } => write!(f, "invalidation({})", priority.0),
476 TaskPriority::Recomputation => write!(f, "recomputation"),
477 }
478 }
479}
480
481enum ScheduledTask {
482 Task {
483 task_id: TaskId,
484 span: Span,
485 },
486 LocalTask {
487 ty: LocalTaskSpec,
488 persistence: TaskPersistence,
489 execution_id: ExecutionId,
490 local_task_id: LocalTaskId,
491 global_task_state: CurrentTaskStateHandle,
492 span: Span,
493 },
494}
495
496#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
500pub enum ScheduleKey {
501 Task(TaskId),
503 LocalTask(ExecutionId, LocalTaskId),
505}
506
507impl Claimable for ScheduledTask {
508 type Key = ScheduleKey;
509
510 fn claim_key(&self) -> Option<ScheduleKey> {
511 Some(match self {
512 ScheduledTask::Task { task_id, .. } => ScheduleKey::Task(*task_id),
513 ScheduledTask::LocalTask {
514 execution_id,
515 local_task_id,
516 ..
517 } => ScheduleKey::LocalTask(*execution_id, *local_task_id),
518 })
519 }
520}
521
522#[cfg(feature = "inline_execution_stats")]
523use std::sync::atomic::AtomicU64;
524
525#[cfg(feature = "inline_execution_stats")]
528#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
529pub struct InlineExecutionStats {
530 pub queued: u64,
532 pub claim_attempted: u64,
534 pub claim_completed: u64,
536 pub claim_yielded: u64,
538 pub claim_failed: u64,
540 pub waited_in_progress: u64,
542}
543
544#[derive(Default)]
550struct InlineExecutionCounters {
551 #[cfg(feature = "inline_execution_stats")]
552 claim_attempted: AtomicU64,
553 #[cfg(feature = "inline_execution_stats")]
554 claim_completed: AtomicU64,
555 #[cfg(feature = "inline_execution_stats")]
556 claim_yielded: AtomicU64,
557 #[cfg(feature = "inline_execution_stats")]
558 claim_failed: AtomicU64,
559 #[cfg(feature = "inline_execution_stats")]
560 waited_in_progress: AtomicU64,
561}
562
563impl InlineExecutionCounters {
564 #[inline]
566 fn claim_attempted(&self) {
567 #[cfg(feature = "inline_execution_stats")]
568 self.claim_attempted.fetch_add(1, Ordering::Relaxed);
569 }
570
571 #[inline]
573 fn claim_completed(&self) {
574 #[cfg(feature = "inline_execution_stats")]
575 self.claim_completed.fetch_add(1, Ordering::Relaxed);
576 }
577
578 #[inline]
580 fn claim_yielded(&self) {
581 #[cfg(feature = "inline_execution_stats")]
582 self.claim_yielded.fetch_add(1, Ordering::Relaxed);
583 }
584
585 #[inline]
587 fn claim_failed(&self) {
588 #[cfg(feature = "inline_execution_stats")]
589 self.claim_failed.fetch_add(1, Ordering::Relaxed);
590 }
591
592 #[cfg(feature = "inline_execution_stats")]
594 #[inline]
595 fn waited_in_progress(&self) {
596 self.waited_in_progress.fetch_add(1, Ordering::Relaxed);
597 }
598}
599
600#[cfg(feature = "inline_execution_stats")]
602pub(crate) fn inline_stats_requested() -> bool {
603 static REQUESTED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
604 std::env::var("TURBO_ENGINE_INLINE_STATS").is_ok_and(|value| value != "0")
605 });
606 *REQUESTED
607}
608
609const MAX_INLINE_EXECUTION_DEPTH: usize = 16;
612
613thread_local! {
614 static INLINE_EXECUTION_DEPTH: Cell<usize> = const { Cell::new(0) };
616}
617
618fn inline_execution_allowed() -> bool {
620 INLINE_EXECUTION_DEPTH.get() < MAX_INLINE_EXECUTION_DEPTH
621}
622
623struct InlineExecutionDepthGuard;
625
626impl InlineExecutionDepthGuard {
627 fn enter() -> Self {
628 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() + 1);
629 Self
630 }
631}
632
633impl Drop for InlineExecutionDepthGuard {
634 fn drop(&mut self) {
635 INLINE_EXECUTION_DEPTH.set(INLINE_EXECUTION_DEPTH.get() - 1);
636 }
637}
638
639fn poll_once_or_spawn(future: impl Future<Output = ()> + Send + 'static) -> bool {
642 let _depth_guard = InlineExecutionDepthGuard::enter();
643 let span_slot = InlineExecutionSpanSlot::default();
644 let mut future = Box::pin(INLINE_EXECUTION_SPAN.scope(span_slot.clone(), future));
645 match future
649 .as_mut()
650 .poll(&mut Context::from_waker(Waker::noop()))
651 {
652 Poll::Ready(()) => {
653 span_slot.record("complete");
654 true
655 }
656 Poll::Pending => {
657 span_slot.record("partial");
658 tokio::task::spawn(future);
659 false
660 }
661 }
662}
663
664pub(crate) fn execute_read_target_inline(
666 turbo_tasks: &dyn TurboTasksApi,
667 key: ScheduleKey,
668) -> bool {
669 if !inline_execution_allowed() {
670 return false;
672 }
673 turbo_tasks.try_execute_scheduled_task_inline(key)
674}
675
676pub struct TurboTasks<B: Backend + 'static> {
677 this: Weak<Self>,
678 backend: B,
679 execution_id_factory: IdFactory<ExecutionId>,
680 stopped: AtomicBool,
681 currently_scheduled_foreground_jobs: AtomicUsize,
682 currently_scheduled_background_jobs: AtomicUsize,
683 scheduled_tasks: AtomicUsize,
684 inline_counters: InlineExecutionCounters,
687 priority_runner:
688 Arc<PriorityRunner<TurboTasks<B>, ScheduledTask, TaskPriority, TurboTasksExecutor>>,
689 start: Mutex<Option<Instant>>,
690 aggregated_update: Mutex<(Option<(Duration, usize)>, InvalidationReasonSet)>,
691 event_foreground_start: Event,
693 event_foreground_done: Event,
696 event_background_done: Event,
698 compilation_events: CompilationEventQueue,
699}
700
701struct CurrentTaskState {
710 task_id: Option<TaskId>,
711 execution_id: ExecutionId,
712 priority: TaskPriority,
713
714 #[cfg(feature = "verify_determinism")]
717 stateful: bool,
718
719 has_invalidator: bool,
721
722 in_top_level_task: bool,
725
726 cell_counters: Option<AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>>,
731
732 local_tasks: LocalTaskTracker,
735}
736
737impl CurrentTaskState {
738 fn new(
739 task_id: TaskId,
740 execution_id: ExecutionId,
741 priority: TaskPriority,
742 in_top_level_task: bool,
743 ) -> Self {
744 Self {
745 task_id: Some(task_id),
746 execution_id,
747 priority,
748 #[cfg(feature = "verify_determinism")]
749 stateful: false,
750 has_invalidator: false,
751 in_top_level_task,
752 cell_counters: Some(AutoMap::default()),
753 local_tasks: LocalTaskTracker::new(),
754 }
755 }
756
757 fn new_temporary(
758 execution_id: ExecutionId,
759 priority: TaskPriority,
760 in_top_level_task: bool,
761 ) -> Self {
762 Self {
763 task_id: None,
764 execution_id,
765 priority,
766 #[cfg(feature = "verify_determinism")]
767 stateful: false,
768 has_invalidator: false,
769 in_top_level_task,
770 cell_counters: None,
771 local_tasks: LocalTaskTracker::new(),
772 }
773 }
774
775 fn assert_execution_id(&self, expected_execution_id: ExecutionId) {
776 if self.execution_id != expected_execution_id {
777 panic!(
778 "Local tasks can only be scheduled/awaited within the same execution of the \
779 parent task that created them"
780 );
781 }
782 }
783}
784
785#[derive(Clone)]
789struct CurrentTaskStateHandle {
790 inner: Arc<CurrentTaskStateInner>,
791}
792
793struct CurrentTaskStateInner {
794 current_task_id: Option<TaskId>,
795 state: RwLock<CurrentTaskState>,
796}
797
798impl CurrentTaskStateHandle {
799 fn new(state: CurrentTaskState) -> Self {
800 Self {
801 inner: Arc::new(CurrentTaskStateInner {
802 current_task_id: state.task_id,
803 state: RwLock::new(state),
804 }),
805 }
806 }
807
808 fn current_task_id(&self) -> Option<TaskId> {
809 self.inner.current_task_id
810 }
811}
812
813impl Deref for CurrentTaskStateHandle {
814 type Target = RwLock<CurrentTaskState>;
815
816 fn deref(&self) -> &Self::Target {
817 &self.inner.state
818 }
819}
820
821task_local! {
823 static TURBO_TASKS: Arc<dyn TurboTasksApi>;
825
826 static CURRENT_TASK_STATE: CurrentTaskStateHandle;
827
828 pub(crate) static SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK: bool;
833
834 static INLINE_EXECUTION_SPAN: InlineExecutionSpanSlot;
837}
838
839#[derive(Clone, Default)]
850struct InlineExecutionSpanSlot(Arc<Mutex<Option<Span>>>);
851
852impl InlineExecutionSpanSlot {
853 fn set(span: &Span) {
855 let _ = INLINE_EXECUTION_SPAN.try_with(|slot| {
856 *slot.0.lock().unwrap() = Some(span.clone());
857 });
858 }
859
860 fn record(&self, outcome: &'static str) {
864 if let Some(span) = self.0.lock().unwrap().as_ref() {
865 span.record("inline_execution", outcome);
866 }
867 }
868}
869
870impl<B: Backend + 'static> TurboTasks<B> {
871 pub fn new(backend: B) -> Arc<Self> {
877 let execution_id_factory = IdFactory::new(ExecutionId::MIN, ExecutionId::MAX);
878 let this = Arc::new_cyclic(|this| Self {
879 this: this.clone(),
880 backend,
881 execution_id_factory,
882 stopped: AtomicBool::new(false),
883 currently_scheduled_foreground_jobs: AtomicUsize::new(0),
884 currently_scheduled_background_jobs: AtomicUsize::new(0),
885 scheduled_tasks: AtomicUsize::new(0),
886 inline_counters: InlineExecutionCounters::default(),
887 priority_runner: Arc::new(PriorityRunner::new(TurboTasksExecutor)),
888 start: Default::default(),
889 aggregated_update: Default::default(),
890 event_foreground_done: Event::new(|| {
891 || "TurboTasks::event_foreground_done".to_string()
892 }),
893 event_foreground_start: Event::new(|| {
894 || "TurboTasks::event_foreground_start".to_string()
895 }),
896 event_background_done: Event::new(|| {
897 || "TurboTasks::event_background_done".to_string()
898 }),
899 compilation_events: CompilationEventQueue::default(),
900 });
901 this.backend.startup(&*this);
902 this
903 }
904
905 pub fn pin(&self) -> Arc<Self> {
906 self.this.upgrade().unwrap()
907 }
908
909 pub fn spawn_root_task<T, F, Fut>(&self, functor: F) -> TaskId
911 where
912 T: ?Sized,
913 F: Fn() -> Fut + Send + Sync + Clone + 'static,
914 Fut: Future<Output = Result<Vc<T>>> + Send,
915 {
916 let id = self.backend.create_transient_task(
917 TransientTaskType::Root(Box::new(move || {
918 let functor = functor.clone();
919 Box::pin(async move {
920 mark_top_level_task();
921 let raw_vc = functor().await?.node;
922 raw_vc.to_non_local().await
923 })
924 })),
925 self,
926 );
927 self.schedule(id, TaskPriority::initial());
928 id
929 }
930
931 pub fn dispose_root_task(&self, task_id: TaskId) {
932 self.backend.dispose_root_task(task_id, self);
933 }
934
935 pub fn pin_task_for_gc(&self, task_id: TaskId) {
939 self.backend.pin_task_for_gc(task_id, self);
940 }
941
942 pub fn unpin_task_for_gc(&self, task_id: TaskId) {
944 self.backend.unpin_task_for_gc(task_id, self);
945 }
946
947 #[track_caller]
951 fn spawn_once_task<T, Fut>(&self, future: Fut)
952 where
953 T: ?Sized,
954 Fut: Future<Output = Result<Vc<T>>> + Send + 'static,
955 {
956 let id = self.backend.create_transient_task(
957 TransientTaskType::Once(Box::pin(async move {
958 mark_top_level_task();
959 let raw_vc = future.await?.node;
960 raw_vc.to_non_local().await
961 })),
962 self,
963 );
964 self.schedule(id, TaskPriority::initial());
965 }
966
967 pub async fn run_once<T: TraceRawVcs + Send + 'static>(
968 &self,
969 future: impl Future<Output = Result<T>> + Send + 'static,
970 ) -> Result<T> {
971 let (tx, rx) = tokio::sync::oneshot::channel();
972 self.spawn_once_task(async move {
973 mark_top_level_task();
974 let result = future.await;
975 tx.send(result)
976 .map_err(|_| anyhow!("unable to send result"))?;
977 Ok(Completion::new())
978 });
979
980 rx.await?
981 }
982
983 #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")]
984 pub async fn run<T: TraceRawVcs + Send + 'static>(
985 &self,
986 future: impl Future<Output = Result<T>> + Send + 'static,
987 ) -> Result<T, TurboTasksExecutionError> {
988 self.begin_foreground_job();
989 let execution_id = self.execution_id_factory.wrapping_get();
991 let current_task_state = CurrentTaskStateHandle::new(CurrentTaskState::new_temporary(
992 execution_id,
993 TaskPriority::initial(),
994 true, ));
996
997 let result = TURBO_TASKS
998 .scope(
999 self.pin(),
1000 CURRENT_TASK_STATE.scope(current_task_state, async {
1001 let result = CaptureFuture::new(future).await;
1002
1003 wait_for_local_tasks().await;
1005
1006 match result {
1007 Ok(Ok(value)) => Ok(value),
1008 Ok(Err(err)) => Err(err.into()),
1009 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1010 }
1011 }),
1012 )
1013 .await;
1014 self.finish_foreground_job();
1015 result
1016 }
1017
1018 pub fn start_once_process(&self, future: impl Future<Output = ()> + Send + 'static) {
1019 let this = self.pin();
1020 tokio::spawn(async move {
1021 this.pin()
1022 .run_once(async move {
1023 this.finish_foreground_job();
1024 future.await;
1025 this.begin_foreground_job();
1026 Ok(())
1027 })
1028 .await
1029 .unwrap()
1030 });
1031 }
1032
1033 pub(crate) fn native_call(
1034 &self,
1035 native_fn: &'static NativeFunction,
1036 this: Option<RawVc>,
1037 arg: &mut dyn DynTaskInputsStorage,
1038 persistence: TaskPersistence,
1039 ) -> RawVc {
1040 RawVc::task_output(self.backend.get_or_create_task(
1041 native_fn,
1042 this,
1043 arg,
1044 current_task_if_available("turbo_function calls"),
1045 persistence,
1046 self,
1047 ))
1048 }
1049
1050 pub fn dynamic_call(
1051 &self,
1052 native_fn: &'static NativeFunction,
1053 this: Option<RawVc>,
1054 arg: &mut dyn DynTaskInputsStorage,
1055 inputs_resolved: InputResolution,
1056 persistence: TaskPersistence,
1057 ) -> RawVc {
1058 if inputs_resolved.is_resolved() && this.is_none_or(|this| this.is_resolved()) {
1059 return self.native_call(native_fn, this, arg, persistence);
1060 }
1061 let arg = arg.take_box();
1063 let task_type = LocalTaskSpec {
1064 task_type: LocalTaskType::ResolveNative { native_fn },
1065 this,
1066 arg,
1067 };
1068 self.schedule_local_task(task_type, persistence)
1069 }
1070
1071 pub fn trait_call(
1072 &self,
1073 trait_method: &'static TraitMethod,
1074 this: RawVc,
1075 arg: &mut dyn DynTaskInputsStorage,
1076 inputs_resolved: InputResolution,
1077 persistence: TaskPersistence,
1078 ) -> RawVc {
1079 if let Some((_, cell_id)) = this.as_task_cell() {
1083 match registry::get_value_type(cell_id.type_id()).get_trait_method(trait_method) {
1084 Some(native_fn) => {
1085 if let Some(filter) = native_fn.arg_meta.filter_owned {
1086 let (resolved, mut arg) = (filter)(arg);
1087 return self.dynamic_call(
1088 native_fn,
1089 Some(this),
1090 &mut arg,
1091 resolved,
1092 persistence,
1093 );
1094 } else {
1095 return self.dynamic_call(
1096 native_fn,
1097 Some(this),
1098 arg,
1099 inputs_resolved,
1100 persistence,
1101 );
1102 }
1103 }
1104 None => {
1105 }
1109 }
1110 }
1111
1112 let task_type = LocalTaskSpec {
1114 task_type: LocalTaskType::ResolveTrait { trait_method },
1115 this: Some(this),
1116 arg: arg.take_box(),
1117 };
1118
1119 self.schedule_local_task(task_type, persistence)
1120 }
1121
1122 #[track_caller]
1123 pub fn schedule(&self, task_id: TaskId, priority: TaskPriority) {
1124 self.begin_foreground_job();
1125 self.scheduled_tasks.fetch_add(1, Ordering::AcqRel);
1126
1127 let task = ScheduledTask::Task {
1128 task_id,
1129 span: Span::current(),
1130 };
1131 self.priority_runner.schedule(&self.pin(), task, priority);
1132 }
1133
1134 fn schedule_local_task(
1135 &self,
1136 ty: LocalTaskSpec,
1137 persistence: TaskPersistence,
1139 ) -> RawVc {
1140 let task_type = ty.task_type;
1141 let (global_task_state, execution_id, priority, local_task_id) =
1142 CURRENT_TASK_STATE.with(|gts| {
1143 let mut gts_write = gts.write().unwrap();
1144 let local_task_id = gts_write.local_tasks.create(task_type);
1145 (
1146 gts.clone(),
1147 gts_write.execution_id,
1148 gts_write.priority,
1149 local_task_id,
1150 )
1151 });
1152
1153 let task = ScheduledTask::LocalTask {
1154 ty,
1155 persistence,
1156 execution_id,
1157 local_task_id,
1158 global_task_state,
1159 span: Span::current(),
1160 };
1161 self.priority_runner.schedule(&self.pin(), task, priority);
1162
1163 RawVc::local_output(execution_id, local_task_id, persistence)
1164 }
1165
1166 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1168 let this = self.pin();
1169 self.inline_counters.claim_attempted();
1170 if let Some(future) = self.priority_runner.claim(&this, &key) {
1171 let completed = poll_once_or_spawn(future);
1172 if completed {
1173 self.inline_counters.claim_completed();
1174 } else {
1175 self.inline_counters.claim_yielded();
1176 }
1177 return completed;
1178 }
1179 self.inline_counters.claim_failed();
1180 false
1181 }
1182
1183 #[cfg(feature = "inline_execution_stats")]
1184 fn note_waited_for_in_progress_task(&self) {
1185 self.inline_counters.waited_in_progress();
1186 }
1187
1188 fn begin_foreground_job(&self) {
1189 if self
1190 .currently_scheduled_foreground_jobs
1191 .fetch_add(1, Ordering::AcqRel)
1192 == 0
1193 {
1194 *self.start.lock().unwrap() = Some(Instant::now());
1195 self.event_foreground_start.notify(usize::MAX);
1196 self.backend.idle_end(self);
1197 }
1198 }
1199
1200 fn finish_foreground_job(&self) {
1201 if self
1202 .currently_scheduled_foreground_jobs
1203 .fetch_sub(1, Ordering::AcqRel)
1204 == 1
1205 {
1206 self.backend.idle_start(self);
1207 let total = self.scheduled_tasks.load(Ordering::Acquire);
1210 self.scheduled_tasks.store(0, Ordering::Release);
1211 if let Some(start) = *self.start.lock().unwrap() {
1212 let (update, _) = &mut *self.aggregated_update.lock().unwrap();
1213 if let Some(update) = update.as_mut() {
1214 update.0 += start.elapsed();
1215 update.1 += total;
1216 } else {
1217 *update = Some((start.elapsed(), total));
1218 }
1219 }
1220 self.event_foreground_done.notify(usize::MAX);
1221 }
1222 }
1223
1224 fn begin_background_job(&self) {
1225 self.currently_scheduled_background_jobs
1226 .fetch_add(1, Ordering::Relaxed);
1227 }
1228
1229 fn finish_background_job(&self) {
1230 if self
1231 .currently_scheduled_background_jobs
1232 .fetch_sub(1, Ordering::Relaxed)
1233 == 1
1234 {
1235 self.event_background_done.notify(usize::MAX);
1236 }
1237 }
1238
1239 pub fn get_in_progress_count(&self) -> usize {
1240 self.currently_scheduled_foreground_jobs
1241 .load(Ordering::Acquire)
1242 }
1243
1244 #[cfg(feature = "inline_execution_stats")]
1247 #[doc(hidden)]
1248 pub fn inline_execution_stats(&self) -> InlineExecutionStats {
1249 let counters = &self.inline_counters;
1250 InlineExecutionStats {
1251 queued: self.priority_runner.total_queued(),
1252 claim_attempted: counters.claim_attempted.load(Ordering::Relaxed),
1253 claim_completed: counters.claim_completed.load(Ordering::Relaxed),
1254 claim_yielded: counters.claim_yielded.load(Ordering::Relaxed),
1255 claim_failed: counters.claim_failed.load(Ordering::Relaxed),
1256 waited_in_progress: counters.waited_in_progress.load(Ordering::Relaxed),
1257 }
1258 }
1259
1260 pub async fn wait_task_completion(
1272 &self,
1273 id: TaskId,
1274 consistency: ReadConsistency,
1275 ) -> Result<()> {
1276 read_task_output(
1277 self,
1278 id,
1279 ReadOutputOptions {
1280 tracking: ReadTracking::Untracked,
1282 consistency,
1283 },
1284 )
1285 .await?;
1286 Ok(())
1287 }
1288
1289 pub async fn get_or_wait_aggregated_update_info(&self, aggregation: Duration) -> UpdateInfo {
1292 self.aggregated_update_info(aggregation, Duration::MAX)
1293 .await
1294 .unwrap()
1295 }
1296
1297 pub async fn aggregated_update_info(
1301 &self,
1302 aggregation: Duration,
1303 timeout: Duration,
1304 ) -> Option<UpdateInfo> {
1305 let listener = self
1306 .event_foreground_done
1307 .listen_with_note(|| || "wait for update info".to_string());
1308 let wait_for_finish = {
1309 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1310 if aggregation.is_zero() {
1311 if let Some((duration, tasks)) = update.take() {
1312 return Some(UpdateInfo {
1313 duration,
1314 tasks,
1315 reasons: take(reason_set),
1316 placeholder_for_future_fields: (),
1317 });
1318 } else {
1319 true
1320 }
1321 } else {
1322 update.is_none()
1323 }
1324 };
1325 if wait_for_finish {
1326 if timeout == Duration::MAX {
1327 listener.await;
1329 } else {
1330 let start_listener = self
1332 .event_foreground_start
1333 .listen_with_note(|| || "wait for update info".to_string());
1334 if self
1335 .currently_scheduled_foreground_jobs
1336 .load(Ordering::Acquire)
1337 == 0
1338 {
1339 start_listener.await;
1340 } else {
1341 drop(start_listener);
1342 }
1343 if timeout.is_zero() || tokio::time::timeout(timeout, listener).await.is_err() {
1344 return None;
1346 }
1347 }
1348 }
1349 if !aggregation.is_zero() {
1350 loop {
1351 select! {
1352 () = tokio::time::sleep(aggregation) => {
1353 break;
1354 }
1355 () = self.event_foreground_done.listen_with_note(|| || "wait for update info".to_string()) => {
1356 }
1358 }
1359 }
1360 }
1361 let (update, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1362 if let Some((duration, tasks)) = update.take() {
1363 Some(UpdateInfo {
1364 duration,
1365 tasks,
1366 reasons: take(reason_set),
1367 placeholder_for_future_fields: (),
1368 })
1369 } else {
1370 panic!("aggregated_update_info must not called concurrently")
1371 }
1372 }
1373
1374 pub async fn wait_background_done(&self) {
1375 let listener = self.event_background_done.listen();
1376 if self
1377 .currently_scheduled_background_jobs
1378 .load(Ordering::Acquire)
1379 != 0
1380 {
1381 listener.await;
1382 }
1383 }
1384
1385 pub async fn stop_and_wait(&self) {
1386 #[cfg(feature = "inline_execution_stats")]
1387 if inline_stats_requested() {
1388 eprintln!(
1391 "turbo-tasks inline execution stats: {:#?}",
1392 self.inline_execution_stats()
1393 );
1394 }
1395 turbo_tasks_future_scope(self.pin(), async move {
1396 self.backend.stopping(self);
1397 self.stopped.store(true, Ordering::Release);
1398 {
1399 let listener = self
1400 .event_foreground_done
1401 .listen_with_note(|| || "wait for stop".to_string());
1402 if self
1403 .currently_scheduled_foreground_jobs
1404 .load(Ordering::Acquire)
1405 != 0
1406 {
1407 listener.await;
1408 }
1409 }
1410 {
1411 let listener = self.event_background_done.listen();
1412 if self
1413 .currently_scheduled_background_jobs
1414 .load(Ordering::Acquire)
1415 != 0
1416 {
1417 listener.await;
1418 }
1419 }
1420 self.backend.stop(self);
1421 })
1422 .await;
1423 }
1424
1425 #[track_caller]
1426 pub(crate) fn schedule_background_job<T>(&self, func: T)
1427 where
1428 T: AsyncFnOnce(Arc<TurboTasks<B>>) -> Arc<TurboTasks<B>> + Send + 'static,
1429 T::CallOnceFuture: Send,
1430 {
1431 let mut this = self.pin();
1432 self.begin_background_job();
1433 tokio::spawn(
1434 TURBO_TASKS
1435 .scope(this.clone(), async move {
1436 if !this.stopped.load(Ordering::Acquire) {
1437 this = func(this).await;
1438 }
1439 this.finish_background_job();
1440 })
1441 .in_current_span(),
1442 );
1443 }
1444
1445 fn finish_current_task_state(&self) -> FinishedTaskState {
1446 CURRENT_TASK_STATE.with(|cell| {
1447 let current_task_state = &*cell.write().unwrap();
1448 FinishedTaskState {
1449 #[cfg(feature = "verify_determinism")]
1450 stateful: current_task_state.stateful,
1451 has_invalidator: current_task_state.has_invalidator,
1452 }
1453 })
1454 }
1455
1456 pub fn backend(&self) -> &B {
1457 &self.backend
1458 }
1459
1460 pub fn get_current_task_priority(&self) -> TaskPriority {
1461 CURRENT_TASK_STATE
1462 .try_with(|task_state| task_state.read().unwrap().priority)
1463 .unwrap_or(TaskPriority::initial())
1464 }
1465
1466 pub fn is_idle(&self) -> bool {
1467 self.currently_scheduled_foreground_jobs
1468 .load(Ordering::Acquire)
1469 == 0
1470 }
1471
1472 #[track_caller]
1473 pub fn schedule_backend_background_job(&self, job: B::BackendJob) {
1474 self.schedule_background_job(async move |this| {
1475 this.backend.run_backend_job(job, &*this).await;
1476 this
1477 })
1478 }
1479}
1480
1481struct TurboTasksExecutor;
1482
1483async fn abort_on_panic<F: Future>(f: F) -> F::Output {
1488 match AssertUnwindSafe(f).catch_unwind().await {
1489 Ok(r) => r,
1490 Err(_) => {
1491 eprintln!(
1492 "\nturbo-tasks: an internal panic occurred outside the per-task panic \
1493 boundary. This is a bug in turbo-tasks/Turbopack — please report it at \
1494 https://github.com/vercel/next.js/discussions and include the panic message \
1495 and stack trace above.\n\nAborting."
1496 );
1497 abort();
1498 }
1499 }
1500}
1501
1502impl<B: Backend> Executor<TurboTasks<B>, ScheduledTask, TaskPriority> for TurboTasksExecutor {
1503 type Future = impl Future<Output = ()> + Send + 'static;
1504
1505 fn execute(
1506 &self,
1507 this: &Arc<TurboTasks<B>>,
1508 scheduled_task: ScheduledTask,
1509 priority: TaskPriority,
1510 ) -> Self::Future {
1511 match scheduled_task {
1512 ScheduledTask::Task { task_id, span } => {
1513 let this2 = this.clone();
1514 let this = this.clone();
1515 let future = async move {
1516 abort_on_panic(async {
1517 let execution_id = this.execution_id_factory.wrapping_get();
1520 let current_task_state =
1521 CurrentTaskStateHandle::new(CurrentTaskState::new(
1522 task_id,
1523 execution_id,
1524 priority,
1525 false, ));
1527 let single_execution_future = async {
1528 if this.stopped.load(Ordering::Acquire) {
1529 this.backend.task_execution_canceled(task_id, &*this);
1530 return None;
1531 }
1532
1533 let TaskExecutionSpec { future, span } = this
1534 .backend
1535 .try_start_task_execution(task_id, priority, &*this)?;
1536
1537 InlineExecutionSpanSlot::set(&span);
1540
1541 async {
1542 let result = CaptureFuture::new(future).await;
1543
1544 wait_for_local_tasks().await;
1546
1547 let result = match result {
1548 Ok(Ok(raw_vc)) => {
1549 raw_vc
1552 .to_non_local_unchecked_sync(&*this)
1553 .map_err(|err| err.into())
1554 }
1555 Ok(Err(err)) => Err(err.into()),
1556 Err(err) => Err(TurboTasksExecutionError::Panic(Arc::new(err))),
1557 };
1558
1559 let finished_state = this.finish_current_task_state();
1560 let cell_counters = CURRENT_TASK_STATE
1561 .with(|ts| ts.write().unwrap().cell_counters.take().unwrap());
1562 this.backend.task_execution_completed(
1563 task_id,
1564 result,
1565 &cell_counters,
1566 #[cfg(feature = "verify_determinism")]
1567 finished_state.stateful,
1568 finished_state.has_invalidator,
1569 &*this,
1570 )
1571 }
1572 .instrument(span)
1573 .await
1574 };
1575 if let Some(stale_priority) = CURRENT_TASK_STATE
1576 .scope(current_task_state, single_execution_future)
1577 .await
1578 {
1579 this.schedule(task_id, stale_priority);
1582 }
1583 this.finish_foreground_job();
1584 })
1585 .await
1586 };
1587
1588 Either::Left(TURBO_TASKS.scope(this2, future).instrument(span))
1589 }
1590 ScheduledTask::LocalTask {
1591 ty,
1592 persistence,
1593 execution_id: _,
1594 local_task_id,
1595 global_task_state,
1596 span,
1597 } => {
1598 let this2 = this.clone();
1599 let this = this.clone();
1600 let task_type = ty.task_type;
1601 let future = async move {
1602 let span = match &ty.task_type {
1603 LocalTaskType::ResolveNative { native_fn } => {
1604 native_fn.resolve_span(priority)
1605 }
1606 LocalTaskType::ResolveTrait { trait_method } => {
1607 trait_method.resolve_span(priority)
1608 }
1609 };
1610 InlineExecutionSpanSlot::set(&span);
1613 abort_on_panic(
1614 async move {
1615 let result = match ty.task_type {
1616 LocalTaskType::ResolveNative { native_fn } => {
1617 LocalTaskType::run_resolve_native(
1618 native_fn,
1619 ty.this,
1620 &*ty.arg,
1621 persistence,
1622 this,
1623 )
1624 .await
1625 }
1626 LocalTaskType::ResolveTrait { trait_method } => {
1627 LocalTaskType::run_resolve_trait(
1628 trait_method,
1629 ty.this.unwrap(),
1630 &*ty.arg,
1631 persistence,
1632 this,
1633 )
1634 .await
1635 }
1636 };
1637
1638 let output = match result {
1639 Ok(raw_vc) => OutputContent::Link(raw_vc),
1640 Err(err) => OutputContent::Error(
1641 TurboTasksExecutionError::from(err)
1642 .with_local_task_context(task_type.to_string()),
1643 ),
1644 };
1645
1646 CURRENT_TASK_STATE.with(move |gts| {
1647 gts.write()
1648 .unwrap()
1649 .local_tasks
1650 .complete(local_task_id, output);
1651 });
1652 }
1653 .instrument(span),
1654 )
1655 .await
1656 };
1657 let future = CURRENT_TASK_STATE.scope(global_task_state, future);
1658
1659 Either::Right(TURBO_TASKS.scope(this2, future).instrument(span))
1660 }
1661 }
1662 }
1663}
1664
1665struct FinishedTaskState {
1666 #[cfg(feature = "verify_determinism")]
1669 stateful: bool,
1670
1671 has_invalidator: bool,
1673}
1674
1675impl<B: Backend + 'static> TurboTasksCallApi for TurboTasks<B> {
1676 fn dynamic_call(
1677 &self,
1678 native_fn: &'static NativeFunction,
1679 this: Option<RawVc>,
1680 arg: &mut dyn DynTaskInputsStorage,
1681 inputs_resolved: InputResolution,
1682 persistence: TaskPersistence,
1683 ) -> RawVc {
1684 self.dynamic_call(native_fn, this, arg, inputs_resolved, persistence)
1685 }
1686 fn native_call(
1687 &self,
1688 native_fn: &'static NativeFunction,
1689 this: Option<RawVc>,
1690 arg: &mut dyn DynTaskInputsStorage,
1691 persistence: TaskPersistence,
1692 ) -> RawVc {
1693 self.native_call(native_fn, this, arg, persistence)
1694 }
1695 fn trait_call(
1696 &self,
1697 trait_method: &'static TraitMethod,
1698 this: RawVc,
1699 arg: &mut dyn DynTaskInputsStorage,
1700 inputs_resolved: InputResolution,
1701 persistence: TaskPersistence,
1702 ) -> RawVc {
1703 self.trait_call(trait_method, this, arg, inputs_resolved, persistence)
1704 }
1705
1706 #[track_caller]
1707 fn run(
1708 &self,
1709 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1710 ) -> Pin<Box<dyn Future<Output = Result<(), TurboTasksExecutionError>> + Send>> {
1711 let this = self.pin();
1712 Box::pin(async move { this.run(future).await })
1713 }
1714
1715 #[track_caller]
1716 fn run_once(
1717 &self,
1718 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1719 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1720 let this = self.pin();
1721 Box::pin(async move { this.run_once(future).await })
1722 }
1723
1724 #[track_caller]
1725 fn run_once_with_reason(
1726 &self,
1727 reason: StaticOrArc<dyn InvalidationReason>,
1728 future: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>,
1729 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
1730 {
1731 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1732 reason_set.insert(reason);
1733 }
1734 let this = self.pin();
1735 Box::pin(async move { this.run_once(future).await })
1736 }
1737
1738 #[track_caller]
1739 fn start_once_process(&self, future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1740 self.start_once_process(future)
1741 }
1742
1743 fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>) {
1744 if let Err(e) = self.compilation_events.send(event) {
1745 tracing::warn!("Failed to send compilation event: {e}");
1746 }
1747 }
1748
1749 fn get_task_name(&self, task: TaskId) -> String {
1750 self.backend.get_task_name(task, self)
1751 }
1752}
1753
1754impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
1755 #[instrument(level = "info", skip_all, name = "invalidate")]
1756 fn invalidate(&self, task: TaskId) {
1757 self.backend.invalidate_task(task, self);
1758 }
1759
1760 #[instrument(level = "info", skip_all, name = "invalidate", fields(name = display(&reason)))]
1761 fn invalidate_with_reason(&self, task: TaskId, reason: StaticOrArc<dyn InvalidationReason>) {
1762 {
1763 let (_, reason_set) = &mut *self.aggregated_update.lock().unwrap();
1764 reason_set.insert(reason);
1765 }
1766 self.backend.invalidate_task(task, self);
1767 }
1768
1769 fn invalidate_serialization(&self, task: TaskId) {
1770 self.backend.invalidate_serialization(task, self);
1771 }
1772
1773 #[track_caller]
1774 fn try_read_task_output(
1775 &self,
1776 task: TaskId,
1777 options: ReadOutputOptions,
1778 ) -> Result<ReadOutcome<RawVc>> {
1779 if options.consistency == ReadConsistency::Eventual {
1780 debug_assert_not_in_top_level_task("read_task_output");
1781 }
1782 self.backend.try_read_task_output(
1783 task,
1784 current_task_if_available("reading Vcs"),
1785 options,
1786 self,
1787 )
1788 }
1789
1790 #[track_caller]
1791 fn try_read_task_cell(
1792 &self,
1793 task: TaskId,
1794 index: CellId,
1795 options: ReadCellOptions,
1796 ) -> Result<ReadOutcome<TypedCellContent>> {
1797 let reader = current_task_if_available("reading Vcs");
1798 self.backend
1799 .try_read_task_cell(task, index, reader, options, self)
1800 }
1801
1802 fn try_read_own_task_cell(
1803 &self,
1804 current_task: TaskId,
1805 index: CellId,
1806 ) -> Result<TypedCellContent> {
1807 self.backend
1808 .try_read_own_task_cell(current_task, index, self)
1809 }
1810
1811 #[track_caller]
1812 fn try_read_local_output(
1813 &self,
1814 execution_id: ExecutionId,
1815 local_task_id: LocalTaskId,
1816 ) -> Result<Result<RawVc, EventListener>> {
1817 debug_assert_not_in_top_level_task("read_local_output");
1818 CURRENT_TASK_STATE.with(|gts| {
1819 let gts_read = gts.read().unwrap();
1820
1821 gts_read.assert_execution_id(execution_id);
1826
1827 match gts_read.local_tasks.get(local_task_id) {
1828 LocalTask::Scheduled { done_event } => Ok(Err(done_event.listen())),
1829 LocalTask::Done { output } => Ok(Ok(output.as_read_result()?)),
1830 }
1831 })
1832 }
1833
1834 fn read_task_collectibles(&self, task: TaskId, trait_id: TraitTypeId) -> TaskCollectiblesMap {
1835 self.backend.read_task_collectibles(
1838 task,
1839 trait_id,
1840 current_task_if_available("reading collectibles"),
1841 self,
1842 )
1843 }
1844
1845 fn try_execute_scheduled_task_inline(&self, key: ScheduleKey) -> bool {
1846 self.try_execute_scheduled_task_inline(key)
1847 }
1848
1849 #[cfg(feature = "inline_execution_stats")]
1850 fn note_waited_for_in_progress_task(&self) {
1851 self.note_waited_for_in_progress_task()
1852 }
1853
1854 fn emit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc) {
1855 self.backend.emit_collectible(
1856 trait_type,
1857 collectible,
1858 current_task("emitting collectible"),
1859 self,
1860 );
1861 }
1862
1863 fn unemit_collectible(&self, trait_type: TraitTypeId, collectible: RawVc, count: u32) {
1864 self.backend.unemit_collectible(
1865 trait_type,
1866 collectible,
1867 count,
1868 current_task("emitting collectible"),
1869 self,
1870 );
1871 }
1872
1873 fn unemit_collectibles(&self, trait_type: TraitTypeId, collectibles: &TaskCollectiblesMap) {
1874 for (&collectible, &count) in collectibles {
1875 if count > 0 {
1876 self.backend.unemit_collectible(
1877 trait_type,
1878 collectible,
1879 count as u32,
1880 current_task("emitting collectible"),
1881 self,
1882 );
1883 }
1884 }
1885 }
1886
1887 fn read_own_task_cell(&self, task: TaskId, index: CellId) -> Result<TypedCellContent> {
1888 self.try_read_own_task_cell(task, index)
1889 }
1890
1891 fn update_own_task_cell(
1892 &self,
1893 task: TaskId,
1894 index: CellId,
1895 content: CellContent,
1896 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
1897 content_hash: Option<CellHash>,
1898 verification_mode: VerificationMode,
1899 ) {
1900 self.backend.update_task_cell(
1901 task,
1902 index,
1903 content,
1904 updated_key_hashes,
1905 content_hash,
1906 verification_mode,
1907 self,
1908 );
1909 }
1910
1911 fn connect_task(&self, task: TaskId) {
1912 self.backend
1913 .connect_task(task, current_task_if_available("connecting task"), self);
1914 }
1915
1916 fn mark_own_task_as_finished(&self, task: TaskId) {
1917 self.backend.mark_own_task_as_finished(task, self);
1918 }
1919
1920 fn pin_task_for_gc(&self, task: TaskId) {
1921 self.backend.pin_task_for_gc(task, self);
1922 }
1923
1924 fn unpin_task_for_gc(&self, task: TaskId) {
1925 self.backend.unpin_task_for_gc(task, self);
1926 }
1927
1928 fn spawn_detached_for_testing(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1931 let global_task_state = CURRENT_TASK_STATE.with(|ts| ts.clone());
1934 global_task_state
1935 .write()
1936 .unwrap()
1937 .local_tasks
1938 .register_detached();
1939 let wrapped = async move {
1940 struct DropGuard;
1942 impl Drop for DropGuard {
1943 fn drop(&mut self) {
1944 CURRENT_TASK_STATE
1945 .with(|ts| ts.write().unwrap().local_tasks.decrement_in_flight());
1946 }
1947 }
1948 let _guard = DropGuard;
1949 fut.await;
1950 };
1951 tokio::spawn(TURBO_TASKS.scope(
1952 turbo_tasks(),
1953 CURRENT_TASK_STATE.scope(global_task_state, wrapped),
1954 ));
1955 }
1956
1957 fn task_statistics(&self) -> &TaskStatisticsApi {
1958 self.backend.task_statistics()
1959 }
1960
1961 fn stop_and_wait(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
1962 let this = self.pin();
1963 Box::pin(async move {
1964 this.stop_and_wait().await;
1965 })
1966 }
1967
1968 fn subscribe_to_compilation_events(
1969 &self,
1970 event_types: Option<Vec<String>>,
1971 ) -> Receiver<Arc<dyn CompilationEvent>> {
1972 self.compilation_events.subscribe(event_types)
1973 }
1974
1975 fn is_tracking_dependencies(&self) -> bool {
1976 self.backend.is_tracking_dependencies()
1977 }
1978}
1979
1980async fn wait_for_local_tasks() {
1981 let listener =
1982 CURRENT_TASK_STATE.with(|ts| ts.read().unwrap().local_tasks.listen_for_in_flight());
1983 let Some(listener) = listener else {
1984 return;
1985 };
1986 listener.await;
1987}
1988
1989pub(crate) fn current_task_if_available(from: &str) -> Option<TaskId> {
1990 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
1991 Ok(id) => id,
1992 Err(_) => panic!(
1993 "{from} can only be used in the context of a turbo_tasks task execution or \
1994 turbo_tasks run"
1995 ),
1996 }
1997}
1998
1999pub(crate) fn current_task(from: &str) -> TaskId {
2000 match CURRENT_TASK_STATE.try_with(|ts| ts.current_task_id()) {
2001 Ok(Some(id)) => id,
2002 Ok(None) | Err(_) => {
2003 panic!("{from} can only be used in the context of a turbo_tasks task execution")
2004 }
2005 }
2006}
2007
2008#[track_caller]
2011pub(crate) fn debug_assert_in_top_level_task(message: &str) {
2012 if !cfg!(debug_assertions) {
2013 return;
2014 }
2015
2016 let in_top_level = CURRENT_TASK_STATE
2017 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2018 .unwrap_or(true);
2019 if !in_top_level {
2020 panic!("{message}");
2021 }
2022}
2023
2024#[track_caller]
2025pub(crate) fn debug_assert_not_in_top_level_task(operation: &str) {
2026 if !cfg!(debug_assertions) {
2027 return;
2028 }
2029
2030 let suppressed = SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK
2033 .try_with(|&suppressed| suppressed)
2034 .unwrap_or(false);
2035 if suppressed {
2036 return;
2037 }
2038
2039 let in_top_level = CURRENT_TASK_STATE
2040 .try_with(|ts| ts.read().unwrap().in_top_level_task)
2041 .unwrap_or(false);
2042 if in_top_level {
2043 panic!(
2044 "Eventually consistent read ({operation}) cannot be performed from a top-level task. \
2045 Top-level tasks (e.g. code inside `.run_once(...)`) must use strongly consistent \
2046 reads to avoid leaking inconsistent return values."
2047 );
2048 }
2049}
2050
2051pub async fn run<T: Send + 'static>(
2052 tt: Arc<dyn TurboTasksApi>,
2053 future: impl Future<Output = Result<T>> + Send + 'static,
2054) -> Result<T> {
2055 let (tx, rx) = tokio::sync::oneshot::channel();
2056
2057 tt.run(Box::pin(async move {
2058 let result = future.await?;
2059 tx.send(result)
2060 .map_err(|_| anyhow!("unable to send result"))?;
2061 Ok(())
2062 }))
2063 .await?;
2064
2065 Ok(rx.await?)
2066}
2067
2068pub async fn run_once<T: Send + 'static>(
2069 tt: Arc<dyn TurboTasksApi>,
2070 future: impl Future<Output = Result<T>> + Send + 'static,
2071) -> Result<T> {
2072 let (tx, rx) = tokio::sync::oneshot::channel();
2073
2074 tt.run_once(Box::pin(async move {
2075 let result = future.await?;
2076 tx.send(result)
2077 .map_err(|_| anyhow!("unable to send result"))?;
2078 Ok(())
2079 }))
2080 .await?;
2081
2082 Ok(rx.await?)
2083}
2084
2085pub async fn run_once_with_reason<T: Send + 'static>(
2086 tt: Arc<dyn TurboTasksApi>,
2087 reason: impl InvalidationReason,
2088 future: impl Future<Output = Result<T>> + Send + 'static,
2089) -> Result<T> {
2090 let (tx, rx) = tokio::sync::oneshot::channel();
2091
2092 tt.run_once_with_reason(
2093 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
2094 Box::pin(async move {
2095 let result = future.await?;
2096 tx.send(result)
2097 .map_err(|_| anyhow!("unable to send result"))?;
2098 Ok(())
2099 }),
2100 )
2101 .await?;
2102
2103 Ok(rx.await?)
2104}
2105
2106pub fn dynamic_call(
2108 func: &'static NativeFunction,
2109 this: Option<RawVc>,
2110 arg: &mut dyn DynTaskInputsStorage,
2111 inputs_resolved: InputResolution,
2112 persistence: TaskPersistence,
2113) -> RawVc {
2114 with_turbo_tasks(|tt| tt.dynamic_call(func, this, arg, inputs_resolved, persistence))
2115}
2116
2117pub fn trait_call(
2119 trait_method: &'static TraitMethod,
2120 this: RawVc,
2121 arg: &mut dyn DynTaskInputsStorage,
2122 inputs_resolved: InputResolution,
2123 persistence: TaskPersistence,
2124) -> RawVc {
2125 with_turbo_tasks(|tt| tt.trait_call(trait_method, this, arg, inputs_resolved, persistence))
2126}
2127
2128pub fn turbo_tasks() -> Arc<dyn TurboTasksApi> {
2129 TURBO_TASKS.with(|arc| arc.clone())
2130}
2131
2132pub fn turbo_tasks_weak() -> Weak<dyn TurboTasksApi> {
2133 TURBO_TASKS.with(Arc::downgrade)
2134}
2135
2136pub fn try_turbo_tasks() -> Option<Arc<dyn TurboTasksApi>> {
2137 TURBO_TASKS.try_with(|arc| arc.clone()).ok()
2138}
2139
2140pub fn with_turbo_tasks<T>(func: impl FnOnce(&Arc<dyn TurboTasksApi>) -> T) -> T {
2141 TURBO_TASKS.with(|arc| func(arc))
2142}
2143
2144pub fn turbo_tasks_scope<T>(tt: Arc<dyn TurboTasksApi>, f: impl FnOnce() -> T) -> T {
2145 TURBO_TASKS.sync_scope(tt, f)
2146}
2147
2148pub fn turbo_tasks_future_scope<T>(
2149 tt: Arc<dyn TurboTasksApi>,
2150 f: impl Future<Output = T>,
2151) -> impl Future<Output = T> {
2152 TURBO_TASKS.scope(tt, f)
2153}
2154
2155pub fn spawn_detached_for_testing(f: impl Future<Output = ()> + Send + 'static) {
2160 turbo_tasks().spawn_detached_for_testing(Box::pin(f));
2161}
2162
2163pub fn mark_finished() {
2166 with_turbo_tasks(|tt| {
2167 tt.mark_own_task_as_finished(current_task("turbo_tasks::mark_finished()"))
2168 });
2169}
2170
2171pub fn get_serialization_invalidator() -> SerializationInvalidator {
2177 CURRENT_TASK_STATE.with(|cell| {
2178 let CurrentTaskState {
2179 task_id,
2180 #[cfg(feature = "verify_determinism")]
2181 stateful,
2182 ..
2183 } = &mut *cell.write().unwrap();
2184 #[cfg(feature = "verify_determinism")]
2185 {
2186 *stateful = true;
2187 }
2188 let Some(task_id) = *task_id else {
2189 panic!(
2190 "get_serialization_invalidator() can only be used in the context of a turbo_tasks \
2191 task execution"
2192 );
2193 };
2194 SerializationInvalidator::new(task_id)
2195 })
2196}
2197
2198pub fn mark_invalidator() {
2199 CURRENT_TASK_STATE.with(|cell| {
2200 let CurrentTaskState {
2201 has_invalidator, ..
2202 } = &mut *cell.write().unwrap();
2203 *has_invalidator = true;
2204 })
2205}
2206
2207pub fn mark_stateful() {
2213 #[cfg(feature = "verify_determinism")]
2214 {
2215 CURRENT_TASK_STATE.with(|cell| {
2216 let CurrentTaskState { stateful, .. } = &mut *cell.write().unwrap();
2217 *stateful = true;
2218 })
2219 }
2220 }
2222
2223pub fn mark_top_level_task() {
2227 if cfg!(debug_assertions) {
2228 CURRENT_TASK_STATE.with(|cell| {
2229 cell.write().unwrap().in_top_level_task = true;
2230 })
2231 }
2232}
2233
2234pub fn unmark_top_level_task_may_leak_eventually_consistent_state() {
2245 if cfg!(debug_assertions) {
2246 CURRENT_TASK_STATE.with(|cell| {
2247 cell.write().unwrap().in_top_level_task = false;
2248 })
2249 }
2250}
2251
2252pub fn prevent_gc() {
2260 if let Some(task) = current_task_if_available("prevent_gc") {
2261 with_turbo_tasks(|tt| tt.pin_task_for_gc(task));
2262 }
2263}
2264
2265pub fn emit<T: VcValueTrait + ?Sized>(collectible: ResolvedVc<T>) {
2266 with_turbo_tasks(|tt| {
2267 let raw_vc = collectible.node.node;
2268 tt.emit_collectible(T::get_trait_type_id(), raw_vc)
2269 })
2270}
2271
2272pub(crate) async fn read_task_output(
2273 this: &dyn TurboTasksApi,
2274 id: TaskId,
2275 options: ReadOutputOptions,
2276) -> Result<RawVc> {
2277 loop {
2278 match this.try_read_task_output(id, options)? {
2279 ReadOutcome::Value(result) => return Ok(result),
2280 ReadOutcome::Scheduled(listener) => {
2281 if execute_read_target_inline(this, ScheduleKey::Task(id)) {
2283 continue;
2284 }
2285 listener.await
2286 }
2287 ReadOutcome::InProgress(listener) => {
2288 #[cfg(feature = "inline_execution_stats")]
2290 this.note_waited_for_in_progress_task();
2291 listener.await
2292 }
2293 }
2294 }
2295}
2296
2297#[derive(Clone, Copy)]
2303pub struct CurrentCellRef {
2304 current_task: TaskId,
2305 index: CellId,
2306}
2307
2308type VcReadTarget<T> = <<T as VcValueType>::Read as VcRead<T>>::Target;
2309
2310type CellUpdate = (
2313 SharedReference,
2314 Option<SmallVec<[u64; 2]>>,
2315 Option<CellHash>,
2316);
2317
2318type CellUpdateFn<'l> = dyn FnMut(Option<&SharedReference>) -> Option<CellUpdate> + 'l;
2321
2322impl CurrentCellRef {
2323 fn conditional_update<T>(
2325 &self,
2326 functor: impl FnOnce(Option<&T>) -> Option<(T, Option<SmallVec<[u64; 2]>>, Option<CellHash>)>,
2327 ) where
2328 T: VcValueType,
2329 {
2330 let mut functor = Some(functor);
2333 self.conditional_update_with_shared_reference(&mut |old_shared_reference| {
2334 let functor = functor.take().expect("functor is called at most once");
2335 let old_ref = old_shared_reference.and_then(|sr| sr.0.downcast_ref::<T>());
2336 let (new_value, updated_key_hashes, content_hash) = functor(old_ref)?;
2337 Some((
2338 SharedReference::new(triomphe::Arc::new(new_value)),
2339 updated_key_hashes,
2340 content_hash,
2341 ))
2342 })
2343 }
2344
2345 fn conditional_update_with_shared_reference(&self, functor: &mut CellUpdateFn<'_>) {
2352 let tt = turbo_tasks();
2353 let cell_content = tt.read_own_task_cell(self.current_task, self.index).ok();
2354 let update = functor(cell_content.as_ref().and_then(|cc| cc.1.0.as_ref()));
2355 if let Some((update, updated_key_hashes, content_hash)) = update {
2356 tt.update_own_task_cell(
2357 self.current_task,
2358 self.index,
2359 CellContent(Some(update)),
2360 updated_key_hashes,
2361 content_hash,
2362 VerificationMode::EqualityCheck,
2363 )
2364 }
2365 }
2366
2367 pub fn compare_and_update<T>(&self, new_value: T)
2402 where
2403 T: PartialEq + VcValueType,
2404 {
2405 self.conditional_update(|old_value| {
2406 if let Some(old_value) = old_value
2407 && old_value == &new_value
2408 {
2409 return None;
2410 }
2411 Some((new_value, None, None))
2412 });
2413 }
2414
2415 pub fn compare_and_update_with_shared_reference<T>(&self, new_shared_reference: SharedReference)
2423 where
2424 T: VcValueType + PartialEq,
2425 {
2426 let mut new_shared_reference = Some(new_shared_reference);
2427 self.conditional_update_with_shared_reference(&mut |old_sr| {
2428 let new_shared_reference = new_shared_reference
2429 .take()
2430 .expect("functor is called at most once");
2431 if let Some(old_sr) = old_sr {
2432 let old_value = extract_sr_value::<T>(old_sr);
2433 let new_value = extract_sr_value::<T>(&new_shared_reference);
2434 if old_value == new_value {
2435 return None;
2436 }
2437 }
2438 Some((new_shared_reference, None, None))
2439 });
2440 }
2441
2442 pub fn hashed_compare_and_update<T>(&self, new_value: T)
2451 where
2452 T: PartialEq + DeterministicHash + VcValueType,
2453 {
2454 self.conditional_update(|old_value| {
2455 if let Some(old_value) = old_value
2456 && old_value == &new_value
2457 {
2458 return None;
2459 }
2460 let content_hash = hash_xxh3_hash128(&new_value).to_le_bytes();
2461
2462 Some((new_value, None, Some(content_hash)))
2463 });
2464 }
2465
2466 pub fn hashed_compare_and_update_with_shared_reference<T>(
2472 &self,
2473 new_shared_reference: SharedReference,
2474 ) where
2475 T: VcValueType + PartialEq + DeterministicHash,
2476 {
2477 let mut new_shared_reference = Some(new_shared_reference);
2478 self.conditional_update_with_shared_reference(&mut move |old_sr| {
2479 let new_shared_reference = new_shared_reference
2480 .take()
2481 .expect("functor is called at most once");
2482 if let Some(old_sr) = old_sr {
2483 let old_value = extract_sr_value::<T>(old_sr);
2484 let new_value = extract_sr_value::<T>(&new_shared_reference);
2485 if old_value == new_value {
2486 return None;
2487 }
2488 }
2489 let content_hash =
2490 hash_xxh3_hash128(extract_sr_value::<T>(&new_shared_reference)).to_le_bytes();
2491 Some((new_shared_reference, None, Some(content_hash)))
2492 });
2493 }
2494
2495 pub fn keyed_compare_and_update<T>(&self, new_value: T)
2497 where
2498 T: PartialEq + VcValueType,
2499 VcReadTarget<T>: KeyedEq,
2500 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2501 {
2502 self.conditional_update(|old_value| {
2503 let Some(old_value) = old_value else {
2504 return Some((new_value, None, None));
2505 };
2506 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2507 let new_value_ref = <T as VcValueType>::Read::value_to_target_ref(&new_value);
2508 let updated_keys = old_value.different_keys(new_value_ref);
2509 if updated_keys.is_empty() {
2510 return None;
2511 }
2512 let updated_key_hashes = updated_keys
2514 .into_iter()
2515 .map(|key| FxBuildHasher.hash_one(key))
2516 .collect();
2517 Some((new_value, Some(updated_key_hashes), None))
2518 });
2519 }
2520
2521 pub fn keyed_compare_and_update_with_shared_reference<T>(
2524 &self,
2525 new_shared_reference: SharedReference,
2526 ) where
2527 T: VcValueType + PartialEq,
2528 VcReadTarget<T>: KeyedEq,
2529 <VcReadTarget<T> as KeyedEq>::Key: std::hash::Hash,
2530 {
2531 let mut new_shared_reference = Some(new_shared_reference);
2532 self.conditional_update_with_shared_reference(&mut |old_sr| {
2533 let new_shared_reference = new_shared_reference
2534 .take()
2535 .expect("functor is called at most once");
2536 let Some(old_sr) = old_sr else {
2537 return Some((new_shared_reference, None, None));
2538 };
2539 let old_value = extract_sr_value::<T>(old_sr);
2540 let old_value = <T as VcValueType>::Read::value_to_target_ref(old_value);
2541 let new_value = extract_sr_value::<T>(&new_shared_reference);
2542 let new_value = <T as VcValueType>::Read::value_to_target_ref(new_value);
2543 let updated_keys = old_value.different_keys(new_value);
2544 if updated_keys.is_empty() {
2545 return None;
2546 }
2547 let updated_key_hashes = updated_keys
2549 .into_iter()
2550 .map(|key| FxBuildHasher.hash_one(key))
2551 .collect();
2552 Some((new_shared_reference, Some(updated_key_hashes), None))
2553 });
2554 }
2555
2556 pub fn update<T>(&self, new_value: T, verification_mode: VerificationMode)
2558 where
2559 T: VcValueType,
2560 {
2561 let tt = turbo_tasks();
2562 tt.update_own_task_cell(
2563 self.current_task,
2564 self.index,
2565 CellContent(Some(SharedReference::new(triomphe::Arc::new(new_value)))),
2566 None,
2567 None,
2568 verification_mode,
2569 )
2570 }
2571
2572 pub fn update_with_shared_reference(
2580 &self,
2581 shared_ref: SharedReference,
2582 verification_mode: VerificationMode,
2583 ) {
2584 let tt = turbo_tasks();
2585 let update = if matches!(verification_mode, VerificationMode::EqualityCheck) {
2586 let content = tt.read_own_task_cell(self.current_task, self.index).ok();
2587 if let Some(TypedCellContent(_, CellContent(Some(shared_ref_exp)))) = content {
2588 shared_ref_exp != shared_ref
2590 } else {
2591 true
2592 }
2593 } else {
2594 true
2595 };
2596 if update {
2597 tt.update_own_task_cell(
2598 self.current_task,
2599 self.index,
2600 CellContent(Some(shared_ref)),
2601 None,
2602 None,
2603 verification_mode,
2604 )
2605 }
2606 }
2607}
2608
2609impl From<CurrentCellRef> for RawVc {
2610 fn from(cell: CurrentCellRef) -> Self {
2611 RawVc::task_cell(cell.current_task, cell.index)
2612 }
2613}
2614
2615fn extract_sr_value<T: VcValueType>(sr: &SharedReference) -> &T {
2616 sr.0.downcast_ref::<T>()
2617 .expect("cannot update SharedReference of different type")
2618}
2619
2620pub fn find_cell_by_type<T: VcValueType>() -> CurrentCellRef {
2621 find_cell_by_id(T::get_value_type_id())
2622}
2623
2624pub fn find_cell_by_id(ty: ValueTypeId) -> CurrentCellRef {
2625 CURRENT_TASK_STATE.with(|ts| {
2626 let current_task = current_task("celling turbo_tasks values");
2627 let mut ts = ts.write().unwrap();
2628 let map = ts.cell_counters.as_mut().unwrap();
2629 let current_index = map.entry(ty).or_default();
2630 let index = *current_index;
2631 assert!(
2632 index <= CellId::MAX_CELL_INDEX,
2633 "task allocated more than {} cells of a single type",
2634 CellId::MAX_CELL_INDEX as u64 + 1,
2635 );
2636 *current_index += 1;
2637 CurrentCellRef {
2638 current_task,
2639 index: CellId::new(ty, index),
2640 }
2641 })
2642}
2643
2644pub(crate) async fn read_local_output(
2645 this: &dyn TurboTasksApi,
2646 execution_id: ExecutionId,
2647 local_task_id: LocalTaskId,
2648) -> Result<RawVc> {
2649 loop {
2650 match this.try_read_local_output(execution_id, local_task_id)? {
2651 Ok(raw_vc) => return Ok(raw_vc),
2652 Err(event_listener) => {
2653 if execute_read_target_inline(
2656 this,
2657 ScheduleKey::LocalTask(execution_id, local_task_id),
2658 ) {
2659 continue;
2660 }
2661 event_listener.await
2662 }
2663 }
2664 }
2665}
2666
2667#[cfg(test)]
2668mod tests {
2669 use super::*;
2670
2671 #[test]
2672 fn test_inline_execution_depth_guard_restores_depth() {
2673 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2674 {
2675 let _outer = InlineExecutionDepthGuard::enter();
2676 {
2677 let _inner = InlineExecutionDepthGuard::enter();
2678 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 2);
2679 }
2680 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 1);
2681 }
2682 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2683 }
2684
2685 #[test]
2686 fn test_inline_depth_cap() {
2687 assert!(inline_execution_allowed(), "nothing is nested yet");
2688 let mut guards = (0..MAX_INLINE_EXECUTION_DEPTH)
2689 .map(|_| InlineExecutionDepthGuard::enter())
2690 .collect::<Vec<_>>();
2691 assert_eq!(INLINE_EXECUTION_DEPTH.get(), MAX_INLINE_EXECUTION_DEPTH);
2692 assert!(
2693 !inline_execution_allowed(),
2694 "at the nesting cap reads wait for a worker instead of executing inline"
2695 );
2696
2697 guards.pop();
2699 assert!(inline_execution_allowed());
2700 }
2701
2702 #[tokio::test]
2703 async fn test_poll_once_or_spawn_completed_execution() {
2704 assert!(
2705 poll_once_or_spawn(async {}),
2706 "a future that completes on the first poll is executed inline"
2707 );
2708 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2709 }
2710
2711 #[tokio::test]
2712 async fn test_poll_once_or_spawn_pending_execution() {
2713 let (tx, rx) = tokio::sync::oneshot::channel();
2714 let done = Arc::new(AtomicBool::new(false));
2715 let done_in_task = done.clone();
2716 assert!(
2717 !poll_once_or_spawn(async move {
2718 tokio::task::yield_now().await;
2720 done_in_task.store(true, Ordering::SeqCst);
2721 let _ = tx.send(());
2722 }),
2723 "a future that yields is not completed inline"
2724 );
2725 assert_eq!(INLINE_EXECUTION_DEPTH.get(), 0);
2726
2727 rx.await.unwrap();
2729 assert!(done.load(Ordering::SeqCst));
2730 }
2731}