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