From d93e0580741f9ee0d8c959b3c996d7b7028929f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 07:44:26 -0700 Subject: [PATCH 1/7] invalidator flag --- turbopack/crates/turbo-tasks-macros/src/func.rs | 13 ++++++++++++- .../crates/turbo-tasks-macros/src/function_macro.rs | 2 ++ .../turbo-tasks-macros/src/value_impl_macro.rs | 4 ++++ .../turbo-tasks-macros/src/value_trait_macro.rs | 1 + turbopack/crates/turbo-tasks/src/native_function.rs | 4 ++++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/turbopack/crates/turbo-tasks-macros/src/func.rs b/turbopack/crates/turbo-tasks-macros/src/func.rs index e6e5edb68aca..5155391e24fe 100644 --- a/turbopack/crates/turbo-tasks-macros/src/func.rs +++ b/turbopack/crates/turbo-tasks-macros/src/func.rs @@ -733,6 +733,9 @@ pub struct FunctionArguments { /// task-local state. The function call itself will not be cached, but cells will be created on /// the parent task. pub local: Option, + /// If true, the function will be allowed to call `get_invalidator` . If this is false, the + /// `get_invalidator` function will panic on calls. + pub invalidator: Option, } impl Parse for FunctionArguments { @@ -760,11 +763,14 @@ impl Parse for FunctionArguments { ("local", Meta::Path(_)) => { parsed_args.local = Some(meta.span()); } + ("invalidator", Meta::Path(_)) => { + parsed_args.invalidator = Some(meta.span()); + } (_, meta) => { return Err(syn::Error::new_spanned( meta, "unexpected token, expected one of: \"fs\", \"network\", \"operation\", \ - \"local\"", + \"local\", \"invalidator\"", )); } } @@ -1092,6 +1098,7 @@ pub struct NativeFn { pub is_self_used: bool, pub filter_trait_call_args: Option, pub local: bool, + pub invalidator: bool, } impl NativeFn { @@ -1107,6 +1114,7 @@ impl NativeFn { is_self_used, filter_trait_call_args, local, + invalidator, } = self; if *is_method { @@ -1133,6 +1141,7 @@ impl NativeFn { #function_path_string.to_owned(), turbo_tasks::macro_helpers::FunctionMeta { local: #local, + invalidator: #invalidator, }, #arg_filter, #function_path, @@ -1147,6 +1156,7 @@ impl NativeFn { #function_path_string.to_owned(), turbo_tasks::macro_helpers::FunctionMeta { local: #local, + invalidator: #invalidator, }, #arg_filter, #function_path, @@ -1162,6 +1172,7 @@ impl NativeFn { #function_path_string.to_owned(), turbo_tasks::macro_helpers::FunctionMeta { local: #local, + invalidator: #invalidator, }, #function_path, ) diff --git a/turbopack/crates/turbo-tasks-macros/src/function_macro.rs b/turbopack/crates/turbo-tasks-macros/src/function_macro.rs index 8709dbecd261..7a12b69b2a81 100644 --- a/turbopack/crates/turbo-tasks-macros/src/function_macro.rs +++ b/turbopack/crates/turbo-tasks-macros/src/function_macro.rs @@ -42,6 +42,7 @@ pub fn function(args: TokenStream, input: TokenStream) -> TokenStream { .inspect_err(|err| errors.push(err.to_compile_error())) .unwrap_or_default(); let local = args.local.is_some(); + let invalidator = args.invalidator.is_some(); let is_self_used = args.operation.is_some() || is_self_used(&block); let Some(turbo_fn) = TurboFn::new(&sig, DefinitionContext::NakedFn, args) else { @@ -65,6 +66,7 @@ pub fn function(args: TokenStream, input: TokenStream) -> TokenStream { is_self_used, filter_trait_call_args: None, // not a trait method local, + invalidator, }; let native_function_ident = get_native_function_ident(ident); let native_function_ty = native_fn.ty(); diff --git a/turbopack/crates/turbo-tasks-macros/src/value_impl_macro.rs b/turbopack/crates/turbo-tasks-macros/src/value_impl_macro.rs index a4742ca42db2..a9292fc4ae70 100644 --- a/turbopack/crates/turbo-tasks-macros/src/value_impl_macro.rs +++ b/turbopack/crates/turbo-tasks-macros/src/value_impl_macro.rs @@ -84,6 +84,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { continue; }; let local = func_args.local.is_some(); + let invalidator = func_args.invalidator.is_some(); let is_self_used = func_args.operation.is_some() || is_self_used(block); let Some(turbo_fn) = @@ -106,6 +107,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { is_self_used, filter_trait_call_args: None, // not a trait method local, + invalidator, }; let native_function_ident = get_inherent_impl_function_ident(ty_ident, ident); @@ -191,6 +193,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { }; let local = func_args.local.is_some(); + let invalidator = func_args.invalidator.is_some(); let is_self_used = func_args.operation.is_some() || is_self_used(block); let Some(turbo_fn) = @@ -223,6 +226,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { is_self_used, filter_trait_call_args: turbo_fn.filter_trait_call_args(), local, + invalidator, }; let native_function_ident = diff --git a/turbopack/crates/turbo-tasks-macros/src/value_trait_macro.rs b/turbopack/crates/turbo-tasks-macros/src/value_trait_macro.rs index 589800b4ef1c..73ee61cb07d2 100644 --- a/turbopack/crates/turbo-tasks-macros/src/value_trait_macro.rs +++ b/turbopack/crates/turbo-tasks-macros/src/value_trait_macro.rs @@ -139,6 +139,7 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { // argument. (This could be fixed) // - This only makes sense when a default implementation is present. local: false, + invalidator: func_args.invalidator.is_some(), }; let native_function_ident = get_trait_default_impl_function_ident(trait_ident, ident); diff --git a/turbopack/crates/turbo-tasks/src/native_function.rs b/turbopack/crates/turbo-tasks/src/native_function.rs index e6c443a03a49..777433e06c25 100644 --- a/turbopack/crates/turbo-tasks/src/native_function.rs +++ b/turbopack/crates/turbo-tasks/src/native_function.rs @@ -144,6 +144,10 @@ pub struct FunctionMeta { /// task-local state. The function call itself will not be cached, but cells will be created on /// the parent task. pub local: bool, + + /// If true, the function will be allowed to call `get_invalidator` . If this is false, the + /// `get_invalidator` function will panic on calls. + pub invalidator: bool, } /// A native (rust) turbo-tasks function. It's used internally by From f3cb2755156c1773dfef4d3b6bf1da35d6a64e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 07:44:45 -0700 Subject: [PATCH 2/7] Debug assertion --- .../crates/turbo-tasks/src/invalidation.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/turbopack/crates/turbo-tasks/src/invalidation.rs b/turbopack/crates/turbo-tasks/src/invalidation.rs index f547f19d59a7..119abbb4948a 100644 --- a/turbopack/crates/turbo-tasks/src/invalidation.rs +++ b/turbopack/crates/turbo-tasks/src/invalidation.rs @@ -9,7 +9,7 @@ use std::{ use anyhow::Result; use indexmap::map::Entry; use serde::{Deserialize, Serialize, de::Visitor}; -use tokio::runtime::Handle; +use tokio::{runtime::Handle, task_local}; use crate::{ FxIndexMap, FxIndexSet, TaskId, TurboTasksApi, @@ -19,9 +19,27 @@ use crate::{ util::StaticOrArc, }; +#[cfg(debug_assertions)] +task_local! { + static ALLOW_INVALIDATOR: (); +} + +#[cfg(debug_assertions)] +pub fn allow_invalidator(f: impl Future) -> impl Future { + ALLOW_INVALIDATOR.scope((), f) +} + /// Get an [`Invalidator`] that can be used to invalidate the current task /// based on external events. pub fn get_invalidator() -> Invalidator { + #[cfg(debug_assertions)] + if ALLOW_INVALIDATOR.try_with(|_| {}).is_err() { + panic!( + "Invalidator can only be used in the turbo-tasks function that has \ + #[turbo_tasks::function(invalidator)] attribute" + ); + } + let handle = Handle::current(); Invalidator { task: current_task("turbo_tasks::get_invalidator()"), From aff1aed6a6a7686ff1872c63402ef53c09eb1709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 07:48:35 -0700 Subject: [PATCH 3/7] Use self.function_meta.invalidator --- .../crates/turbo-tasks-testing/tests/trait_ref_cell.rs | 2 +- turbopack/crates/turbo-tasks/src/native_function.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs b/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs index d06727332a80..882280ec2fa0 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/trait_ref_cell.rs @@ -85,7 +85,7 @@ trait CounterTrait { #[turbo_tasks::value_impl] impl CounterTrait for Counter { - #[turbo_tasks::function] + #[turbo_tasks::function(invalidator)] async fn get_value(&self) -> Result> { let mut lock = self.value.lock().unwrap(); lock.1 = Some(get_invalidator()); diff --git a/turbopack/crates/turbo-tasks/src/native_function.rs b/turbopack/crates/turbo-tasks/src/native_function.rs index 777433e06c25..256501800711 100644 --- a/turbopack/crates/turbo-tasks/src/native_function.rs +++ b/turbopack/crates/turbo-tasks/src/native_function.rs @@ -7,6 +7,7 @@ use tracing::Span; use crate::{ RawVc, TaskInput, TaskPersistence, + invalidation::allow_invalidator, magic_any::{MagicAny, MagicAnyDeserializeSeed, MagicAnySerializeSeed}, registry::register_function, task::{ @@ -239,7 +240,14 @@ impl NativeFunction { /// Executed the function pub fn execute(&'static self, this: Option, arg: &dyn MagicAny) -> NativeTaskFuture { match (self.implementation).functor(this, arg) { - Ok(functor) => functor, + Ok(functor) => { + #[cfg(debug_assertions)] + if self.function_meta.invalidator { + return Box::pin(allow_invalidator(functor)); + } + + functor + } Err(err) => Box::pin(async { Err(err) }), } } From 58d267d6d900bbff21d810336f470c0da36551a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 09:04:58 -0700 Subject: [PATCH 4/7] (invalidator) --- turbopack/crates/turbo-tasks-fs/src/lib.rs | 12 ++++++------ .../turbo-tasks-testing/tests/read_ref_cell.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/turbopack/crates/turbo-tasks-fs/src/lib.rs b/turbopack/crates/turbo-tasks-fs/src/lib.rs index f08a9e2e09fc..a40a8760c870 100644 --- a/turbopack/crates/turbo-tasks-fs/src/lib.rs +++ b/turbopack/crates/turbo-tasks-fs/src/lib.rs @@ -551,7 +551,7 @@ impl Debug for DiskFileSystem { #[turbo_tasks::value_impl] impl FileSystem for DiskFileSystem { - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn read(&self, fs_path: Vc) -> Result> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; @@ -577,7 +577,7 @@ impl FileSystem for DiskFileSystem { Ok(content.cell()) } - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn raw_read_dir(&self, fs_path: Vc) -> Result> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; @@ -632,7 +632,7 @@ impl FileSystem for DiskFileSystem { Ok(RawDirectoryContent::new(entries)) } - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn read_link(&self, fs_path: Vc) -> Result> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; @@ -718,7 +718,7 @@ impl FileSystem for DiskFileSystem { .cell()) } - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn write(&self, fs_path: Vc, content: Vc) -> Result<()> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; @@ -843,7 +843,7 @@ impl FileSystem for DiskFileSystem { Ok(()) } - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn write_link(&self, fs_path: Vc, target: Vc) -> Result<()> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; @@ -962,7 +962,7 @@ impl FileSystem for DiskFileSystem { Ok(()) } - #[turbo_tasks::function(fs)] + #[turbo_tasks::function(fs, invalidator)] async fn metadata(&self, fs_path: Vc) -> Result> { mark_session_dependent(); let full_path = self.to_sys_path(fs_path).await?; diff --git a/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs b/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs index 7949c493f526..921d9be73c4f 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/read_ref_cell.rs @@ -70,7 +70,7 @@ impl Counter { #[turbo_tasks::value_impl] impl Counter { - #[turbo_tasks::function] + #[turbo_tasks::function(invalidator)] async fn get_value(&self) -> Result> { let mut lock = self.value.lock().unwrap(); lock.1 = Some(get_invalidator()); From 24f97f8da1d06e95cd6c19e9ba8b7095b211c117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 09:16:50 -0700 Subject: [PATCH 5/7] qualify to fix build --- turbopack/crates/turbo-tasks/src/native_function.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/turbopack/crates/turbo-tasks/src/native_function.rs b/turbopack/crates/turbo-tasks/src/native_function.rs index 256501800711..15c0ebb89044 100644 --- a/turbopack/crates/turbo-tasks/src/native_function.rs +++ b/turbopack/crates/turbo-tasks/src/native_function.rs @@ -7,7 +7,6 @@ use tracing::Span; use crate::{ RawVc, TaskInput, TaskPersistence, - invalidation::allow_invalidator, magic_any::{MagicAny, MagicAnyDeserializeSeed, MagicAnySerializeSeed}, registry::register_function, task::{ @@ -243,7 +242,7 @@ impl NativeFunction { Ok(functor) => { #[cfg(debug_assertions)] if self.function_meta.invalidator { - return Box::pin(allow_invalidator(functor)); + return Box::pin(crate::invalidation::allow_invalidator(functor)); } functor From 2ab0c803b7cbff5404c1c6a69d64c7f958d59836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 12:50:11 -0700 Subject: [PATCH 6/7] `(invalidator)` --- crates/next-api/src/project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/next-api/src/project.rs b/crates/next-api/src/project.rs index 5630e075bb7c..7e418f2157d6 100644 --- a/crates/next-api/src/project.rs +++ b/crates/next-api/src/project.rs @@ -423,7 +423,7 @@ impl ProjectContainer { #[turbo_tasks::value_impl] impl ProjectContainer { - #[turbo_tasks::function] + #[turbo_tasks::function(invalidator)] pub async fn project(&self) -> Result> { let env_map: Vc; let next_config; From 5ddc0af60739a88ed6dc176ab877c77228792a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4=20=28Donny=29?= Date: Wed, 11 Jun 2025 15:02:14 -0700 Subject: [PATCH 7/7] Update test snapshots --- .../tests/function/fail_attribute_invalid_args.stderr | 2 +- .../function/fail_attribute_invalid_args_inherent_impl.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args.stderr b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args.stderr index 04266ef3404c..fc599d59ec73 100644 --- a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args.stderr +++ b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args.stderr @@ -1,4 +1,4 @@ -error: unexpected token, expected one of: "fs", "network", "operation", "local" +error: unexpected token, expected one of: "fs", "network", "operation", "local", "invalidator" --> tests/function/fail_attribute_invalid_args.rs:9:25 | 9 | #[turbo_tasks::function(invalid_argument)] diff --git a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args_inherent_impl.stderr b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args_inherent_impl.stderr index 1522fe8676a2..80a5d65669fa 100644 --- a/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args_inherent_impl.stderr +++ b/turbopack/crates/turbo-tasks-macros-tests/tests/function/fail_attribute_invalid_args_inherent_impl.stderr @@ -1,4 +1,4 @@ -error: unexpected token, expected one of: "fs", "network", "operation", "local" +error: unexpected token, expected one of: "fs", "network", "operation", "local", "invalidator" --> tests/function/fail_attribute_invalid_args_inherent_impl.rs:14:29 | 14 | #[turbo_tasks::function(invalid_argument)]