@@ -15,6 +15,7 @@ use crate::{DatabaseKeyIndex, QueryDb};
15
15
use indexmap::map::Entry;
16
16
use parking_lot::RwLock;
17
17
use std::convert::TryFrom;
18
+ use std::iter;
18
19
use tracing::debug;
19
20
20
21
/// Input queries store the result plus a list of the other queries
@@ -25,15 +26,12 @@ where
25
26
Q: Query,
26
27
{
27
28
group_index: u16,
28
- slots: RwLock<FxIndexMap<Q::Key, Slot<Q>>>,
29
+ slots: RwLock<FxIndexMap<Q::Key, Slot<Q::Value >>>,
29
30
}
30
31
31
- struct Slot<Q>
32
- where
33
- Q: Query,
34
- {
32
+ struct Slot<V> {
35
33
database_key_index: DatabaseKeyIndex,
36
- stamped_value: RwLock<StampedValue<Q::Value >>,
34
+ stamped_value: RwLock<StampedValue<V >>,
37
35
}
38
36
39
37
impl<Q> std::panic::RefUnwindSafe for InputStorage<Q>
78
76
debug_assert!(revision < db.salsa_runtime().current_revision());
79
77
let slots = &self.slots.read();
80
78
let slot = slots.get_index(input.key_index as usize).unwrap().1;
81
- slot.maybe_changed_after(db, revision)
79
+
80
+ debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
81
+
82
+ let changed_at = slot.stamped_value.read().changed_at;
83
+
84
+ debug!("maybe_changed_after: changed_at = {:?}", changed_at);
85
+
86
+ changed_at > revision
82
87
}
83
88
84
89
fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Q::Value {
@@ -121,21 +126,6 @@ where
121
126
}
122
127
}
123
128
124
- impl<Q> Slot<Q>
125
- where
126
- Q: Query,
127
- {
128
- fn maybe_changed_after(&self, _db: &<Q as QueryDb<'_>>::DynDb, revision: Revision) -> bool {
129
- debug!("maybe_changed_after(slot={:?}, revision={:?})", self, revision,);
130
-
131
- let changed_at = self.stamped_value.read().changed_at;
132
-
133
- debug!("maybe_changed_after: changed_at = {:?}", changed_at);
134
-
135
- changed_at > revision
136
- }
137
- }
138
-
139
129
impl<Q> QueryStorageMassOps for InputStorage<Q>
140
130
where
141
131
Q: Query,
@@ -202,6 +192,167 @@ where
202
192
}
203
193
}
204
194
195
+ /// Same as `InputStorage`, but optimized for queries that take no inputs.
196
+ pub struct UnitInputStorage<Q>
197
+ where
198
+ Q: Query<Key = ()>,
199
+ {
200
+ group_index: u16,
201
+ slot: UnitSlot<Q::Value>,
202
+ }
203
+
204
+ struct UnitSlot<V> {
205
+ database_key_index: DatabaseKeyIndex,
206
+ stamped_value: RwLock<Option<StampedValue<V>>>,
207
+ }
208
+
209
+ impl<Q> std::panic::RefUnwindSafe for UnitInputStorage<Q>
210
+ where
211
+ Q: Query<Key = ()>,
212
+ Q::Key: std::panic::RefUnwindSafe,
213
+ Q::Value: std::panic::RefUnwindSafe,
214
+ {
215
+ }
216
+
217
+ impl<Q> QueryStorageOps<Q> for UnitInputStorage<Q>
218
+ where
219
+ Q: Query<Key = ()>,
220
+ {
221
+ const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = CycleRecoveryStrategy::Panic;
222
+
223
+ fn new(group_index: u16) -> Self {
224
+ let database_key_index =
225
+ DatabaseKeyIndex { group_index, query_index: Q::QUERY_INDEX, key_index: 0 };
226
+ UnitInputStorage {
227
+ group_index,
228
+ slot: UnitSlot { database_key_index, stamped_value: RwLock::new(None) },
229
+ }
230
+ }
231
+
232
+ fn fmt_index(
233
+ &self,
234
+ _db: &<Q as QueryDb<'_>>::DynDb,
235
+ index: DatabaseKeyIndex,
236
+ fmt: &mut std::fmt::Formatter<'_>,
237
+ ) -> std::fmt::Result {
238
+ assert_eq!(index.group_index, self.group_index);
239
+ assert_eq!(index.query_index, Q::QUERY_INDEX);
240
+ write!(fmt, "{}", Q::QUERY_NAME)
241
+ }
242
+
243
+ fn maybe_changed_after(
244
+ &self,
245
+ db: &<Q as QueryDb<'_>>::DynDb,
246
+ input: DatabaseKeyIndex,
247
+ revision: Revision,
248
+ ) -> bool {
249
+ assert_eq!(input.group_index, self.group_index);
250
+ assert_eq!(input.query_index, Q::QUERY_INDEX);
251
+ debug_assert!(revision < db.salsa_runtime().current_revision());
252
+
253
+ debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
254
+
255
+ let changed_at = self.slot.stamped_value.read().as_ref().unwrap().changed_at;
256
+
257
+ debug!("maybe_changed_after: changed_at = {:?}", changed_at);
258
+
259
+ changed_at > revision
260
+ }
261
+
262
+ fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, &(): &Q::Key) -> Q::Value {
263
+ db.unwind_if_cancelled();
264
+
265
+ let StampedValue { value, durability, changed_at } = self
266
+ .slot
267
+ .stamped_value
268
+ .read()
269
+ .clone()
270
+ .unwrap_or_else(|| panic!("no value set for {:?}", Q::default()));
271
+
272
+ db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
273
+ self.slot.database_key_index,
274
+ durability,
275
+ changed_at,
276
+ );
277
+
278
+ value
279
+ }
280
+
281
+ fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, &(): &Q::Key) -> Durability {
282
+ match &*self.slot.stamped_value.read() {
283
+ Some(stamped_value) => stamped_value.durability,
284
+ None => panic!("no value set for {:?}", Q::default(),),
285
+ }
286
+ }
287
+
288
+ fn entries<C>(&self, _db: &<Q as QueryDb<'_>>::DynDb) -> C
289
+ where
290
+ C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
291
+ {
292
+ iter::once(TableEntry::new(
293
+ (),
294
+ self.slot.stamped_value.read().as_ref().map(|it| it.value.clone()),
295
+ ))
296
+ .collect()
297
+ }
298
+ }
299
+
300
+ impl<Q> QueryStorageMassOps for UnitInputStorage<Q>
301
+ where
302
+ Q: Query<Key = ()>,
303
+ {
304
+ fn purge(&self) {
305
+ *self.slot.stamped_value.write() = Default::default();
306
+ }
307
+ }
308
+
309
+ impl<Q> InputQueryStorageOps<Q> for UnitInputStorage<Q>
310
+ where
311
+ Q: Query<Key = ()>,
312
+ {
313
+ fn set(&self, runtime: &mut Runtime, (): &Q::Key, value: Q::Value, durability: Durability) {
314
+ tracing::debug!("{:?} = {:?} ({:?})", Q::default(), value, durability);
315
+
316
+ // The value is changing, so we need a new revision (*). We also
317
+ // need to update the 'last changed' revision by invoking
318
+ // `guard.mark_durability_as_changed`.
319
+ //
320
+ // CAREFUL: This will block until the global revision lock can
321
+ // be acquired. If there are still queries executing, they may
322
+ // need to read from this input. Therefore, we wait to acquire
323
+ // the lock on `map` until we also hold the global query write
324
+ // lock.
325
+ //
326
+ // (*) Technically, since you can't presently access an input
327
+ // for a non-existent key, and you can't enumerate the set of
328
+ // keys, we only need a new revision if the key used to
329
+ // exist. But we may add such methods in the future and this
330
+ // case doesn't generally seem worth optimizing for.
331
+ runtime.with_incremented_revision(|next_revision| {
332
+ let mut stamped_value_slot = self.slot.stamped_value.write();
333
+
334
+ // Do this *after* we acquire the lock, so that we are not
335
+ // racing with somebody else to modify this same cell.
336
+ // (Otherwise, someone else might write a *newer* revision
337
+ // into the same cell while we block on the lock.)
338
+ let stamped_value = StampedValue { value, durability, changed_at: next_revision };
339
+
340
+ match &mut *stamped_value_slot {
341
+ Some(slot_stamped_value) => {
342
+ let old_durability = slot_stamped_value.durability;
343
+ *slot_stamped_value = stamped_value;
344
+ Some(old_durability)
345
+ }
346
+
347
+ stamped_value_slot @ None => {
348
+ *stamped_value_slot = Some(stamped_value);
349
+ None
350
+ }
351
+ }
352
+ });
353
+ }
354
+ }
355
+
205
356
/// Check that `Slot<Q, MP>: Send + Sync` as long as
206
357
/// `DB::DatabaseData: Send + Sync`, which in turn implies that
207
358
/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`.
@@ -213,7 +364,8 @@ where
213
364
Q::Value: Send + Sync,
214
365
{
215
366
fn is_send_sync<T: Send + Sync>() {}
216
- is_send_sync::<Slot<Q>>();
367
+ is_send_sync::<Slot<Q::Value>>();
368
+ is_send_sync::<UnitSlot<Q::Value>>();
217
369
}
218
370
219
371
/// Check that `Slot<Q, MP>: 'static` as long as
@@ -227,14 +379,6 @@ where
227
379
Q::Value: 'static,
228
380
{
229
381
fn is_static<T: 'static>() {}
230
- is_static::<Slot<Q>>();
231
- }
232
-
233
- impl<Q> std::fmt::Debug for Slot<Q>
234
- where
235
- Q: Query,
236
- {
237
- fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238
- write!(fmt, "{:?}", Q::default())
239
- }
382
+ is_static::<Slot<Q::Value>>();
383
+ is_static::<UnitSlot<Q::Value>>();
240
384
}
0 commit comments