Adds safer alternatives to get_internal() (#35325)

This commit is contained in:
Brooks 2024-02-27 18:08:25 -05:00 committed by GitHub
parent 94698b8dd0
commit da088681ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 11 deletions

View File

@ -1130,9 +1130,9 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
pub fn get_and_then<R>(
&self,
pubkey: &Pubkey,
callback: impl FnOnce(Option<&AccountMapEntry<T>>) -> (bool, R),
callback: impl FnOnce(Option<&AccountMapEntryInner<T>>) -> (bool, R),
) -> R {
self.get_bin(pubkey).get_internal(pubkey, callback)
self.get_bin(pubkey).get_internal_inner(pubkey, callback)
}
/// Gets the index's entry for `pubkey`, with `ancestors` and `max_root`,
@ -1159,10 +1159,8 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
///
/// Prefer `get_and_then()` whenever possible.
pub fn get_cloned(&self, pubkey: &Pubkey) -> Option<AccountMapEntry<T>> {
// We *must* add the index entry to the in-mem cache!
// If the index entry is only on-disk, returning a clone would allow the entry
// to be modified, but those modifications would be lost on drop!
self.get_and_then(pubkey, |entry| (true, entry.cloned()))
self.get_bin(pubkey)
.get_internal_cloned(pubkey, |entry| entry)
}
/// Is `pubkey` in the index?
@ -1443,6 +1441,9 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
lock = Some(&self.account_maps[bin]);
last_bin = bin;
}
// SAFETY: The caller must ensure that if `provide_entry_in_callback` is true, and
// if it's possible for `callback` to clone the entry Arc, then it must also add
// the entry to the in-mem cache if the entry is made dirty.
lock.as_ref().unwrap().get_internal(pubkey, |entry| {
let mut cache = false;
match entry {
@ -1830,7 +1831,7 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
pub fn ref_count_from_storage(&self, pubkey: &Pubkey) -> RefCount {
let map = self.get_bin(pubkey);
map.get_internal(pubkey, |entry| {
map.get_internal_inner(pubkey, |entry| {
(
false,
entry.map(|entry| entry.ref_count()).unwrap_or_default(),
@ -4073,7 +4074,7 @@ pub mod tests {
let map = index.get_bin(&key);
for expected in [false, true] {
assert!(map.get_internal(&key, |entry| {
assert!(map.get_internal_inner(&key, |entry| {
// check refcount BEFORE the unref
assert_eq!(u64::from(!expected), entry.unwrap().ref_count());
// first time, ref count was at 1, we can unref once. Unref should return false.

View File

@ -320,7 +320,7 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> InMemAccountsIndex<T,
/// lookup 'pubkey' in index (in mem or on disk)
pub fn get(&self, pubkey: &K) -> Option<AccountMapEntry<T>> {
self.get_internal(pubkey, |entry| (true, entry.map(Arc::clone)))
self.get_internal_cloned(pubkey, |entry| entry)
}
/// set age of 'entry' to the future
@ -331,7 +331,40 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> InMemAccountsIndex<T,
/// lookup 'pubkey' in index (in_mem or disk).
/// call 'callback' whether found or not
pub fn get_internal<RT>(
pub(crate) fn get_internal_inner<RT>(
&self,
pubkey: &K,
// return true if item should be added to in_mem cache
callback: impl for<'a> FnOnce(Option<&AccountMapEntryInner<T>>) -> (bool, RT),
) -> RT {
// SAFETY: The entry Arc is not passed to `callback`, so
// it cannot live beyond this function call.
self.get_internal(pubkey, |entry| callback(entry.map(Arc::as_ref)))
}
/// lookup 'pubkey' in the index (in_mem or disk).
/// call 'callback' whether found or not
pub(crate) fn get_internal_cloned<RT>(
&self,
pubkey: &K,
callback: impl for<'a> FnOnce(Option<AccountMapEntry<T>>) -> RT,
) -> RT {
// SAFETY: Since we're passing the entry Arc clone to `callback`, we must
// also add the entry to the in-mem cache.
self.get_internal(pubkey, |entry| (true, callback(entry.map(Arc::clone))))
}
/// lookup 'pubkey' in index (in_mem or disk).
/// call 'callback' whether found or not
///
/// # Safety
///
/// If the item is on-disk (and not in-mem), add if the item is/could be made dirty
/// *after* `callback` finishes (e.g. the entry Arc is cloned and saved by the caller),
/// then the disk entry *must* also be added to the in-mem cache.
///
/// Prefer `get_internal_inner()` or `get_internal_cloned()` for safe alternatives.
pub(crate) fn get_internal<RT>(
&self,
pubkey: &K,
// return true if item should be added to in_mem cache
@ -446,7 +479,7 @@ impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> InMemAccountsIndex<T,
pubkey: &Pubkey,
user: impl for<'a> FnOnce(&mut RwLockWriteGuard<'a, SlotList<T>>) -> RT,
) -> Option<RT> {
self.get_internal(pubkey, |entry| {
self.get_internal_inner(pubkey, |entry| {
(
true,
entry.map(|entry| {