merge read_probe and probe

This commit is contained in:
Niko Matsakis 2018-10-15 09:30:08 -04:00
parent be983aacdf
commit a8fd113636

View file

@ -207,7 +207,7 @@ where
); );
// First, do a check with a read-lock. // First, do a check with a read-lock.
match self.read_probe(self.map.read(), runtime, revision_now, descriptor, key) { match self.probe(self.map.read(), runtime, revision_now, descriptor, key) {
ProbeState::UpToDate(v) => return Ok(v), ProbeState::UpToDate(v) => return Ok(v),
ProbeState::CycleDetected => return Err(CycleDetected), ProbeState::CycleDetected => return Err(CycleDetected),
ProbeState::StaleOrAbsent(_guard) => (), ProbeState::StaleOrAbsent(_guard) => (),
@ -232,7 +232,7 @@ where
// Check with an upgradable read to see if there is a value // Check with an upgradable read to see if there is a value
// already. (This permits other readers but prevents anyone // already. (This permits other readers but prevents anyone
// else from running `read_upgrade` at the same time.) // else from running `read_upgrade` at the same time.)
let mut old_value = match self.read_probe( let mut old_value = match self.probe(
self.map.upgradable_read(), self.map.upgradable_read(),
runtime, runtime,
revision_now, revision_now,
@ -329,13 +329,25 @@ where
/// Helper for `read`: /// Helper for `read`:
/// ///
/// Looks in the map to see if we have an up-to-date value or a /// Invoked with the guard `map` of some lock on `self.map` (read
/// cycle. If so, returns `Ok(v)` with either the value or a cycle-error; /// or write) as well as details about the key to look up. Looks
/// this can be propagated as the final result of read. /// in the map to see if we have an up-to-date value or a
/// cycle. Returns a suitable `ProbeState`:
/// ///
/// Otherwise, returns `Err(map)` where `map` is the lock guard /// - `ProbeState::UpToDate(r)` if the table has an up-to-date
/// that was given in as argument. /// value (or we blocked on another thread that produced such a value).
fn read_probe<MapGuard>( /// - `ProbeState::CycleDetected` if this thread is (directly or
/// indirectly) already computing this value.
/// - `ProbeState::BlockedOnOtherThread` if some other thread
/// (which does not depend on us) was already computing this
/// value; caller should re-acquire the lock and try again.
/// - `ProbeState::StaleOrAbsent` if either (a) there is no memo
/// for this key, (b) the memo has no value; or (c) the memo
/// has not been verified at the current revision.
///
/// Note that in all cases **except** for `StaleOrAbsent`, the lock on
/// `map` will have been released.
fn probe<MapGuard>(
&self, &self,
map: MapGuard, map: MapGuard,
runtime: &Runtime<DB>, runtime: &Runtime<DB>,
@ -343,64 +355,6 @@ where
descriptor: &DB::QueryDescriptor, descriptor: &DB::QueryDescriptor,
key: &Q::Key, key: &Q::Key,
) -> ProbeState<StampedValue<Q::Value>, MapGuard> ) -> ProbeState<StampedValue<Q::Value>, MapGuard>
where
MapGuard: Deref<Target = FxHashMap<Q::Key, QueryState<DB, Q>>>,
{
self.probe(
map,
runtime,
revision_now,
descriptor,
key,
|memo| {
if let Some(value) = &memo.value {
debug!(
"{:?}({:?}): returning memoized value (changed_at={:?})",
Q::default(),
key,
memo.changed_at,
);
Some(StampedValue {
value: value.clone(),
changed_at: memo.changed_at,
})
} else {
None
}
},
|v| v,
)
}
/// Helper:
///
/// Invoked with the guard `map` of some lock on `self.map` (read
/// or write) as well as details about the key to look up. It will
/// check the map and return a suitable `ProbeState`:
///
/// - `ProbeState::UpToDate(r)` if the memo is up-to-date,
/// and invoking `with_up_to_date_memo` returned `Some(r)`.
/// - `ProbeState::CycleDetected` if this thread is (directly or
/// indirectly) already computing this value.
/// - `ProbeState::BlockedOnOtherThread` if some other thread
/// (which does not depend on us) was already computing this
/// value; caller should re-acquire the lock and try again.
/// - `ProbeState::StaleOrAbsent` if either (a) there is no memo for this key,
/// (b) the memo has not been verified at the current revision, or
/// (c) `with_up_to_date_memo` returned `None`.
///
/// Note that in all cases **except** for `StaleOrAbsent`, the lock on
/// `map` will have been released.
fn probe<MapGuard, R>(
&self,
map: MapGuard,
runtime: &Runtime<DB>,
revision_now: Revision,
descriptor: &DB::QueryDescriptor,
key: &Q::Key,
with_up_to_date_memo: impl FnOnce(&Memo<DB, Q>) -> Option<R>,
with_stamped_value: impl FnOnce(StampedValue<Q::Value>) -> R,
) -> ProbeState<R, MapGuard>
where where
MapGuard: Deref<Target = FxHashMap<Q::Key, QueryState<DB, Q>>>, MapGuard: Deref<Target = FxHashMap<Q::Key, QueryState<DB, Q>>>,
{ {
@ -425,25 +379,33 @@ where
std::mem::drop(map); std::mem::drop(map);
let value = rx.recv().unwrap(); let value = rx.recv().unwrap();
let value = with_stamped_value(value);
return ProbeState::UpToDate(value); return ProbeState::UpToDate(value);
} }
} }
Some(QueryState::Memoized(m)) => { Some(QueryState::Memoized(memo)) => {
debug!( debug!(
"{:?}({:?}): found memoized value verified_at={:?}", "{:?}({:?}): found memoized value verified_at={:?}",
Q::default(), Q::default(),
key, key,
m.verified_at, memo.verified_at,
); );
// We've found that the query is definitely up-to-date. // We've found that the query is definitely up-to-date.
// If the value is also memoized, return it. // If the value is also memoized, return it.
// Otherwise fallback to recomputing the value. // Otherwise fallback to recomputing the value.
if m.verified_at == revision_now { if memo.verified_at == revision_now {
if let Some(r) = with_up_to_date_memo(&m) { if let Some(value) = &memo.value {
return ProbeState::UpToDate(r); debug!(
"{:?}({:?}): returning memoized value (changed_at={:?})",
Q::default(),
key,
memo.changed_at,
);
return ProbeState::UpToDate(StampedValue {
value: value.clone(),
changed_at: memo.changed_at,
});
} }
} }
} }