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, NonLocalValue, OperationValue, 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, Debug)]
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
58unsafe impl OperationValue for Invalidator {}
59unsafe impl NonLocalValue for Invalidator {}
62
63pub trait InvalidationReason: DynEq + DynHash + Display + Send + Sync + 'static {
68 fn kind(&self) -> Option<StaticOrArc<dyn InvalidationReasonKind>> {
69 None
70 }
71}
72
73pub trait InvalidationReasonKind: DynEq + DynHash + Send + Sync + 'static {
79 fn fmt(
82 &self,
83 data: &FxIndexSet<StaticOrArc<dyn InvalidationReason>>,
84 f: &mut std::fmt::Formatter<'_>,
85 ) -> std::fmt::Result;
86}
87
88impl_partial_eq_for_dyn!(dyn InvalidationReason);
89impl_eq_for_dyn!(dyn InvalidationReason);
90impl_hash_for_dyn!(dyn InvalidationReason);
91
92impl_partial_eq_for_dyn!(dyn InvalidationReasonKind);
93impl_eq_for_dyn!(dyn InvalidationReasonKind);
94impl_hash_for_dyn!(dyn InvalidationReasonKind);
95
96#[derive(PartialEq, Eq, Hash)]
97enum MapKey {
98 Untyped {
99 unique_tag: usize,
100 },
101 Typed {
102 kind: StaticOrArc<dyn InvalidationReasonKind>,
103 },
104}
105
106enum MapEntry {
107 Single {
108 reason: StaticOrArc<dyn InvalidationReason>,
109 },
110 Multiple {
111 reasons: FxIndexSet<StaticOrArc<dyn InvalidationReason>>,
112 },
113}
114
115#[derive(Default)]
119pub struct InvalidationReasonSet {
120 next_unique_tag: usize,
121 map: FxIndexMap<MapKey, MapEntry>,
123}
124
125impl InvalidationReasonSet {
126 pub(crate) fn insert(&mut self, reason: StaticOrArc<dyn InvalidationReason>) {
127 if let Some(kind) = reason.kind() {
128 let key = MapKey::Typed { kind };
129 match self.map.entry(key) {
130 Entry::Occupied(mut entry) => {
131 let entry = &mut *entry.get_mut();
132 match replace(
133 entry,
134 MapEntry::Multiple {
135 reasons: FxIndexSet::default(),
136 },
137 ) {
138 MapEntry::Single {
139 reason: existing_reason,
140 } => {
141 if reason == existing_reason {
142 *entry = MapEntry::Single {
143 reason: existing_reason,
144 };
145 return;
146 }
147 let mut reasons = FxIndexSet::default();
148 reasons.insert(existing_reason);
149 reasons.insert(reason);
150 *entry = MapEntry::Multiple { reasons };
151 }
152 MapEntry::Multiple { mut reasons } => {
153 reasons.insert(reason);
154 *entry = MapEntry::Multiple { reasons };
155 }
156 }
157 }
158 Entry::Vacant(entry) => {
159 entry.insert(MapEntry::Single { reason });
160 }
161 }
162 } else {
163 let key = MapKey::Untyped {
164 unique_tag: self.next_unique_tag,
165 };
166 self.next_unique_tag += 1;
167 self.map.insert(key, MapEntry::Single { reason });
168 }
169 }
170
171 pub fn is_empty(&self) -> bool {
172 self.map.is_empty()
173 }
174
175 pub fn len(&self) -> usize {
176 self.map.len()
177 }
178}
179
180impl Display for InvalidationReasonSet {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 let count = self.map.len();
183 for (i, (key, entry)) in self.map.iter().enumerate() {
184 if i > 0 {
185 write!(f, ", ")?;
186 if i == count - 1 {
187 write!(f, "and ")?;
188 }
189 }
190 match entry {
191 MapEntry::Single { reason } => {
192 write!(f, "{reason}")?;
193 }
194 MapEntry::Multiple { reasons } => {
195 let MapKey::Typed { kind } = key else {
196 unreachable!("An untyped reason can't collect more than one reason");
197 };
198 kind.fmt(reasons, f)?
199 }
200 }
201 }
202 Ok(())
203 }
204}