turbo_tasks/
invalidation.rs1use std::{fmt::Display, mem::replace, sync::Arc};
2
3use bincode::{Decode, Encode};
4use indexmap::map::Entry;
5use turbo_dyn_eq_hash::{
6 DynEq, DynHash, impl_eq_for_dyn, impl_hash_for_dyn, impl_partial_eq_for_dyn,
7};
8
9use crate::{
10 FxIndexMap, FxIndexSet, TaskId, TurboTasksApi,
11 manager::{current_task_if_available, mark_invalidator},
12 trace::TraceRawVcs,
13 util::StaticOrArc,
14};
15
16pub fn get_invalidator() -> Option<Invalidator> {
20 if let Some(task) = current_task_if_available("turbo_tasks::get_invalidator()") {
21 mark_invalidator();
22 Some(Invalidator { task })
23 } else {
24 None
25 }
26}
27
28#[derive(Clone, Copy, Hash, PartialEq, Eq, Encode, Decode)]
31pub struct Invalidator {
32 task: TaskId,
33}
34
35impl Invalidator {
36 pub fn invalidate(self, turbo_tasks: &dyn TurboTasksApi) {
37 turbo_tasks.invalidate(self.task);
38 }
39
40 pub fn invalidate_with_reason<T: InvalidationReason>(
41 self,
42 turbo_tasks: &dyn TurboTasksApi,
43 reason: T,
44 ) {
45 turbo_tasks.invalidate_with_reason(
46 self.task,
47 (Arc::new(reason) as Arc<dyn InvalidationReason>).into(),
48 );
49 }
50}
51
52impl TraceRawVcs for Invalidator {
53 fn trace_raw_vcs(&self, _context: &mut crate::trace::TraceRawVcsContext) {
54 }
56}
57
58pub trait InvalidationReason: DynEq + DynHash + Display + Send + Sync + 'static {
63 fn kind(&self) -> Option<StaticOrArc<dyn InvalidationReasonKind>> {
64 None
65 }
66}
67
68pub trait InvalidationReasonKind: DynEq + DynHash + Send + Sync + 'static {
74 fn fmt(
77 &self,
78 data: &FxIndexSet<StaticOrArc<dyn InvalidationReason>>,
79 f: &mut std::fmt::Formatter<'_>,
80 ) -> std::fmt::Result;
81}
82
83impl_partial_eq_for_dyn!(dyn InvalidationReason);
84impl_eq_for_dyn!(dyn InvalidationReason);
85impl_hash_for_dyn!(dyn InvalidationReason);
86
87impl_partial_eq_for_dyn!(dyn InvalidationReasonKind);
88impl_eq_for_dyn!(dyn InvalidationReasonKind);
89impl_hash_for_dyn!(dyn InvalidationReasonKind);
90
91#[derive(PartialEq, Eq, Hash)]
92enum MapKey {
93 Untyped {
94 unique_tag: usize,
95 },
96 Typed {
97 kind: StaticOrArc<dyn InvalidationReasonKind>,
98 },
99}
100
101enum MapEntry {
102 Single {
103 reason: StaticOrArc<dyn InvalidationReason>,
104 },
105 Multiple {
106 reasons: FxIndexSet<StaticOrArc<dyn InvalidationReason>>,
107 },
108}
109
110#[derive(Default)]
114pub struct InvalidationReasonSet {
115 next_unique_tag: usize,
116 map: FxIndexMap<MapKey, MapEntry>,
118}
119
120impl InvalidationReasonSet {
121 pub(crate) fn insert(&mut self, reason: StaticOrArc<dyn InvalidationReason>) {
122 if let Some(kind) = reason.kind() {
123 let key = MapKey::Typed { kind };
124 match self.map.entry(key) {
125 Entry::Occupied(mut entry) => {
126 let entry = &mut *entry.get_mut();
127 match replace(
128 entry,
129 MapEntry::Multiple {
130 reasons: FxIndexSet::default(),
131 },
132 ) {
133 MapEntry::Single {
134 reason: existing_reason,
135 } => {
136 if reason == existing_reason {
137 *entry = MapEntry::Single {
138 reason: existing_reason,
139 };
140 return;
141 }
142 let mut reasons = FxIndexSet::default();
143 reasons.insert(existing_reason);
144 reasons.insert(reason);
145 *entry = MapEntry::Multiple { reasons };
146 }
147 MapEntry::Multiple { mut reasons } => {
148 reasons.insert(reason);
149 *entry = MapEntry::Multiple { reasons };
150 }
151 }
152 }
153 Entry::Vacant(entry) => {
154 entry.insert(MapEntry::Single { reason });
155 }
156 }
157 } else {
158 let key = MapKey::Untyped {
159 unique_tag: self.next_unique_tag,
160 };
161 self.next_unique_tag += 1;
162 self.map.insert(key, MapEntry::Single { reason });
163 }
164 }
165
166 pub fn is_empty(&self) -> bool {
167 self.map.is_empty()
168 }
169
170 pub fn len(&self) -> usize {
171 self.map.len()
172 }
173}
174
175impl Display for InvalidationReasonSet {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 let count = self.map.len();
178 for (i, (key, entry)) in self.map.iter().enumerate() {
179 if i > 0 {
180 write!(f, ", ")?;
181 if i == count - 1 {
182 write!(f, "and ")?;
183 }
184 }
185 match entry {
186 MapEntry::Single { reason } => {
187 write!(f, "{reason}")?;
188 }
189 MapEntry::Multiple { reasons } => {
190 let MapKey::Typed { kind } = key else {
191 unreachable!("An untyped reason can't collect more than one reason");
192 };
193 kind.fmt(reasons, f)?
194 }
195 }
196 }
197 Ok(())
198 }
199}