diff --git a/turbopack/crates/turbo-tasks-macros/src/func.rs b/turbopack/crates/turbo-tasks-macros/src/func.rs index 88350b088c337..e79a811173a95 100644 --- a/turbopack/crates/turbo-tasks-macros/src/func.rs +++ b/turbopack/crates/turbo-tasks-macros/src/func.rs @@ -352,12 +352,14 @@ impl TurboFn { parse_quote! { { #assertions + let turbo_tasks_transient = #( turbo_tasks::TaskInput::is_transient(&#inputs) ||)* false; <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( turbo_tasks::trait_call( *#trait_type_id_ident, std::borrow::Cow::Borrowed(stringify!(#ident)), #converted_this, Box::new((#(#inputs,)*)) as Box, + turbo_tasks_transient, ) ) } @@ -381,11 +383,13 @@ impl TurboFn { parse_quote! { { #assertions + let turbo_tasks_transient = #( turbo_tasks::TaskInput::is_transient(&#inputs) ||)* false; <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( turbo_tasks::dynamic_this_call( *#native_function_id_ident, #converted_this, Box::new((#(#inputs,)*)) as Box, + turbo_tasks_transient ) ) } @@ -394,10 +398,12 @@ impl TurboFn { parse_quote! { { #assertions + let turbo_tasks_transient = #( turbo_tasks::TaskInput::is_transient(&#inputs) ||)* false; <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( turbo_tasks::dynamic_call( *#native_function_id_ident, Box::new((#(#inputs,)*)) as Box, + turbo_tasks_transient, ) ) } diff --git a/turbopack/crates/turbo-tasks-memory/src/memory_backend.rs b/turbopack/crates/turbo-tasks-memory/src/memory_backend.rs index d9bb0d63f30af..5509fc3287123 100644 --- a/turbopack/crates/turbo-tasks-memory/src/memory_backend.rs +++ b/turbopack/crates/turbo-tasks-memory/src/memory_backend.rs @@ -21,13 +21,13 @@ use tracing::trace_span; use turbo_prehash::{BuildHasherExt, PassThroughHash, PreHashed}; use turbo_tasks::{ backend::{ - Backend, BackendJobId, CellContent, PersistentTaskType, TaskCollectiblesMap, - TaskExecutionSpec, TransientTaskType, TypedCellContent, + Backend, BackendJobId, CachedTaskType, CellContent, TaskCollectiblesMap, TaskExecutionSpec, + TransientTaskType, TypedCellContent, }, event::EventListener, util::{IdFactoryWithReuse, NoMoveVec}, CellId, FunctionId, RawVc, TaskId, TaskIdSet, TraitTypeId, TurboTasksBackendApi, Unused, - ValueTypeId, + ValueTypeId, TRANSIENT_TASK_BIT, }; use crate::{ @@ -41,16 +41,19 @@ use crate::{ task_statistics::TaskStatisticsApi, }; -fn prehash_task_type(task_type: PersistentTaskType) -> PreHashed { +fn prehash_task_type(task_type: CachedTaskType) -> PreHashed { BuildHasherDefault::::prehash(&Default::default(), task_type) } pub struct MemoryBackend { - memory_tasks: NoMoveVec, + persistent_tasks: NoMoveVec, + transient_tasks: NoMoveVec, backend_jobs: NoMoveVec, backend_job_id_factory: IdFactoryWithReuse, task_cache: - DashMap>, TaskId, BuildHasherDefault>, + DashMap>, TaskId, BuildHasherDefault>, + transient_task_cache: + DashMap>, TaskId, BuildHasherDefault>, memory_limit: AtomicUsize, gc_queue: Option, idle_gc_active: AtomicBool, @@ -65,14 +68,17 @@ impl Default for MemoryBackend { impl MemoryBackend { pub fn new(memory_limit: usize) -> Self { + let shard_amount = + (std::thread::available_parallelism().map_or(1, usize::from) * 32).next_power_of_two(); Self { - memory_tasks: NoMoveVec::new(), + persistent_tasks: NoMoveVec::new(), + transient_tasks: NoMoveVec::new(), backend_jobs: NoMoveVec::new(), - backend_job_id_factory: IdFactoryWithReuse::new(), - task_cache: DashMap::with_hasher_and_shard_amount( + backend_job_id_factory: IdFactoryWithReuse::new(1, u32::MAX as u64), + task_cache: DashMap::with_hasher_and_shard_amount(Default::default(), shard_amount), + transient_task_cache: DashMap::with_hasher_and_shard_amount( Default::default(), - (std::thread::available_parallelism().map_or(1, usize::from) * 32) - .next_power_of_two(), + shard_amount, ), memory_limit: AtomicUsize::new(memory_limit), gc_queue: (memory_limit != usize::MAX).then(GcQueue::new), @@ -123,16 +129,33 @@ impl MemoryBackend { for id in self.task_cache.clone().into_read_only().values() { func(*id); } + for id in self.transient_task_cache.clone().into_read_only().values() { + func(*id); + } } #[inline(always)] pub fn with_task(&self, id: TaskId, func: impl FnOnce(&Task) -> T) -> T { - func(self.memory_tasks.get(*id as usize).unwrap()) + let value = *id; + let index = (value & !TRANSIENT_TASK_BIT) as usize; + let item = if value & TRANSIENT_TASK_BIT == 0 { + self.persistent_tasks.get(index) + } else { + self.transient_tasks.get(index) + }; + func(item.unwrap()) } #[inline(always)] pub fn task(&self, id: TaskId) -> &Task { - self.memory_tasks.get(*id as usize).unwrap() + let value = *id; + let index = (value & !TRANSIENT_TASK_BIT) as usize; + let item = if value & TRANSIENT_TASK_BIT == 0 { + self.persistent_tasks.get(index) + } else { + self.transient_tasks.get(index) + }; + item.unwrap() } /// Runs the garbage collection until reaching the target memory. An `idle` @@ -222,18 +245,21 @@ impl MemoryBackend { false } - fn insert_and_connect_fresh_task( + fn insert_and_connect_fresh_task( &self, parent_task: TaskId, task_cache: &DashMap, + task_storage: &NoMoveVec, + task_storage_offset: u32, key: K, new_id: Unused, task: Task, turbo_tasks: &dyn TurboTasksBackendApi, ) -> TaskId { let new_id = new_id.into(); + let index = (*new_id - task_storage_offset) as usize; // Safety: We have a fresh task id that nobody knows about yet - unsafe { self.memory_tasks.insert(*new_id as usize, task) }; + unsafe { task_storage.insert(index, task) }; let result_task = match task_cache.entry(key) { Entry::Vacant(entry) => { // This is the most likely case @@ -245,9 +271,14 @@ impl MemoryBackend { let task_id = *entry.get(); drop(entry); unsafe { - self.memory_tasks.remove(*new_id as usize); - let new_id = Unused::new_unchecked(new_id); - turbo_tasks.reuse_task_id(new_id); + task_storage.remove(index); + if new_id.is_transient() { + let new_id = Unused::new_unchecked(new_id); + turbo_tasks.reuse_transient_task_id(new_id); + } else { + let new_id = Unused::new_unchecked(new_id); + turbo_tasks.reuse_persistent_task_id(new_id); + } } task_id } @@ -291,6 +322,74 @@ impl MemoryBackend { pub fn task_statistics(&self) -> &TaskStatisticsApi { &self.task_statistics } + + fn track_cache_hit( + &self, + task_type: &PreHashed, + turbo_tasks: &dyn TurboTasksBackendApi, + ) { + self.task_statistics().map(|stats| match &**task_type { + CachedTaskType::ResolveNative { + fn_type: function_id, + this: _, + arg: _, + } + | CachedTaskType::Native { + fn_type: function_id, + this: _, + arg: _, + } => { + stats.increment_cache_hit(*function_id); + } + CachedTaskType::ResolveTrait { + trait_type, + method_name: name, + this, + arg: _, + } => { + // HACK: Resolve the this argument (`self`) in order to attribute the cache hit + // to the concrete trait implementation, rather than the dynamic trait method. + // This ensures cache hits and misses are both attributed to the same thing. + // + // Because this task already resolved, in most cases `self` should either be + // resolved, or already in the process of being resolved. + // + // However, `self` could become unloaded due to cache eviction, and this might + // trigger an otherwise unnecessary re-evalutation. + // + // This is a potentially okay trade-off as long as we don't log statistics by + // default. The alternative would be to store function ids on completed + // ResolveTrait tasks. + let trait_type = *trait_type; + let name = name.clone(); + let this = *this; + let stats = Arc::clone(stats); + turbo_tasks.run_once(Box::pin(async move { + let function_id = + CachedTaskType::resolve_trait_method(trait_type, name, this).await?; + stats.increment_cache_hit(function_id); + Ok(()) + })); + } + }); + } + + fn track_cache_miss(&self, task_type: &PreHashed) { + self.task_statistics().map(|stats| match &**task_type { + CachedTaskType::Native { + fn_type: function_id, + this: _, + arg: _, + } => { + stats.increment_cache_miss(*function_id); + } + CachedTaskType::ResolveTrait { .. } | CachedTaskType::ResolveNative { .. } => { + // these types re-execute themselves as `Native` after + // resolving their arguments, skip counting their + // executions here to avoid double-counting + } + }); + } } impl Backend for MemoryBackend { @@ -592,7 +691,7 @@ impl Backend for MemoryBackend { fn get_or_create_persistent_task( &self, - task_type: PersistentTaskType, + task_type: CachedTaskType, parent_task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi, ) -> TaskId { @@ -601,74 +700,16 @@ impl Backend for MemoryBackend { self.lookup_and_connect_task(parent_task, &self.task_cache, &task_type, turbo_tasks) { // fast pass without creating a new task - self.task_statistics().map(|stats| match &*task_type { - PersistentTaskType::ResolveNative { - fn_type: function_id, - this: _, - arg: _, - } - | PersistentTaskType::Native { - fn_type: function_id, - this: _, - arg: _, - } => { - stats.increment_cache_hit(*function_id); - } - PersistentTaskType::ResolveTrait { - trait_type, - method_name: name, - this, - arg: _, - } => { - // HACK: Resolve the this argument (`self`) in order to attribute the cache hit - // to the concrete trait implementation, rather than the dynamic trait method. - // This ensures cache hits and misses are both attributed to the same thing. - // - // Because this task already resolved, in most cases `self` should either be - // resolved, or already in the process of being resolved. - // - // However, `self` could become unloaded due to cache eviction, and this might - // trigger an otherwise unnecessary re-evalutation. - // - // This is a potentially okay trade-off as long as we don't log statistics by - // default. The alternative would be to store function ids on completed - // ResolveTrait tasks. - let trait_type = *trait_type; - let name = name.clone(); - let this = *this; - let stats = Arc::clone(stats); - turbo_tasks.run_once(Box::pin(async move { - let function_id = - PersistentTaskType::resolve_trait_method(trait_type, name, this) - .await?; - stats.increment_cache_hit(function_id); - Ok(()) - })); - } - }); + self.track_cache_hit(&task_type, turbo_tasks); task } else { - self.task_statistics().map(|stats| match &*task_type { - PersistentTaskType::Native { - fn_type: function_id, - this: _, - arg: _, - } => { - stats.increment_cache_miss(*function_id); - } - PersistentTaskType::ResolveTrait { .. } - | PersistentTaskType::ResolveNative { .. } => { - // these types re-execute themselves as `Native` after - // resolving their arguments, skip counting their - // executions here to avoid double-counting - } - }); + self.track_cache_miss(&task_type); // It's important to avoid overallocating memory as this will go into the task // cache and stay there forever. We can to be as small as possible. let (task_type_hash, task_type) = PreHashed::into_parts(task_type); let task_type = Arc::new(PreHashed::new(task_type_hash, task_type)); // slow pass with key lock - let id = turbo_tasks.get_fresh_task_id(); + let id = turbo_tasks.get_fresh_persistent_task_id(); let task = Task::new_persistent( // Safety: That task will hold the value, but we are still in // control of the task @@ -678,6 +719,51 @@ impl Backend for MemoryBackend { self.insert_and_connect_fresh_task( parent_task, &self.task_cache, + &self.persistent_tasks, + 0, + task_type, + id, + task, + turbo_tasks, + ) + } + } + + fn get_or_create_transient_task( + &self, + task_type: CachedTaskType, + parent_task: TaskId, + turbo_tasks: &dyn TurboTasksBackendApi, + ) -> TaskId { + let task_type = prehash_task_type(task_type); + if let Some(task) = self.lookup_and_connect_task( + parent_task, + &self.transient_task_cache, + &task_type, + turbo_tasks, + ) { + // fast pass without creating a new task + self.track_cache_hit(&task_type, turbo_tasks); + task + } else { + self.track_cache_miss(&task_type); + // It's important to avoid overallocating memory as this will go into the task + // cache and stay there forever. We can to be as small as possible. + let (task_type_hash, task_type) = PreHashed::into_parts(task_type); + let task_type = Arc::new(PreHashed::new(task_type_hash, task_type)); + // slow pass with key lock + let id = turbo_tasks.get_fresh_transient_task_id(); + let task = Task::new_transient( + // Safety: That task will hold the value, but we are still in + // control of the task + *unsafe { id.get_unchecked() }, + task_type.clone(), + ); + self.insert_and_connect_fresh_task( + parent_task, + &self.transient_task_cache, + &self.transient_tasks, + TRANSIENT_TASK_BIT, task_type, id, task, @@ -715,19 +801,20 @@ impl Backend for MemoryBackend { task_type: TransientTaskType, turbo_tasks: &dyn TurboTasksBackendApi, ) -> TaskId { - let id = turbo_tasks.get_fresh_task_id(); + let id = turbo_tasks.get_fresh_transient_task_id(); let id = id.into(); + let index = (*id - TRANSIENT_TASK_BIT) as usize; match task_type { TransientTaskType::Root(f) => { let task = Task::new_root(id, move || f() as _); // SAFETY: We have a fresh task id where nobody knows about yet - unsafe { self.memory_tasks.insert(*id as usize, task) }; + unsafe { self.transient_tasks.insert(index, task) }; Task::set_root(id, self, turbo_tasks); } TransientTaskType::Once(f) => { let task = Task::new_once(id, f); // SAFETY: We have a fresh task id where nobody knows about yet - unsafe { self.memory_tasks.insert(*id as usize, task) }; + unsafe { self.transient_tasks.insert(index, task) }; Task::set_once(id, self, turbo_tasks); } }; diff --git a/turbopack/crates/turbo-tasks-memory/src/task.rs b/turbopack/crates/turbo-tasks-memory/src/task.rs index 83138f8f1105e..4b7cd70953455 100644 --- a/turbopack/crates/turbo-tasks-memory/src/task.rs +++ b/turbopack/crates/turbo-tasks-memory/src/task.rs @@ -21,7 +21,7 @@ use tokio::task_local; use tracing::Span; use turbo_prehash::PreHashed; use turbo_tasks::{ - backend::{CellContent, PersistentTaskType, TaskCollectiblesMap, TaskExecutionSpec}, + backend::{CachedTaskType, CellContent, TaskCollectiblesMap, TaskExecutionSpec}, event::{Event, EventListener}, get_invalidator, registry, CellId, Invalidator, RawVc, TaskId, TaskIdSet, TraitTypeId, TurboTasksBackendApi, ValueTypeId, @@ -71,16 +71,17 @@ pub enum TaskType { Once(Box), /// A normal persistent task - Persistent { - ty: Arc>, - }, + Persistent { ty: Arc> }, + + /// A cached transient task + Transient { ty: Arc> }, } #[derive(Clone)] enum TaskTypeForDescription { Root, Once, - Persistent(Arc>), + Persistent(Arc>), } impl TaskTypeForDescription { @@ -89,6 +90,7 @@ impl TaskTypeForDescription { TaskType::Root(..) => Self::Root, TaskType::Once(..) => Self::Once, TaskType::Persistent { ty, .. } => Self::Persistent(ty.clone()), + TaskType::Transient { ty, .. } => Self::Persistent(ty.clone()), } } } @@ -99,6 +101,7 @@ impl Debug for TaskType { Self::Root(..) => f.debug_tuple("Root").finish(), Self::Once(..) => f.debug_tuple("Once").finish(), Self::Persistent { ty, .. } => Debug::fmt(ty, f), + Self::Transient { ty } => Debug::fmt(ty, f), } } } @@ -109,6 +112,7 @@ impl Display for TaskType { Self::Root(..) => f.debug_tuple("Root").finish(), Self::Once(..) => f.debug_tuple("Once").finish(), Self::Persistent { ty, .. } => Display::fmt(ty, f), + Self::Transient { ty } => Display::fmt(ty, f), } } } @@ -460,10 +464,7 @@ pub enum ReadCellError { } impl Task { - pub(crate) fn new_persistent( - id: TaskId, - task_type: Arc>, - ) -> Self { + pub(crate) fn new_persistent(id: TaskId, task_type: Arc>) -> Self { let ty = TaskType::Persistent { ty: task_type }; Self { id, @@ -473,6 +474,16 @@ impl Task { } } + pub(crate) fn new_transient(id: TaskId, task_type: Arc>) -> Self { + let ty = TaskType::Transient { ty: task_type }; + Self { + id, + ty, + state: RwLock::new(TaskMetaState::Full(Box::new(TaskState::new()))), + graph_modification_in_progress_counter: AtomicU32::new(0), + } + } + pub(crate) fn new_root( id: TaskId, functor: impl Fn() -> NativeTaskFuture + Sync + Send + 'static, @@ -508,6 +519,7 @@ impl Task { pub(crate) fn is_pure(&self) -> bool { match &self.ty { TaskType::Persistent { .. } => true, + TaskType::Transient { .. } => true, TaskType::Root(_) => false, TaskType::Once(_) => false, } @@ -516,6 +528,7 @@ impl Task { pub(crate) fn is_once(&self) -> bool { match &self.ty { TaskType::Persistent { .. } => false, + TaskType::Transient { .. } => false, TaskType::Root(_) => false, TaskType::Once(_) => true, } @@ -579,7 +592,7 @@ impl Task { } pub(crate) fn get_function_name(&self) -> Option> { - if let TaskType::Persistent { ty, .. } = &self.ty { + if let TaskType::Persistent { ty, .. } | TaskType::Transient { ty, .. } = &self.ty { Some(ty.get_name()) } else { None @@ -595,14 +608,14 @@ impl Task { TaskTypeForDescription::Root => format!("[{}] root", id), TaskTypeForDescription::Once => format!("[{}] once", id), TaskTypeForDescription::Persistent(ty) => match &***ty { - PersistentTaskType::Native { + CachedTaskType::Native { fn_type: native_fn, this: _, arg: _, } => { format!("[{}] {}", id, registry::get_function(*native_fn).name) } - PersistentTaskType::ResolveNative { + CachedTaskType::ResolveNative { fn_type: native_fn, this: _, arg: _, @@ -613,7 +626,7 @@ impl Task { registry::get_function(*native_fn).name ) } - PersistentTaskType::ResolveTrait { + CachedTaskType::ResolveTrait { trait_type, method_name: fn_name, this: _, @@ -770,8 +783,8 @@ impl Task { mutex.lock().take().expect("Task can only be executed once"), tracing::trace_span!("turbo_tasks::once_task"), ), - TaskType::Persistent { ty, .. } => match &***ty { - PersistentTaskType::Native { + TaskType::Persistent { ty, .. } | TaskType::Transient { ty, .. } => match &***ty { + CachedTaskType::Native { fn_type: native_fn, this, arg, @@ -783,7 +796,7 @@ impl Task { drop(entered); (future, span) } - PersistentTaskType::ResolveNative { + CachedTaskType::ResolveNative { fn_type: ref native_fn_id, this, arg, @@ -793,16 +806,17 @@ impl Task { let span = func.resolve_span(); let entered = span.enter(); let turbo_tasks = turbo_tasks.pin(); - let future = Box::pin(PersistentTaskType::run_resolve_native( + let future = Box::pin(CachedTaskType::run_resolve_native( native_fn_id, *this, &**arg, + self.id.is_transient(), turbo_tasks, )); drop(entered); (future, span) } - PersistentTaskType::ResolveTrait { + CachedTaskType::ResolveTrait { trait_type: trait_type_id, method_name: name, this, @@ -814,11 +828,12 @@ impl Task { let entered = span.enter(); let name = name.clone(); let turbo_tasks = turbo_tasks.pin(); - let future = Box::pin(PersistentTaskType::run_resolve_trait( + let future = Box::pin(CachedTaskType::run_resolve_trait( trait_type_id, name, *this, &**arg, + self.id.is_transient(), turbo_tasks, )); drop(entered); diff --git a/turbopack/crates/turbo-tasks-memory/tests/task_statistics.rs b/turbopack/crates/turbo-tasks-memory/tests/task_statistics.rs index e6775812a1983..ac19c3b53a148 100644 --- a/turbopack/crates/turbo-tasks-memory/tests/task_statistics.rs +++ b/turbopack/crates/turbo-tasks-memory/tests/task_statistics.rs @@ -184,13 +184,13 @@ async fn test_no_execution() { .await; } -// Internally, this function uses `PersistentTaskType::Native`. +// Internally, this function uses `CachedTaskType::Native`. #[turbo_tasks::function] fn double(val: u64) -> Vc { Vc::cell(val * 2) } -// Internally, this function uses `PersistentTaskType::ResolveNative`. +// Internally, this function uses `CachedTaskType::ResolveNative`. #[turbo_tasks::function] async fn double_vc(val: Vc) -> Result> { let val = *val.await?; diff --git a/turbopack/crates/turbo-tasks-testing/src/lib.rs b/turbopack/crates/turbo-tasks-testing/src/lib.rs index f15697a4b4c08..62116f98444d4 100644 --- a/turbopack/crates/turbo-tasks-testing/src/lib.rs +++ b/turbopack/crates/turbo-tasks-testing/src/lib.rs @@ -88,7 +88,12 @@ impl VcStorage { } impl TurboTasksCallApi for VcStorage { - fn dynamic_call(&self, func: turbo_tasks::FunctionId, arg: Box) -> RawVc { + fn dynamic_call( + &self, + func: turbo_tasks::FunctionId, + arg: Box, + _is_transient: bool, + ) -> RawVc { self.dynamic_call(func, None, arg) } @@ -97,11 +102,17 @@ impl TurboTasksCallApi for VcStorage { func: turbo_tasks::FunctionId, this_arg: RawVc, arg: Box, + _is_transient: bool, ) -> RawVc { self.dynamic_call(func, Some(this_arg), arg) } - fn native_call(&self, _func: turbo_tasks::FunctionId, _arg: Box) -> RawVc { + fn native_call( + &self, + _func: turbo_tasks::FunctionId, + _arg: Box, + _is_transient: bool, + ) -> RawVc { unreachable!() } @@ -110,6 +121,7 @@ impl TurboTasksCallApi for VcStorage { _func: turbo_tasks::FunctionId, _this: RawVc, _arg: Box, + _is_transient: bool, ) -> RawVc { unreachable!() } @@ -120,6 +132,7 @@ impl TurboTasksCallApi for VcStorage { _trait_fn_name: Cow<'static, str>, _this: RawVc, _arg: Box, + _is_transient: bool, ) -> RawVc { unreachable!() } diff --git a/turbopack/crates/turbo-tasks/src/backend.rs b/turbopack/crates/turbo-tasks/src/backend.rs index d56e7eb8cba22..8dc9fa2f60188 100644 --- a/turbopack/crates/turbo-tasks/src/backend.rs +++ b/turbopack/crates/turbo-tasks/src/backend.rs @@ -27,16 +27,6 @@ use crate::{ ValueTypeId, VcRead, VcValueTrait, VcValueType, }; -pub enum TaskType { - /// Tasks that only exist for a certain operation and - /// won't persist between sessions - Transient(TransientTaskType), - - /// Tasks that can persist between sessions and potentially - /// shared globally - Persistent(PersistentTaskType), -} - type TransientTaskRoot = Box Pin> + Send>> + Send + Sync>; @@ -67,7 +57,7 @@ impl Debug for TransientTaskType { } #[derive(Debug, PartialEq, Eq, Hash)] -pub enum PersistentTaskType { +pub enum CachedTaskType { /// A normal task execution a native (rust) function Native { fn_type: FunctionId, @@ -95,7 +85,7 @@ pub enum PersistentTaskType { }, } -impl Display for PersistentTaskType { +impl Display for CachedTaskType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.get_name()) } @@ -144,7 +134,7 @@ mod ser { type Value = FunctionAndArg<'de>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid PersistentTaskType") + write!(formatter, "a valid CachedTaskType") } fn visit_seq(self, mut seq: A) -> std::result::Result @@ -167,13 +157,13 @@ mod ser { } } - impl Serialize for PersistentTaskType { + impl Serialize for CachedTaskType { fn serialize(&self, serializer: S) -> std::result::Result where S: ser::Serializer, { match self { - PersistentTaskType::Native { fn_type, this, arg } => { + CachedTaskType::Native { fn_type, this, arg } => { let mut s = serializer.serialize_seq(Some(3))?; s.serialize_element::(&0)?; s.serialize_element(&FunctionAndArg::Borrowed { @@ -183,7 +173,7 @@ mod ser { s.serialize_element(this)?; s.end() } - PersistentTaskType::ResolveNative { fn_type, this, arg } => { + CachedTaskType::ResolveNative { fn_type, this, arg } => { let mut s = serializer.serialize_seq(Some(3))?; s.serialize_element::(&1)?; s.serialize_element(&FunctionAndArg::Borrowed { @@ -193,7 +183,7 @@ mod ser { s.serialize_element(this)?; s.end() } - PersistentTaskType::ResolveTrait { + CachedTaskType::ResolveTrait { trait_type, method_name, this, @@ -218,14 +208,14 @@ mod ser { } } - impl<'de> Deserialize<'de> for PersistentTaskType { + impl<'de> Deserialize<'de> for CachedTaskType { fn deserialize>(deserializer: D) -> Result { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = PersistentTaskType; + type Value = CachedTaskType; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid PersistentTaskType") + write!(formatter, "a valid CachedTaskType") } fn visit_seq(self, mut seq: A) -> std::result::Result @@ -246,7 +236,7 @@ mod ser { let this = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; - Ok(PersistentTaskType::Native { fn_type, this, arg }) + Ok(CachedTaskType::Native { fn_type, this, arg }) } 1 => { let FunctionAndArg::Owned { fn_type, arg } = seq @@ -258,7 +248,7 @@ mod ser { let this = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; - Ok(PersistentTaskType::ResolveNative { fn_type, this, arg }) + Ok(CachedTaskType::ResolveNative { fn_type, this, arg }) } 2 => { let trait_type = seq @@ -278,7 +268,7 @@ mod ser { let arg = seq .next_element_seed(method.arg_deserializer)? .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?; - Ok(PersistentTaskType::ResolveTrait { + Ok(CachedTaskType::ResolveTrait { trait_type, method_name, this, @@ -294,7 +284,7 @@ mod ser { } } -impl PersistentTaskType { +impl CachedTaskType { /// Returns the name of the function in the code. Trait methods are /// formatted as `TraitName::method_name`. /// @@ -302,17 +292,17 @@ impl PersistentTaskType { /// it can return a `&'static str` in many cases. pub fn get_name(&self) -> Cow<'static, str> { match self { - PersistentTaskType::Native { + Self::Native { fn_type: native_fn, this: _, arg: _, } - | PersistentTaskType::ResolveNative { + | Self::ResolveNative { fn_type: native_fn, this: _, arg: _, } => Cow::Borrowed(®istry::get_function(*native_fn).name), - PersistentTaskType::ResolveTrait { + Self::ResolveTrait { trait_type: trait_id, method_name: fn_name, this: _, @@ -323,9 +313,8 @@ impl PersistentTaskType { pub fn try_get_function_id(&self) -> Option { match self { - PersistentTaskType::Native { fn_type, .. } - | PersistentTaskType::ResolveNative { fn_type, .. } => Some(*fn_type), - PersistentTaskType::ResolveTrait { .. } => None, + Self::Native { fn_type, .. } | Self::ResolveNative { fn_type, .. } => Some(*fn_type), + Self::ResolveTrait { .. } => None, } } } @@ -574,7 +563,14 @@ pub trait Backend: Sync + Send { fn get_or_create_persistent_task( &self, - task_type: PersistentTaskType, + task_type: CachedTaskType, + parent_task: TaskId, + turbo_tasks: &dyn TurboTasksBackendApi, + ) -> TaskId; + + fn get_or_create_transient_task( + &self, + task_type: CachedTaskType, parent_task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi, ) -> TaskId; @@ -607,11 +603,12 @@ pub trait Backend: Sync + Send { fn dispose_root_task(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi); } -impl PersistentTaskType { +impl CachedTaskType { pub async fn run_resolve_native( fn_id: FunctionId, mut this: Option, arg: &dyn MagicAny, + is_transient: bool, turbo_tasks: Arc>, ) -> Result { if let Some(this) = this.as_mut() { @@ -619,9 +616,9 @@ impl PersistentTaskType { } let arg = registry::get_function(fn_id).arg_meta.resolve(arg).await?; Ok(if let Some(this) = this { - turbo_tasks.this_call(fn_id, this, arg) + turbo_tasks.this_call(fn_id, this, arg, is_transient) } else { - turbo_tasks.native_call(fn_id, arg) + turbo_tasks.native_call(fn_id, arg, is_transient) }) } @@ -639,6 +636,7 @@ impl PersistentTaskType { name: Cow<'static, str>, this: RawVc, arg: &dyn MagicAny, + is_transient: bool, turbo_tasks: Arc>, ) -> Result { let this = this.resolve().await?; @@ -649,7 +647,7 @@ impl PersistentTaskType { .arg_meta .resolve(arg) .await?; - Ok(turbo_tasks.dynamic_this_call(native_fn, this, arg)) + Ok(turbo_tasks.dynamic_this_call(native_fn, this, arg, is_transient)) } /// Shared helper used by [`Self::resolve_trait_method`] and @@ -705,7 +703,7 @@ pub(crate) mod tests { fn test_get_name() { crate::register(); assert_eq!( - PersistentTaskType::Native { + CachedTaskType::Native { fn_type: *MOCK_FUNC_TASK_FUNCTION_ID, this: None, arg: Box::new(()), @@ -714,7 +712,7 @@ pub(crate) mod tests { "mock_func_task", ); assert_eq!( - PersistentTaskType::ResolveTrait { + CachedTaskType::ResolveTrait { trait_type: *MOCKTRAIT_TRAIT_TYPE_ID, method_name: "mock_method_task".into(), this: RawVc::TaskOutput(unsafe { TaskId::new_unchecked(1) }), diff --git a/turbopack/crates/turbo-tasks/src/id.rs b/turbopack/crates/turbo-tasks/src/id.rs index 4312e3ca184e8..d851e0114d861 100644 --- a/turbopack/crates/turbo-tasks/src/id.rs +++ b/turbopack/crates/turbo-tasks/src/id.rs @@ -78,6 +78,14 @@ impl Debug for TaskId { } } +pub const TRANSIENT_TASK_BIT: u32 = 0x8000_0000; + +impl TaskId { + pub fn is_transient(&self) -> bool { + **self & TRANSIENT_TASK_BIT != 0 + } +} + macro_rules! make_serializable { ($ty:ty, $get_global_name:path, $get_id:path, $visitor_name:ident) => { impl Serialize for $ty { diff --git a/turbopack/crates/turbo-tasks/src/id_factory.rs b/turbopack/crates/turbo-tasks/src/id_factory.rs index 1abce319654b2..00290b86e0050 100644 --- a/turbopack/crates/turbo-tasks/src/id_factory.rs +++ b/turbopack/crates/turbo-tasks/src/id_factory.rs @@ -12,24 +12,20 @@ use concurrent_queue::ConcurrentQueue; /// For ids that may be re-used, see [`IdFactoryWithReuse`]. pub struct IdFactory { next_id: AtomicU64, + max_id: u64, _phantom_data: PhantomData, } impl IdFactory { - pub const fn new() -> Self { + pub const fn new(start: u64, max: u64) -> Self { Self { - next_id: AtomicU64::new(1), + next_id: AtomicU64::new(start), + max_id: max, _phantom_data: PhantomData, } } } -impl Default for IdFactory { - fn default() -> Self { - Self::new() - } -} - impl IdFactory where T: TryFrom, @@ -38,10 +34,18 @@ where /// /// Panics (best-effort) if the id type overflows. pub fn get(&self) -> T { + let new_id = self.next_id.fetch_add(1, Ordering::Relaxed); + + if new_id > self.max_id { + panic!( + "Max id limit hit while attempting to generate a unique {}", + type_name::(), + ) + } + // Safety: u64 will not overflow. This is *very* unlikely to happen (would take // decades). - let new_id = - unsafe { NonZeroU64::new_unchecked(self.next_id.fetch_add(1, Ordering::Relaxed)) }; + let new_id = unsafe { NonZeroU64::new_unchecked(new_id) }; // Use the extra bits of the AtomicU64 as cheap overflow detection when the // value is less than 64 bits. @@ -63,20 +67,14 @@ pub struct IdFactoryWithReuse { } impl IdFactoryWithReuse { - pub const fn new() -> Self { + pub const fn new(start: u64, max: u64) -> Self { Self { - factory: IdFactory::new(), + factory: IdFactory::new(start, max), free_ids: ConcurrentQueue::unbounded(), } } } -impl Default for IdFactoryWithReuse { - fn default() -> Self { - Self::new() - } -} - impl IdFactoryWithReuse where T: TryFrom, @@ -93,7 +91,8 @@ where /// /// # Safety /// - /// It must be ensured that the id is no longer used + /// It must be ensured that the id is no longer used. Id must be a valid id + /// that was previously returned by `get`. pub unsafe fn reuse(&self, id: T) { let _ = self.free_ids.push(id); } @@ -108,7 +107,7 @@ mod tests { #[test] #[should_panic(expected = "Overflow detected")] fn test_overflow() { - let factory = IdFactory::::new(); + let factory = IdFactory::::new(1, u16::MAX as u64); assert_eq!(factory.get(), NonZeroU8::new(1).unwrap()); assert_eq!(factory.get(), NonZeroU8::new(2).unwrap()); for _i in 2..256 { diff --git a/turbopack/crates/turbo-tasks/src/lib.rs b/turbopack/crates/turbo-tasks/src/lib.rs index 7cb889f6d970e..b0f17aff7678e 100644 --- a/turbopack/crates/turbo-tasks/src/lib.rs +++ b/turbopack/crates/turbo-tasks/src/lib.rs @@ -81,7 +81,7 @@ use auto_hash_map::AutoSet; pub use collectibles::CollectiblesSource; pub use completion::{Completion, Completions}; pub use display::ValueToString; -pub use id::{ExecutionId, FunctionId, TaskId, TraitTypeId, ValueTypeId}; +pub use id::{ExecutionId, FunctionId, TaskId, TraitTypeId, ValueTypeId, TRANSIENT_TASK_BIT}; pub use invalidation::{ DynamicEqHash, InvalidationReason, InvalidationReasonKind, InvalidationReasonSet, }; @@ -90,8 +90,8 @@ pub use magic_any::MagicAny; pub use manager::{ dynamic_call, dynamic_this_call, emit, get_invalidator, mark_finished, mark_stateful, prevent_gc, run_once, run_once_with_reason, spawn_blocking, spawn_thread, trait_call, - turbo_tasks, CurrentCellRef, Invalidator, TaskIdProvider, TurboTasks, TurboTasksApi, - TurboTasksBackendApi, TurboTasksCallApi, Unused, UpdateInfo, + turbo_tasks, CurrentCellRef, Invalidator, TurboTasks, TurboTasksApi, TurboTasksBackendApi, + TurboTasksCallApi, Unused, UpdateInfo, }; pub use native_function::{FunctionMeta, NativeFunction}; pub use raw_vc::{CellId, RawVc, ReadRawVcFuture, ResolveTypeError}; diff --git a/turbopack/crates/turbo-tasks/src/manager.rs b/turbopack/crates/turbo-tasks/src/manager.rs index 16a10edc77ad9..99f0241a5dbd0 100644 --- a/turbopack/crates/turbo-tasks/src/manager.rs +++ b/turbopack/crates/turbo-tasks/src/manager.rs @@ -25,12 +25,12 @@ use turbo_tasks_malloc::TurboMalloc; use crate::{ backend::{ - Backend, CellContent, PersistentTaskType, TaskCollectiblesMap, TaskExecutionSpec, + Backend, CachedTaskType, CellContent, TaskCollectiblesMap, TaskExecutionSpec, TransientTaskType, TypedCellContent, }, capture_future::{self, CaptureFuture}, event::{Event, EventListener}, - id::{BackendJobId, ExecutionId, FunctionId, LocalCellId, TraitTypeId}, + id::{BackendJobId, ExecutionId, FunctionId, LocalCellId, TraitTypeId, TRANSIENT_TASK_BIT}, id_factory::{IdFactory, IdFactoryWithReuse}, magic_any::MagicAny, raw_vc::{CellId, RawVc}, @@ -45,16 +45,39 @@ use crate::{ }; pub trait TurboTasksCallApi: Sync + Send { - fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc; - fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc; - fn native_call(&self, func: FunctionId, arg: Box) -> RawVc; - fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc; + /// Calls a native function with arguments. Resolves arguments when needed + /// with a wrapper task. + fn dynamic_call(&self, func: FunctionId, arg: Box, is_transient: bool) -> RawVc; + /// Calls a native function with arguments. Resolves arguments when needed + /// with a wrapper task. + fn dynamic_this_call( + &self, + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, + ) -> RawVc; + /// Call a native function with arguments. + /// All inputs must be resolved. + fn native_call(&self, func: FunctionId, arg: Box, is_transient: bool) -> RawVc; + /// Call a native function with arguments. + /// All inputs must be resolved. + fn this_call( + &self, + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, + ) -> RawVc; + /// Calls a trait method with arguments. First input is the `self` object. + /// Uses a wrapper task to resolve fn trait_call( &self, trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, this: RawVc, arg: Box, + is_transient: bool, ) -> RawVc; fn run_once( @@ -140,22 +163,6 @@ pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send { ) -> Pin> + Send + 'static>>; } -pub trait TaskIdProvider { - fn get_fresh_task_id(&self) -> Unused; - fn reuse_task_id(&self, id: Unused); -} - -impl TaskIdProvider for IdFactoryWithReuse { - fn get_fresh_task_id(&self) -> Unused { - // Safety: This is a fresh id from the factory - unsafe { Unused::new_unchecked(self.get()) } - } - - fn reuse_task_id(&self, id: Unused) { - unsafe { self.reuse(id.into()) } - } -} - /// A wrapper around a value that is unused. pub struct Unused { inner: T, @@ -186,11 +193,20 @@ impl Unused { } } -pub trait TurboTasksBackendApi: - TaskIdProvider + TurboTasksCallApi + Sync + Send -{ +pub trait TurboTasksBackendApi: TurboTasksCallApi + Sync + Send { fn pin(&self) -> Arc>; + fn get_fresh_persistent_task_id(&self) -> Unused; + fn get_fresh_transient_task_id(&self) -> Unused; + /// # Safety + /// + /// The caller must ensure that the task id is not used anymore. + unsafe fn reuse_persistent_task_id(&self, id: Unused); + /// # Safety + /// + /// The caller must ensure that the task id is not used anymore. + unsafe fn reuse_transient_task_id(&self, id: Unused); + fn schedule(&self, task: TaskId); fn schedule_backend_background_job(&self, id: BackendJobId); fn schedule_backend_foreground_job(&self, id: BackendJobId); @@ -214,26 +230,6 @@ pub trait TurboTasksBackendApi: fn backend(&self) -> &B; } -impl TaskIdProvider for &dyn TurboTasksBackendApi { - fn get_fresh_task_id(&self) -> Unused { - (*self).get_fresh_task_id() - } - - fn reuse_task_id(&self, id: Unused) { - (*self).reuse_task_id(id) - } -} - -impl TaskIdProvider for &dyn TaskIdProvider { - fn get_fresh_task_id(&self) -> Unused { - (*self).get_fresh_task_id() - } - - fn reuse_task_id(&self, id: Unused) { - (*self).reuse_task_id(id) - } -} - #[allow(clippy::manual_non_exhaustive)] pub struct UpdateInfo { pub duration: Duration, @@ -247,6 +243,7 @@ pub struct TurboTasks { this: Weak, backend: B, task_id_factory: IdFactoryWithReuse, + transient_task_id_factory: IdFactoryWithReuse, execution_id_factory: IdFactory, stopped: AtomicBool, currently_scheduled_tasks: AtomicUsize, @@ -326,12 +323,15 @@ impl TurboTasks { // so we probably want to make sure that all tasks are joined // when trying to drop turbo tasks pub fn new(backend: B) -> Arc { - let task_id_factory = IdFactoryWithReuse::new(); + let task_id_factory = IdFactoryWithReuse::new(1, (TRANSIENT_TASK_BIT - 1) as u64); + let transient_task_id_factory = + IdFactoryWithReuse::new(TRANSIENT_TASK_BIT as u64, u32::MAX as u64); let this = Arc::new_cyclic(|this| Self { this: this.clone(), backend, task_id_factory, - execution_id_factory: IdFactory::new(), + transient_task_id_factory, + execution_id_factory: IdFactory::new(1, u64::MAX), stopped: AtomicBool::new(false), currently_scheduled_tasks: AtomicUsize::new(0), currently_scheduled_background_jobs: AtomicUsize::new(0), @@ -412,43 +412,81 @@ impl TurboTasks { Ok(rx.await?) } - /// Call a native function with arguments. - /// All inputs must be resolved. - pub(crate) fn native_call(&self, func: FunctionId, arg: Box) -> RawVc { - RawVc::TaskOutput(self.backend.get_or_create_persistent_task( - PersistentTaskType::Native { - fn_type: func, - this: None, - arg, - }, - current_task("turbo_function calls"), - self, - )) + pub(crate) fn native_call( + &self, + func: FunctionId, + arg: Box, + is_transient: bool, + ) -> RawVc { + let task_type = CachedTaskType::Native { + fn_type: func, + this: None, + arg, + }; + if is_transient { + RawVc::TaskOutput(self.backend.get_or_create_transient_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } else { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } } - /// Call a native function with arguments. - /// All inputs must be resolved. - pub(crate) fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { - RawVc::TaskOutput(self.backend.get_or_create_persistent_task( - PersistentTaskType::Native { - fn_type: func, - this: Some(this), - arg, - }, - current_task("turbo_function calls"), - self, - )) + pub(crate) fn this_call( + &self, + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, + ) -> RawVc { + let task_type = CachedTaskType::Native { + fn_type: func, + this: Some(this), + arg, + }; + if is_transient { + RawVc::TaskOutput(self.backend.get_or_create_transient_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } else { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } } - /// Calls a native function with arguments. Resolves arguments when needed - /// with a wrapper task. - pub fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc { + pub fn dynamic_call( + &self, + func: FunctionId, + arg: Box, + is_transient: bool, + ) -> RawVc { // TODO(bgw): Don't create a full turbo task if this is a function using local_cells if registry::get_function(func).arg_meta.is_resolved(&*arg) { - self.native_call(func, arg) + self.native_call(func, arg, is_transient) + } else if is_transient { + RawVc::TaskOutput(self.backend.get_or_create_transient_task( + CachedTaskType::ResolveNative { + fn_type: func, + this: None, + arg, + }, + current_task("turbo_function calls"), + self, + )) } else { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( - PersistentTaskType::ResolveNative { + CachedTaskType::ResolveNative { fn_type: func, this: None, arg, @@ -459,37 +497,44 @@ impl TurboTasks { } } - /// Calls a native function with arguments. Resolves arguments when needed - /// with a wrapper task. pub fn dynamic_this_call( &self, func: FunctionId, this: RawVc, arg: Box, + is_transient: bool, ) -> RawVc { if this.is_resolved() && registry::get_function(func).arg_meta.is_resolved(&*arg) { - self.this_call(func, this, arg) + self.this_call(func, this, arg, is_transient) } else { - RawVc::TaskOutput(self.backend.get_or_create_persistent_task( - PersistentTaskType::ResolveNative { - fn_type: func, - this: Some(this), - arg, - }, - current_task("turbo_function calls"), - self, - )) + let task_type = CachedTaskType::ResolveNative { + fn_type: func, + this: Some(this), + arg, + }; + if is_transient { + RawVc::TaskOutput(self.backend.get_or_create_transient_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } else { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } } } - /// Calls a trait method with arguments. First input is the `self` object. - /// Uses a wrapper task to resolve pub fn trait_call( &self, trait_type: TraitTypeId, mut trait_fn_name: Cow<'static, str>, this: RawVc, arg: Box, + is_transient: bool, ) -> RawVc { // avoid creating a wrapper task if self is already resolved // for resolved cells we already know the value type so we can lookup the @@ -497,7 +542,7 @@ impl TurboTasks { if let RawVc::TaskCell(_, CellId { type_id, .. }) = this { match get_trait_method(trait_type, type_id, trait_fn_name) { Ok(native_fn) => { - return self.dynamic_this_call(native_fn, this, arg); + return self.dynamic_this_call(native_fn, this, arg, is_transient); } Err(name) => { trait_fn_name = name; @@ -506,16 +551,25 @@ impl TurboTasks { } // create a wrapper task to resolve all inputs - RawVc::TaskOutput(self.backend.get_or_create_persistent_task( - PersistentTaskType::ResolveTrait { - trait_type, - method_name: trait_fn_name, - this, - arg, - }, - current_task("turbo_function calls"), - self, - )) + let task_type = CachedTaskType::ResolveTrait { + trait_type, + method_name: trait_fn_name, + this, + arg, + }; + if is_transient { + RawVc::TaskOutput(self.backend.get_or_create_transient_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } else { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + task_type, + current_task("turbo_function calls"), + self, + )) + } } #[track_caller] @@ -897,17 +951,29 @@ impl TurboTasks { } impl TurboTasksCallApi for TurboTasks { - fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc { - self.dynamic_call(func, arg) + fn dynamic_call(&self, func: FunctionId, arg: Box, is_transient: bool) -> RawVc { + self.dynamic_call(func, arg, is_transient) } - fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { - self.dynamic_this_call(func, this, arg) + fn dynamic_this_call( + &self, + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, + ) -> RawVc { + self.dynamic_this_call(func, this, arg, is_transient) } - fn native_call(&self, func: FunctionId, arg: Box) -> RawVc { - self.native_call(func, arg) + fn native_call(&self, func: FunctionId, arg: Box, is_transient: bool) -> RawVc { + self.native_call(func, arg, is_transient) } - fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { - self.this_call(func, this, arg) + fn this_call( + &self, + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, + ) -> RawVc { + self.this_call(func, this, arg, is_transient) } fn trait_call( &self, @@ -915,8 +981,9 @@ impl TurboTasksCallApi for TurboTasks { trait_fn_name: Cow<'static, str>, this: RawVc, arg: Box, + is_transient: bool, ) -> RawVc { - self.trait_call(trait_type, trait_fn_name, this, arg) + self.trait_call(trait_type, trait_fn_name, this, arg, is_transient) } #[track_caller] @@ -1137,6 +1204,7 @@ impl TurboTasksBackendApi for TurboTasks { fn backend(&self) -> &B { &self.backend } + #[track_caller] fn schedule_backend_background_job(&self, id: BackendJobId) { self.schedule_background_job(move |this| async move { @@ -1224,17 +1292,24 @@ impl TurboTasksBackendApi for TurboTasks { fn program_duration_until(&self, instant: Instant) -> Duration { instant - self.program_start } -} -impl TaskIdProvider for TurboTasks { - fn get_fresh_task_id(&self) -> Unused { - // Safety: This is a fresh id from the factory + fn get_fresh_persistent_task_id(&self) -> Unused { + // SAFETY: This is a fresh id from the factory unsafe { Unused::new_unchecked(self.task_id_factory.get()) } } - fn reuse_task_id(&self, id: Unused) { + fn get_fresh_transient_task_id(&self) -> Unused { + // SAFETY: This is a fresh id from the factory + unsafe { Unused::new_unchecked(self.transient_task_id_factory.get()) } + } + + unsafe fn reuse_persistent_task_id(&self, id: Unused) { unsafe { self.task_id_factory.reuse(id.into()) } } + + unsafe fn reuse_transient_task_id(&self, id: Unused) { + unsafe { self.transient_task_id_factory.reuse(id.into()) } + } } pub(crate) fn current_task(from: &str) -> TaskId { @@ -1400,14 +1475,19 @@ pub async fn run_once_with_reason( } /// Calls [`TurboTasks::dynamic_call`] for the current turbo tasks instance. -pub fn dynamic_call(func: FunctionId, arg: Box) -> RawVc { - with_turbo_tasks(|tt| tt.dynamic_call(func, arg)) +pub fn dynamic_call(func: FunctionId, arg: Box, is_transient: bool) -> RawVc { + with_turbo_tasks(|tt| tt.dynamic_call(func, arg, is_transient)) } /// Calls [`TurboTasks::dynamic_this_call`] for the current turbo tasks /// instance. -pub fn dynamic_this_call(func: FunctionId, this: RawVc, arg: Box) -> RawVc { - with_turbo_tasks(|tt| tt.dynamic_this_call(func, this, arg)) +pub fn dynamic_this_call( + func: FunctionId, + this: RawVc, + arg: Box, + is_transient: bool, +) -> RawVc { + with_turbo_tasks(|tt| tt.dynamic_this_call(func, this, arg, is_transient)) } /// Calls [`TurboTasks::trait_call`] for the current turbo tasks instance. @@ -1416,8 +1496,9 @@ pub fn trait_call( trait_fn_name: Cow<'static, str>, this: RawVc, arg: Box, + is_transient: bool, ) -> RawVc { - with_turbo_tasks(|tt| tt.trait_call(trait_type, trait_fn_name, this, arg)) + with_turbo_tasks(|tt| tt.trait_call(trait_type, trait_fn_name, this, arg, is_transient)) } pub fn turbo_tasks() -> Arc { diff --git a/turbopack/crates/turbo-tasks/src/persisted_graph.rs b/turbopack/crates/turbo-tasks/src/persisted_graph.rs index 14609f8b6071d..85944eae78901 100644 --- a/turbopack/crates/turbo-tasks/src/persisted_graph.rs +++ b/turbopack/crates/turbo-tasks/src/persisted_graph.rs @@ -2,7 +2,7 @@ use anyhow::Result; use serde::{ser::SerializeSeq, Deserialize, Serialize}; use crate::{ - backend::{CellContent, PersistentTaskType}, + backend::{CachedTaskType, CellContent}, task::shared_reference::TypedSharedReference, CellId, RawVc, TaskId, }; @@ -160,14 +160,14 @@ pub trait PersistedGraph: Sync + Send { /// returns false if that were too many fn lookup( &self, - partial_task_type: &PersistentTaskType, + partial_task_type: &CachedTaskType, api: &dyn PersistedGraphApi, ) -> Result; /// lookup one cache entry fn lookup_one( &self, - task_type: &PersistentTaskType, + task_type: &CachedTaskType, api: &dyn PersistedGraphApi, ) -> Result>; @@ -252,9 +252,9 @@ pub trait PersistedGraph: Sync + Send { } pub trait PersistedGraphApi { - fn get_or_create_task_type(&self, ty: PersistentTaskType) -> TaskId; + fn get_or_create_task_type(&self, ty: CachedTaskType) -> TaskId; - fn lookup_task_type(&self, id: TaskId) -> &PersistentTaskType; + fn lookup_task_type(&self, id: TaskId) -> &CachedTaskType; } /* @@ -262,8 +262,8 @@ pub trait PersistedGraphApi { read: data: (TaskId) => (TaskData) - cache: (PersistentTaskType) => (TaskId) - type: (TaskId) => (PersistentTaskType) + cache: (CachedTaskType) => (TaskId) + type: (TaskId) => (CachedTaskType) read_dependents: @@ -293,7 +293,7 @@ impl PersistedGraph for () { fn lookup( &self, - _partial_task_type: &PersistentTaskType, + _partial_task_type: &CachedTaskType, _api: &dyn PersistedGraphApi, ) -> Result { Ok(false) @@ -301,7 +301,7 @@ impl PersistedGraph for () { fn lookup_one( &self, - _task_type: &PersistentTaskType, + _task_type: &CachedTaskType, _api: &dyn PersistedGraphApi, ) -> Result> { Ok(None) diff --git a/turbopack/crates/turbo-tasks/src/registry.rs b/turbopack/crates/turbo-tasks/src/registry.rs index e4e0581fe6e80..28519b052c54e 100644 --- a/turbopack/crates/turbo-tasks/src/registry.rs +++ b/turbopack/crates/turbo-tasks/src/registry.rs @@ -10,20 +10,20 @@ use crate::{ NativeFunction, TraitType, ValueType, }; -static FUNCTION_ID_FACTORY: IdFactory = IdFactory::new(); +static FUNCTION_ID_FACTORY: IdFactory = IdFactory::new(1, u32::MAX as u64); static FUNCTIONS_BY_NAME: Lazy> = Lazy::new(DashMap::new); static FUNCTIONS_BY_VALUE: Lazy> = Lazy::new(DashMap::new); static FUNCTIONS: Lazy> = Lazy::new(NoMoveVec::new); -static VALUE_TYPE_ID_FACTORY: IdFactory = IdFactory::new(); +static VALUE_TYPE_ID_FACTORY: IdFactory = IdFactory::new(1, u32::MAX as u64); static VALUE_TYPES_BY_NAME: Lazy> = Lazy::new(DashMap::new); static VALUE_TYPES_BY_VALUE: Lazy> = Lazy::new(DashMap::new); static VALUE_TYPES: Lazy> = Lazy::new(NoMoveVec::new); -static TRAIT_TYPE_ID_FACTORY: IdFactory = IdFactory::new(); +static TRAIT_TYPE_ID_FACTORY: IdFactory = IdFactory::new(1, u32::MAX as u64); static TRAIT_TYPES_BY_NAME: Lazy> = Lazy::new(DashMap::new); static TRAIT_TYPES_BY_VALUE: Lazy> = Lazy::new(DashMap::new); diff --git a/turbopack/crates/turbo-tasks/src/task/task_input.rs b/turbopack/crates/turbo-tasks/src/task/task_input.rs index b9f9ebb9d8b9c..17f5a9ba4b58c 100644 --- a/turbopack/crates/turbo-tasks/src/task/task_input.rs +++ b/turbopack/crates/turbo-tasks/src/task/task_input.rs @@ -107,7 +107,7 @@ where } fn is_transient(&self) -> bool { - false + self.node.get_task_id().is_transient() } async fn resolve(&self) -> Result { @@ -124,7 +124,7 @@ where } fn is_transient(&self) -> bool { - false + self.node.node.get_task_id().is_transient() } } @@ -189,7 +189,7 @@ where impl TaskInput for TransientInstance where - T: Sync + Send, + T: Sync + Send + 'static, { fn is_transient(&self) -> bool { true