diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs index d9dc5cea2894a..83826ceac9d43 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs @@ -61,7 +61,7 @@ use crate::{ backing_storage::BackingStorage, data::{ ActivenessState, AggregationNumber, CachedDataItem, CachedDataItemKey, CachedDataItemType, - CachedDataItemValueRef, CellRef, CollectibleRef, CollectiblesRef, DirtyState, + CachedDataItemValueRef, CellRef, CollectibleRef, CollectiblesRef, Dirtyness, InProgressCellState, InProgressState, InProgressStateInner, OutputValue, RootType, }, utils::{ @@ -557,8 +557,7 @@ impl TurboTasksBackendInner { } } - let is_dirty = - get!(task, Dirty).map_or(false, |dirty_state| dirty_state.get(self.session_id)); + let is_dirty = task.is_dirty(self.session_id); // Check the dirty count of the root node let dirty_tasks = get!(task, AggregatedDirtyContainerCount) @@ -614,8 +613,7 @@ impl TurboTasksBackendInner { visited: &mut FxHashSet, ) -> String { let task = ctx.task(task_id, TaskDataCategory::Data); - let is_dirty = get!(task, Dirty) - .map_or(false, |dirty_state| dirty_state.get(ctx.session_id())); + let is_dirty = task.is_dirty(ctx.session_id()); let in_progress = get!(task, InProgress).map_or("not in progress", |p| match p { InProgressState::InProgress(_) => "in progress", @@ -2356,57 +2354,60 @@ impl TurboTasksBackendInner { )); // Update the dirty state - let old_dirty_state = get!(task, Dirty).copied(); + let old_dirtyness = task.dirtyness_and_session(); - let new_dirty_state = if session_dependent { - Some(DirtyState { - clean_in_session: Some(self.session_id), - }) + let new_dirtyness = if session_dependent { + Some((Dirtyness::SessionDependent, Some(self.session_id))) } else { None }; - let dirty_changed = old_dirty_state != new_dirty_state; + let dirty_changed = old_dirtyness != new_dirtyness; let data_update = if dirty_changed { - if let Some(new_dirty_state) = new_dirty_state { - task.insert(CachedDataItem::Dirty { - value: new_dirty_state, - }); - } else { + if let Some((value, _)) = new_dirtyness { + task.insert(CachedDataItem::Dirty { value }); + } else if old_dirtyness.is_some() { task.remove(&CachedDataItemKey::Dirty {}); } + if let Some(session_id) = new_dirtyness.and_then(|t| t.1) { + task.insert(CachedDataItem::CleanInSession { value: session_id }); + } else if old_dirtyness.is_some_and(|t| t.1.is_some()) { + task.remove(&CachedDataItemKey::CleanInSession {}); + } - if old_dirty_state.is_some() || new_dirty_state.is_some() { - let mut dirty_containers = get!(task, AggregatedDirtyContainerCount) - .cloned() - .unwrap_or_default(); - if let Some(old_dirty_state) = old_dirty_state { - dirty_containers.update_with_dirty_state(&old_dirty_state); + let mut dirty_containers = get!(task, AggregatedDirtyContainerCount) + .cloned() + .unwrap_or_default(); + if let Some((old_dirtyness, old_clean_in_session)) = old_dirtyness { + dirty_containers + .update_with_dirtyness_and_session(old_dirtyness, old_clean_in_session); + } + let aggregated_update = match (old_dirtyness, new_dirtyness) { + (None, None) => unreachable!(), + (Some(old), None) => { + dirty_containers.undo_update_with_dirtyness_and_session(old.0, old.1) } - let aggregated_update = match (old_dirty_state, new_dirty_state) { - (None, None) => unreachable!(), - (Some(old), None) => dirty_containers.undo_update_with_dirty_state(&old), - (None, Some(new)) => dirty_containers.update_with_dirty_state(&new), - (Some(old), Some(new)) => dirty_containers.replace_dirty_state(&old, &new), - }; - if !aggregated_update.is_zero() { - if aggregated_update.get(self.session_id) < 0 - && let Some(activeness_state) = get_mut!(task, Activeness) - { - activeness_state.all_clean_event.notify(usize::MAX); - activeness_state.unset_active_until_clean(); - if activeness_state.is_empty() { - task.remove(&CachedDataItemKey::Activeness {}); - } + (None, Some(new)) => { + dirty_containers.update_with_dirtyness_and_session(new.0, new.1) + } + (Some(old), Some(new)) => { + dirty_containers.replace_dirtyness_and_session(old.0, old.1, new.0, new.1) + } + }; + if !aggregated_update.is_zero() { + if aggregated_update.get(self.session_id) < 0 + && let Some(activeness_state) = get_mut!(task, Activeness) + { + activeness_state.all_clean_event.notify(usize::MAX); + activeness_state.unset_active_until_clean(); + if activeness_state.is_empty() { + task.remove(&CachedDataItemKey::Activeness {}); } - AggregationUpdateJob::data_update( - &mut task, - AggregatedDataUpdate::new() - .dirty_container_update(task_id, aggregated_update), - ) - } else { - None } + AggregationUpdateJob::data_update( + &mut task, + AggregatedDataUpdate::new().dirty_container_update(task_id, aggregated_update), + ) } else { None } @@ -2875,7 +2876,7 @@ impl TurboTasksBackendInner { let mut ctx = self.execute_context(turbo_tasks); let mut task = ctx.task(task_id, TaskDataCategory::All); - let is_dirty = get!(task, Dirty).map_or(false, |dirty| dirty.get(self.session_id)); + let is_dirty = task.is_dirty(self.session_id); let has_dirty_containers = get!(task, AggregatedDirtyContainerCount) .map_or(false, |dirty_containers| { dirty_containers.get(self.session_id) > 0 @@ -2968,7 +2969,7 @@ impl TurboTasksBackendInner { } } - let is_dirty = get!(task, Dirty).is_some_and(|dirty| dirty.get(self.session_id)); + let is_dirty = task.is_dirty(self.session_id); let has_dirty_container = get!(task, AggregatedDirtyContainerCount) .is_some_and(|count| count.get(self.session_id) > 0); let should_be_in_upper = is_dirty || has_dirty_container; diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs index d287b0d40d123..c7fc49b3c100e 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs @@ -249,8 +249,8 @@ impl AggregatedDataUpdate { collectibles_update.push((collectible, 1)); } } - if let Some(dirty) = get!(task, Dirty) { - dirty_container_count.update_with_dirty_state(dirty); + if let Some((dirtyness, clean_in_session)) = task.dirtyness_and_session() { + dirty_container_count.update_with_dirtyness_and_session(dirtyness, clean_in_session); } let mut result = Self::new().collectibles_update(collectibles_update); @@ -323,18 +323,18 @@ impl AggregatedDataUpdate { ); if !aggregated_update.is_zero() { - let dirty_state = get!(task, Dirty).copied(); + let dirtyness_and_session = task.dirtyness_and_session(); let task_id = task.id(); update!(task, AggregatedDirtyContainerCount, |old: Option< DirtyContainerCount, >| { let mut new = old.unwrap_or_default(); - if let Some(dirty_state) = dirty_state { - new.update_with_dirty_state(&dirty_state); + if let Some((dirtyness, clean_in_session)) = dirtyness_and_session { + new.update_with_dirtyness_and_session(dirtyness, clean_in_session); } let aggregated_update = new.update_count(&aggregated_update); - if let Some(dirty_state) = dirty_state { - new.undo_update_with_dirty_state(&dirty_state); + if let Some((dirtyness, clean_in_session)) = dirtyness_and_session { + new.undo_update_with_dirtyness_and_session(dirtyness, clean_in_session); } if !aggregated_update.is_zero() { result.dirty_container_update = Some((task_id, aggregated_update)); @@ -1209,7 +1209,7 @@ impl AggregationUpdateQueue { ) { let session_id = ctx.session_id(); // Task need to be scheduled if it's dirty or doesn't have output - let dirty = get!(task, Dirty).map_or(false, |d| d.get(session_id)); + let dirty = task.is_dirty(session_id); let should_schedule = if dirty { Some(TaskExecutionReason::ActivateDirty) } else if !task.has_key(&CachedDataItemKey::Output {}) { diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs index 56f955dd43569..1e7961e9c44a5 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs @@ -11,10 +11,10 @@ use crate::{ AggregatedDataUpdate, AggregationUpdateJob, AggregationUpdateQueue, }, }, - storage::{get, get_mut}, + storage::{get, get_mut, remove}, }, data::{ - CachedDataItem, CachedDataItemKey, CachedDataItemValue, DirtyState, InProgressState, + CachedDataItem, CachedDataItemKey, CachedDataItemValue, Dirtyness, InProgressState, InProgressStateInner, }, }; @@ -232,15 +232,11 @@ pub fn make_task_dirty_internal( *stale = true; } let old = task.insert(CachedDataItem::Dirty { - value: DirtyState { - clean_in_session: None, - }, + value: Dirtyness::Dirty, }); let mut dirty_container = match old { Some(CachedDataItemValue::Dirty { - value: DirtyState { - clean_in_session: None, - }, + value: Dirtyness::Dirty, }) => { #[cfg(feature = "trace_task_dirty")] let _span = tracing::trace_span!( @@ -254,16 +250,30 @@ pub fn make_task_dirty_internal( return; } Some(CachedDataItemValue::Dirty { - value: DirtyState { - clean_in_session: Some(session_id), - }, + value: Dirtyness::SessionDependent, }) => { - // Got dirty in that one session only - let mut dirty_container = get!(task, AggregatedDirtyContainerCount) - .cloned() - .unwrap_or_default(); - dirty_container.update_session_dependent(session_id, 1); - dirty_container + let old = remove!(task, CleanInSession); + match old { + None => { + #[cfg(feature = "trace_task_dirty")] + let _span = tracing::trace_span!( + "session-dependent task already dirty", + name = ctx.get_task_description(task_id), + cause = %TaskDirtyCauseInContext::new(&cause, ctx) + ) + .entered(); + // already dirty + return; + } + Some(session_id) => { + // Got dirty in that one session only + let mut dirty_container = get!(task, AggregatedDirtyContainerCount) + .cloned() + .unwrap_or_default(); + dirty_container.update_session_dependent(session_id, 1); + dirty_container + } + } } None => { // Get dirty for all sessions @@ -284,9 +294,8 @@ pub fn make_task_dirty_internal( .entered(); let should_schedule = { - let aggregated_update = dirty_container.update_with_dirty_state(&DirtyState { - clean_in_session: None, - }); + let aggregated_update = + dirty_container.update_with_dirtyness_and_session(Dirtyness::Dirty, None); if !aggregated_update.is_zero() { queue.extend(AggregationUpdateJob::data_update( &mut task, diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs index cd924b85d127c..443ee182e5df2 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs @@ -20,12 +20,12 @@ use crate::{ backend::{ OperationGuard, TaskDataCategory, TransientTask, TurboTasksBackend, TurboTasksBackendInner, TurboTasksBackendJob, - storage::{SpecificTaskDataCategory, StorageWriteGuard, iter_many}, + storage::{SpecificTaskDataCategory, StorageWriteGuard, get, iter_many}, }, backing_storage::BackingStorage, data::{ CachedDataItem, CachedDataItemKey, CachedDataItemType, CachedDataItemValue, - CachedDataItemValueRef, CachedDataItemValueRefMut, + CachedDataItemValueRef, CachedDataItemValueRefMut, Dirtyness, }, }; @@ -415,6 +415,21 @@ pub trait TaskGuard: Debug { fn invalidate_serialization(&mut self); fn prefetch(&mut self) -> Option>; fn is_immutable(&self) -> bool; + fn is_dirty(&self, session_id: SessionId) -> bool { + get!(self, Dirty).is_some_and(|dirtyness| match dirtyness { + Dirtyness::Dirty => true, + Dirtyness::SessionDependent => get!(self, CleanInSession).copied() != Some(session_id), + }) + } + fn dirtyness_and_session(&self) -> Option<(Dirtyness, Option)> { + match get!(self, Dirty)? { + Dirtyness::Dirty => Some((Dirtyness::Dirty, None)), + Dirtyness::SessionDependent => Some(( + Dirtyness::SessionDependent, + get!(self, CleanInSession).copied(), + )), + } + } } pub struct TaskGuardImpl<'a, B: BackingStorage> { diff --git a/turbopack/crates/turbo-tasks-backend/src/data.rs b/turbopack/crates/turbo-tasks-backend/src/data.rs index 2afaab53df280..2bdb13f41619c 100644 --- a/turbopack/crates/turbo-tasks-backend/src/data.rs +++ b/turbopack/crates/turbo-tasks-backend/src/data.rs @@ -144,14 +144,9 @@ transient_traits!(ActivenessState); impl Eq for ActivenessState {} #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub struct DirtyState { - pub clean_in_session: Option, -} - -impl DirtyState { - pub fn get(&self, session: SessionId) -> bool { - self.clean_in_session != Some(session) - } +pub enum Dirtyness { + Dirty, + SessionDependent, } fn add_with_diff(v: &mut i32, u: i32) -> i32 { @@ -251,34 +246,47 @@ impl DirtyContainerCount { diff } - /// Applies a dirty state to the count. Returns an aggregated count that represents the change. - pub fn update_with_dirty_state(&mut self, dirty: &DirtyState) -> DirtyContainerCount { - if let Some(clean_in_session) = dirty.clean_in_session { - self.update_session_dependent(clean_in_session, 1) + /// Applies a dirtyness to the count. Returns an aggregated count that represents the change. + pub fn update_with_dirtyness_and_session( + &mut self, + dirtyness: Dirtyness, + clean_in_session: Option, + ) -> DirtyContainerCount { + if let (Dirtyness::SessionDependent, Some(session_id)) = (dirtyness, clean_in_session) { + self.update_session_dependent(session_id, 1) } else { self.update(1) } } - /// Undoes the effect of a dirty state on the count. Returns an aggregated count that represents + /// Undoes the effect of a dirtyness on the count. Returns an aggregated count that represents /// the change. - pub fn undo_update_with_dirty_state(&mut self, dirty: &DirtyState) -> DirtyContainerCount { - if let Some(clean_in_session) = dirty.clean_in_session { - self.update_session_dependent(clean_in_session, -1) + pub fn undo_update_with_dirtyness_and_session( + &mut self, + dirtyness: Dirtyness, + clean_in_session: Option, + ) -> DirtyContainerCount { + if let (Dirtyness::SessionDependent, Some(session_id)) = (dirtyness, clean_in_session) { + self.update_session_dependent(session_id, -1) } else { self.update(-1) } } - /// Replaces the old dirty state with the new one. Returns an aggregated count that represents + /// Replaces the old dirtyness with the new one. Returns an aggregated count that represents /// the change. - pub fn replace_dirty_state( + pub fn replace_dirtyness_and_session( &mut self, - old: &DirtyState, - new: &DirtyState, + old_dirtyness: Dirtyness, + old_clean_in_session: Option, + new_dirtyness: Dirtyness, + new_clean_in_session: Option, ) -> DirtyContainerCount { - let mut diff = self.undo_update_with_dirty_state(old); - diff.update_count(&self.update_with_dirty_state(new)); + let mut diff = + self.undo_update_with_dirtyness_and_session(old_dirtyness, old_clean_in_session); + diff.update_count( + &self.update_with_dirtyness_and_session(new_dirtyness, new_clean_in_session), + ); diff } @@ -412,19 +420,16 @@ mod dirty_container_count_tests { } #[test] - fn test_update_with_dirty_state() { + fn test_update_with_dirtyness_and_session() { let mut count = DirtyContainerCount::default(); - let dirty = DirtyState { - clean_in_session: None, - }; - let diff = count.update_with_dirty_state(&dirty); + let diff = count.update_with_dirtyness_and_session(Dirtyness::Dirty, None); assert!(!count.is_zero()); assert_eq!(count.get(SESSION_1), 1); assert_eq!(diff.get(SESSION_1), 1); assert_eq!(count.get(SESSION_2), 1); assert_eq!(diff.get(SESSION_2), 1); - let diff = count.undo_update_with_dirty_state(&dirty); + let diff = count.undo_update_with_dirtyness_and_session(Dirtyness::Dirty, None); assert!(count.is_zero()); assert_eq!(count.get(SESSION_1), 0); assert_eq!(diff.get(SESSION_1), -1); @@ -432,17 +437,16 @@ mod dirty_container_count_tests { assert_eq!(diff.get(SESSION_2), -1); let mut count = DirtyContainerCount::default(); - let dirty = DirtyState { - clean_in_session: Some(SESSION_1), - }; - let diff = count.update_with_dirty_state(&dirty); + let diff = + count.update_with_dirtyness_and_session(Dirtyness::SessionDependent, Some(SESSION_1)); assert!(!count.is_zero()); assert_eq!(count.get(SESSION_1), 0); assert_eq!(diff.get(SESSION_1), 0); assert_eq!(count.get(SESSION_2), 1); assert_eq!(diff.get(SESSION_2), 1); - let diff = count.undo_update_with_dirty_state(&dirty); + let diff = count + .undo_update_with_dirtyness_and_session(Dirtyness::SessionDependent, Some(SESSION_1)); assert!(count.is_zero()); assert_eq!(count.get(SESSION_1), 0); assert_eq!(diff.get(SESSION_1), 0); @@ -451,16 +455,15 @@ mod dirty_container_count_tests { } #[test] - fn test_replace_dirty_state() { + fn test_replace_dirtyness_and_session() { let mut count = DirtyContainerCount::default(); - let old = DirtyState { - clean_in_session: None, - }; - let new = DirtyState { - clean_in_session: Some(SESSION_1), - }; - count.update_with_dirty_state(&old); - let diff = count.replace_dirty_state(&old, &new); + count.update_with_dirtyness_and_session(Dirtyness::Dirty, None); + let diff = count.replace_dirtyness_and_session( + Dirtyness::Dirty, + None, + Dirtyness::SessionDependent, + Some(SESSION_1), + ); assert!(!count.is_zero()); assert_eq!(count.get(SESSION_1), 0); assert_eq!(diff.get(SESSION_1), -1); @@ -468,14 +471,13 @@ mod dirty_container_count_tests { assert_eq!(diff.get(SESSION_2), 0); let mut count = DirtyContainerCount::default(); - let old = DirtyState { - clean_in_session: Some(SESSION_1), - }; - let new = DirtyState { - clean_in_session: None, - }; - count.update_with_dirty_state(&old); - let diff = count.replace_dirty_state(&old, &new); + count.update_with_dirtyness_and_session(Dirtyness::SessionDependent, Some(SESSION_1)); + let diff = count.replace_dirtyness_and_session( + Dirtyness::SessionDependent, + Some(SESSION_1), + Dirtyness::Dirty, + None, + ); assert!(!count.is_zero()); assert_eq!(count.get(SESSION_1), 1); assert_eq!(diff.get(SESSION_1), 1); @@ -563,7 +565,10 @@ pub enum CachedDataItem { // State Dirty { - value: DirtyState, + value: Dirtyness, + }, + CleanInSession { + value: SessionId, }, // Children @@ -695,6 +700,7 @@ impl CachedDataItem { !collectible.cell.task.is_transient() } CachedDataItem::Dirty { .. } => true, + CachedDataItem::CleanInSession { .. } => true, CachedDataItem::Child { task, .. } => !task.is_transient(), CachedDataItem::CellData { .. } => true, CachedDataItem::CellTypeMaxIndex { .. } => true, @@ -777,6 +783,7 @@ impl CachedDataItem { | Self::Output { .. } | Self::AggregationNumber { .. } | Self::Dirty { .. } + | Self::CleanInSession { .. } | Self::Follower { .. } | Self::Child { .. } | Self::Upper { .. } @@ -811,6 +818,7 @@ impl CachedDataItemKey { !collectible.cell.task.is_transient() } CachedDataItemKey::Dirty { .. } => true, + CachedDataItemKey::CleanInSession { .. } => true, CachedDataItemKey::Child { task, .. } => !task.is_transient(), CachedDataItemKey::CellData { .. } => true, CachedDataItemKey::CellTypeMaxIndex { .. } => true, @@ -861,6 +869,7 @@ impl CachedDataItemType { | Self::Output { .. } | Self::AggregationNumber { .. } | Self::Dirty { .. } + | Self::CleanInSession { .. } | Self::Follower { .. } | Self::Child { .. } | Self::Upper { .. } @@ -887,6 +896,7 @@ impl CachedDataItemType { Self::Output | Self::Collectible | Self::Dirty + | Self::CleanInSession | Self::Child | Self::CellData | Self::CellTypeMaxIndex