Repository: vercel/next.js
Pull request: [turbopack] Reorganize the fields in LocalTaskType to avoid an allocation

### What?
Create a new `struct` `LocalTaskSpec` that holds a `LocalTaskType` as an enum.  This allows us to share the common fields and push the formatting routines down to the LocalTaskType.

### Why?
This allows us to avoid an `Arc::new` call when launching tasks since the `LocalTaskType` enum now becomes copyable.



Closes PACK-4868

Changed files:
filename: turbopack/crates/turbo-tasks/src/manager.rs
patch: @@ -39,7 +39,7 @@ use crate::{
     raw_vc::{CellId, RawVc},
     registry,
     serialization_invalidation::SerializationInvalidator,
-    task::local_task::{LocalTask, LocalTaskType},
+    task::local_task::{LocalTask, LocalTaskSpec, LocalTaskType},
     task_statistics::TaskStatisticsApi,
     trace::TraceRawVcs,
     util::{IdFactory, StaticOrArc},
@@ -595,8 +595,8 @@ impl<B: Backend + 'static> TurboTasks<B> {
     ) -> RawVc {
         match persistence {
             TaskPersistence::Local => {
-                let task_type = LocalTaskType::Native {
-                    native_fn,
+                let task_type = LocalTaskSpec {
+                    task_type: LocalTaskType::Native { native_fn },
                     this,
                     arg,
                 };
@@ -645,8 +645,8 @@ impl<B: Backend + 'static> TurboTasks<B> {
         if this.is_none_or(|this| this.is_resolved()) && native_fn.arg_meta.is_resolved(&*arg) {
             return self.native_call(native_fn, this, arg, persistence);
         }
-        let task_type = LocalTaskType::ResolveNative {
-            native_fn,
+        let task_type = LocalTaskSpec {
+            task_type: LocalTaskType::ResolveNative { native_fn },
             this,
             arg,
         };
@@ -678,9 +678,9 @@ impl<B: Backend + 'static> TurboTasks<B> {
         }
 
         // create a wrapper task to resolve all inputs
-        let task_type = LocalTaskType::ResolveTrait {
-            trait_method,
-            this,
+        let task_type = LocalTaskSpec {
+            task_type: LocalTaskType::ResolveTrait { trait_method },
+            this: Some(this),
             arg,
         };
 
@@ -774,7 +774,7 @@ impl<B: Backend + 'static> TurboTasks<B> {
 
     fn schedule_local_task(
         &self,
-        ty: LocalTaskType,
+        ty: LocalTaskSpec,
         // if this is a `LocalTaskType::Resolve*`, we may spawn another task with this persistence,
         // if this is a `LocalTaskType::Native`, persistence is unused.
         //
@@ -784,14 +784,13 @@ impl<B: Backend + 'static> TurboTasks<B> {
         // don't end up owning any cells.
         persistence: TaskPersistence,
     ) -> RawVc {
-        let ty = Arc::new(ty);
+        let task_type = ty.task_type;
         let (global_task_state, parent_task_id, execution_id, local_task_id) = CURRENT_TASK_STATE
             .with(|gts| {
                 let mut gts_write = gts.write().unwrap();
                 let local_task_id = gts_write.create_local_task(LocalTask::Scheduled {
                     done_event: Event::new({
-                        let ty = Arc::clone(&ty);
-                        move || format!("LocalTask({ty})::done_event")
+                        move || format!("LocalTask({task_type})::done_event")
                     }),
                 });
                 (
@@ -815,7 +814,6 @@ impl<B: Backend + 'static> TurboTasks<B> {
         let future = async move {
             let TaskExecutionSpec { future, span } =
                 crate::task::local_task::get_local_task_execution_spec(&*this, &ty, persistence);
-            let ty = ty.clone();
             async move {
                 let (result, _duration, _memory_usage) = CaptureFuture::new(future).await;
 
@@ -828,7 +826,7 @@ impl<B: Backend + 'static> TurboTasks<B> {
                 let local_task = LocalTask::Done {
                     output: match result {
                         Ok(raw_vc) => OutputContent::Link(raw_vc),
-                        Err(err) => OutputContent::Error(err.task_context(ty)),
+                        Err(err) => OutputContent::Error(err.task_context(task_type)),
                     },
                 };
 

filename: turbopack/crates/turbo-tasks/src/task/local_task.rs
patch: @@ -1,4 +1,4 @@
-use std::{borrow::Cow, fmt, sync::Arc};
+use std::{fmt, sync::Arc};
 
 use anyhow::{Result, anyhow};
 
@@ -19,51 +19,39 @@ pub enum LocalTask {
 
 pub fn get_local_task_execution_spec<'a>(
     turbo_tasks: &'_ dyn TurboTasksBackendApi<impl Backend + 'static>,
-    ty: &'a LocalTaskType,
+    ty: &'a LocalTaskSpec,
     // if this is a `LocalTaskType::Resolve*`, we'll spawn another task with this persistence, if
     // this is a `LocalTaskType::Native`, this refers to the parent non-local task.
     persistence: TaskPersistence,
 ) -> TaskExecutionSpec<'a> {
-    match ty {
-        LocalTaskType::Native {
-            native_fn,
-            this,
-            arg,
-        } => {
+    match ty.task_type {
+        LocalTaskType::Native { native_fn } => {
             let span = native_fn.span(TaskPersistence::Local);
             let entered = span.enter();
-            let future = native_fn.execute(*this, &**arg);
+            let future = native_fn.execute(ty.this, &*ty.arg);
             drop(entered);
             TaskExecutionSpec { future, span }
         }
-        LocalTaskType::ResolveNative {
-            native_fn,
-            this,
-            arg,
-        } => {
+        LocalTaskType::ResolveNative { native_fn } => {
             let span = native_fn.resolve_span(TaskPersistence::Local);
             let entered = span.enter();
             let future = Box::pin(LocalTaskType::run_resolve_native(
                 native_fn,
-                *this,
-                &**arg,
+                ty.this,
+                &*ty.arg,
                 persistence,
                 turbo_tasks.pin(),
             ));
             drop(entered);
             TaskExecutionSpec { future, span }
         }
-        LocalTaskType::ResolveTrait {
-            trait_method,
-            this,
-            arg,
-        } => {
+        LocalTaskType::ResolveTrait { trait_method } => {
             let span = trait_method.resolve_span();
             let entered = span.enter();
             let future = Box::pin(LocalTaskType::run_resolve_trait(
                 trait_method,
-                *this,
-                &**arg,
+                ty.this.unwrap(),
+                &*ty.arg,
                 persistence,
                 turbo_tasks.pin(),
             ));
@@ -73,50 +61,35 @@ pub fn get_local_task_execution_spec<'a>(
     }
 }
 
+pub struct LocalTaskSpec {
+    /// The self value, will always be present for `ResolveTrait` tasks and is optional otherwise
+    pub(crate) this: Option<RawVc>,
+    /// Function arguments
+    pub(crate) arg: Box<dyn MagicAny>,
+    pub(crate) task_type: LocalTaskType,
+}
+
+#[derive(Copy, Clone)]
 pub enum LocalTaskType {
     /// A normal task execution a native (rust) function
-    Native {
-        native_fn: &'static NativeFunction,
-        this: Option<RawVc>,
-        arg: Box<dyn MagicAny>,
-    },
+    Native { native_fn: &'static NativeFunction },
 
     /// A resolve task, which resolves arguments and calls the function with resolve arguments. The
     /// inner function call will be a `PersistentTaskType` or `LocalTaskType::Native`.
-    ResolveNative {
-        native_fn: &'static NativeFunction,
-        this: Option<RawVc>,
-        arg: Box<dyn MagicAny>,
-    },
+    ResolveNative { native_fn: &'static NativeFunction },
 
     /// A trait method resolve task. It resolves the first (`self`) argument and looks up the trait
     /// method on that value. Then it calls that method. The method call will do a cache lookup and
     /// might resolve arguments before.
-    ResolveTrait {
-        trait_method: &'static TraitMethod,
-        this: RawVc,
-        arg: Box<dyn MagicAny>,
-    },
+    ResolveTrait { trait_method: &'static TraitMethod },
 }
 
 impl fmt::Display for LocalTaskType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            LocalTaskType::Native {
-                native_fn,
-                this: _,
-                arg: _,
-            } => f.write_str(native_fn.name),
-            LocalTaskType::ResolveNative {
-                native_fn,
-                this: _,
-                arg: _,
-            } => write!(f, "*{}", native_fn.name),
-            LocalTaskType::ResolveTrait {
-                trait_method,
-                this: _,
-                arg: _,
-            } => write!(
+            LocalTaskType::Native { native_fn } => f.write_str(native_fn.name),
+            LocalTaskType::ResolveNative { native_fn } => write!(f, "*{}", native_fn.name),
+            LocalTaskType::ResolveTrait { trait_method } => write!(
                 f,
                 "*{}::{}",
                 trait_method.trait_name, trait_method.method_name
@@ -126,31 +99,6 @@ impl fmt::Display for LocalTaskType {
 }
 
 impl LocalTaskType {
-    /// Returns the name of the function in the code. Trait methods are
-    /// formatted as `TraitName::method_name`.
-    ///
-    /// Equivalent to [`ToString::to_string`], but potentially more efficient as
-    /// it can return a `&'static str` in many cases.
-    pub fn get_name(&self) -> Cow<'static, str> {
-        match self {
-            Self::Native {
-                native_fn,
-                this: _,
-                arg: _,
-            } => Cow::Borrowed(native_fn.name),
-            Self::ResolveNative {
-                native_fn,
-                this: _,
-                arg: _,
-            } => format!("*{}", native_fn.name).into(),
-            Self::ResolveTrait {
-                trait_method,
-                this: _,
-                arg: _,
-            } => format!("*{}::{}", trait_method.trait_name, trait_method.method_name).into(),
-        }
-    }
-
     /// Implementation of the LocalTaskType::ResolveNative task.
     /// Resolves all the task inputs and then calls the given function.
     async fn run_resolve_native<B: Backend + 'static>(
@@ -200,7 +148,7 @@ impl LocalTaskType {
 #[cfg(test)]
 pub(crate) mod tests {
     use super::*;
-    use crate::{self as turbo_tasks, TaskId, Vc};
+    use crate::{self as turbo_tasks, Vc};
 
     #[turbo_tasks::function]
     fn mock_func_task() -> Vc<()> {
@@ -214,24 +162,20 @@ pub(crate) mod tests {
     }
 
     #[test]
-    fn test_get_name() {
+    fn test_fmt() {
         crate::register();
         assert_eq!(
             LocalTaskType::Native {
                 native_fn: &MOCK_FUNC_TASK_FUNCTION,
-                this: None,
-                arg: Box::new(()),
             }
-            .get_name(),
+            .to_string(),
             "mock_func_task",
         );
         assert_eq!(
             LocalTaskType::ResolveTrait {
                 trait_method: MOCKTRAIT_TRAIT_TYPE.get("mock_method_task"),
-                this: RawVc::TaskOutput(unsafe { TaskId::new_unchecked(1) }),
-                arg: Box::new(()),
             }
-            .get_name(),
+            .to_string(),
             "*MockTrait::mock_method_task",
         );
     }