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