From e191d8d1ca8bd83f7f51d269cb56af7b4b774320 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:53:37 +0100 Subject: [PATCH] v1.17: Refactor - `ExtractedPrograms` (backport of #34205) (#34351) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor - `ExtractedPrograms` (#34205) * Puts ExtractedPrograms into Arc>. * Merges ExtractedPrograms::missing and ExtractedPrograms::unloaded. * Unifies missing entry insertion in LoadedPrograms::extract(). (cherry picked from commit 4ee5078e5ffdfff36d3f7920217788e2892c1a85) Co-authored-by: Alexander Meißner --- program-runtime/src/loaded_programs.rs | 396 ++++++++++--------------- runtime/src/bank.rs | 31 +- 2 files changed, 165 insertions(+), 262 deletions(-) diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 896c21134d1ee6..b7bf3365648b66 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -25,7 +25,7 @@ use { fmt::{Debug, Formatter}, sync::{ atomic::{AtomicU64, Ordering}, - Arc, RwLock, + Arc, Mutex, RwLock, }, }, }; @@ -486,8 +486,7 @@ pub struct LoadedProgramsForTxBatch { pub struct ExtractedPrograms { pub loaded: LoadedProgramsForTxBatch, - pub missing: Vec<(Pubkey, u64)>, - pub unloaded: Vec<(Pubkey, u64)>, + pub missing: HashMap, } impl LoadedProgramsForTxBatch { @@ -776,13 +775,21 @@ impl LoadedPrograms { &self, working_slot: &S, keys: impl Iterator, - ) -> ExtractedPrograms { + ) -> Arc> { let environments = self.get_environments_for_epoch(working_slot.current_epoch()); - let mut missing = Vec::new(); - let mut unloaded = Vec::new(); let current_slot = working_slot.current_slot(); - let found = keys + let extracted = Arc::new(Mutex::new(ExtractedPrograms { + loaded: LoadedProgramsForTxBatch { + entries: HashMap::new(), + slot: current_slot, + environments: environments.clone(), + }, + missing: HashMap::new(), + })); + let mut extracting = extracted.lock().unwrap(); + extracting.loaded.entries = keys .filter_map(|(key, (match_criteria, usage_count))| { + let mut reloading = false; if let Some(second_level) = self.entries.get(&key) { for entry in second_level.iter().rev() { let is_ancestor = if let Some(fork_graph) = &self.fork_graph { @@ -805,19 +812,15 @@ impl LoadedPrograms { || is_ancestor { if current_slot >= entry.effective_slot { - if !Self::is_entry_usable(entry, current_slot, &match_criteria) { - missing.push((key, usage_count)); - return None; - } - - if !Self::matches_environment(entry, environments) { - missing.push((key, usage_count)); - return None; + if !Self::is_entry_usable(entry, current_slot, &match_criteria) + || !Self::matches_environment(entry, environments) + { + break; } if let LoadedProgramType::Unloaded(_environment) = &entry.program { - unloaded.push((key, usage_count)); - return None; + reloading = true; + break; } entry @@ -840,26 +843,18 @@ impl LoadedPrograms { } } } - missing.push((key, usage_count)); + extracting.missing.insert(key, (usage_count, reloading)); None }) .collect::>>(); - self.stats .misses - .fetch_add(missing.len() as u64, Ordering::Relaxed); + .fetch_add(extracting.missing.len() as u64, Ordering::Relaxed); self.stats .hits - .fetch_add(found.len() as u64, Ordering::Relaxed); - ExtractedPrograms { - loaded: LoadedProgramsForTxBatch { - entries: found, - slot: current_slot, - environments: environments.clone(), - }, - missing, - unloaded, - } + .fetch_add(extracting.loaded.entries.len() as u64, Ordering::Relaxed); + drop(extracting); + extracted } pub fn merge(&mut self, tx_batch_cache: &LoadedProgramsForTxBatch) { @@ -990,7 +985,7 @@ mod tests { use { crate::loaded_programs::{ BlockRelation, ExtractedPrograms, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria, - LoadedProgramType, LoadedPrograms, LoadedProgramsForTxBatch, ProgramRuntimeEnvironment, + LoadedProgramType, LoadedPrograms, ProgramRuntimeEnvironment, ProgramRuntimeEnvironments, WorkingSlot, DELAY_VISIBILITY_SLOT_OFFSET, }, assert_matches::assert_matches, @@ -1004,7 +999,7 @@ mod tests { ops::ControlFlow, sync::{ atomic::{AtomicU64, Ordering}, - Arc, RwLock, + Arc, Mutex, RwLock, }, }, }; @@ -1607,18 +1602,33 @@ mod tests { } fn match_slot( - table: &LoadedProgramsForTxBatch, + extracted: &Arc>, program: &Pubkey, deployment_slot: Slot, working_slot: Slot, ) -> bool { - assert_eq!(table.slot, working_slot); - table + let extracted = extracted.lock().unwrap(); + assert_eq!(extracted.loaded.slot, working_slot); + extracted + .loaded .find(program) .map(|entry| entry.deployment_slot == deployment_slot) .unwrap_or(false) } + fn match_missing( + extracted: &Arc>, + key: &Pubkey, + reload: bool, + ) -> bool { + let extracted = extracted.lock().unwrap(); + extracted + .missing + .get(key) + .filter(|(_count, reloading)| *reloading == reload) + .is_some() + } + #[test] fn test_fork_extract_and_prune() { let mut cache = new_mock_cache::(); @@ -1697,11 +1707,7 @@ mod tests { // 23 // Testing fork 0 - 10 - 12 - 22 with current slot at 22 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(22), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1712,19 +1718,14 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 20, 22)); - assert!(match_slot(&found, &program4, 0, 22)); + assert!(match_slot(&extracted, &program1, 20, 22)); + assert!(match_slot(&extracted, &program4, 0, 22)); - assert!(missing.contains(&(program2, 2))); - assert!(missing.contains(&(program3, 3))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program2, false)); + assert!(match_missing(&extracted, &program3, false)); - // Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 15 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + // Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 16 + let extracted = cache.extract( &TestWorkingSlot(15), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1735,24 +1736,24 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 15)); - assert!(match_slot(&found, &program2, 11, 15)); + assert!(match_slot(&extracted, &program1, 0, 15)); + assert!(match_slot(&extracted, &program2, 11, 15)); // The effective slot of program4 deployed in slot 15 is 19. So it should not be usable in slot 16. // A delay visibility tombstone should be returned here. - let tombstone = found.find(&program4).expect("Failed to find the tombstone"); + let tombstone = extracted + .lock() + .unwrap() + .loaded + .find(&program4) + .expect("Failed to find the tombstone"); assert_matches!(tombstone.program, LoadedProgramType::DelayVisibility); assert_eq!(tombstone.deployment_slot, 15); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing the same fork above, but current slot is now 18 (equal to effective slot of program4). - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(18), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1763,21 +1764,16 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 18)); - assert!(match_slot(&found, &program2, 11, 18)); + assert!(match_slot(&extracted, &program1, 0, 18)); + assert!(match_slot(&extracted, &program2, 11, 18)); // The effective slot of program4 deployed in slot 15 is 18. So it should be usable in slot 18. - assert!(match_slot(&found, &program4, 15, 18)); + assert!(match_slot(&extracted, &program4, 15, 18)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing the same fork above, but current slot is now 23 (future slot than effective slot of program4). - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(23), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1788,21 +1784,16 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 23)); - assert!(match_slot(&found, &program2, 11, 23)); + assert!(match_slot(&extracted, &program1, 0, 23)); + assert!(match_slot(&extracted, &program2, 11, 23)); // The effective slot of program4 deployed in slot 15 is 19. So it should be usable in slot 23. - assert!(match_slot(&found, &program4, 15, 23)); + assert!(match_slot(&extracted, &program4, 15, 23)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 11 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(11), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1813,15 +1804,19 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 11)); + assert!(match_slot(&extracted, &program1, 0, 11)); // program2 was updated at slot 11, but is not effective till slot 12. The result should contain a tombstone. - let tombstone = found.find(&program2).expect("Failed to find the tombstone"); + let tombstone = extracted + .lock() + .unwrap() + .loaded + .find(&program2) + .expect("Failed to find the tombstone"); assert_matches!(tombstone.program, LoadedProgramType::DelayVisibility); assert_eq!(tombstone.deployment_slot, 11); - assert!(match_slot(&found, &program4, 5, 11)); + assert!(match_slot(&extracted, &program4, 5, 11)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // The following is a special case, where there's an expiration slot let test_program = Arc::new(LoadedProgram { @@ -1836,11 +1831,7 @@ mod tests { assert!(!cache.replenish(program4, test_program).0); // Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(19), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1851,21 +1842,16 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 19)); - assert!(match_slot(&found, &program2, 11, 19)); + assert!(match_slot(&extracted, &program1, 0, 19)); + assert!(match_slot(&extracted, &program2, 11, 19)); // Program4 deployed at slot 19 should not be expired yet - assert!(match_slot(&found, &program4, 19, 19)); + assert!(match_slot(&extracted, &program4, 19, 19)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 21 // This would cause program4 deployed at slot 19 to be expired. - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(21), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1876,12 +1862,11 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 21)); - assert!(match_slot(&found, &program2, 11, 21)); + assert!(match_slot(&extracted, &program1, 0, 21)); + assert!(match_slot(&extracted, &program2, 11, 21)); - assert!(missing.contains(&(program3, 1))); - assert!(missing.contains(&(program4, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); + assert!(match_missing(&extracted, &program4, false)); // Remove the expired entry to let the rest of the test continue if let Some(programs) = cache.entries.get_mut(&program4) { @@ -1905,12 +1890,8 @@ mod tests { // | // 23 - // Testing fork 11 - 15 - 16- 19 - 22 with root at 5 and current slot at 21 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + // Testing fork 11 - 15 - 16- 19 - 22 with root at 5 and current slot at 22 + let extracted = cache.extract( &TestWorkingSlot(21), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1922,19 +1903,14 @@ mod tests { ); // Since the fork was pruned, we should not find the entry deployed at slot 20. - assert!(match_slot(&found, &program1, 0, 21)); - assert!(match_slot(&found, &program2, 11, 21)); - assert!(match_slot(&found, &program4, 15, 21)); + assert!(match_slot(&extracted, &program1, 0, 21)); + assert!(match_slot(&extracted, &program2, 11, 21)); + assert!(match_slot(&extracted, &program4, 15, 21)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27 - let ExtractedPrograms { - loaded: found, - missing: _, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(27), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1945,11 +1921,10 @@ mod tests { .into_iter(), ); - assert!(unloaded.is_empty()); - assert!(match_slot(&found, &program1, 0, 27)); - assert!(match_slot(&found, &program2, 11, 27)); - assert!(match_slot(&found, &program3, 25, 27)); - assert!(match_slot(&found, &program4, 5, 27)); + assert!(match_slot(&extracted, &program1, 0, 27)); + assert!(match_slot(&extracted, &program2, 11, 27)); + assert!(match_slot(&extracted, &program3, 25, 27)); + assert!(match_slot(&extracted, &program4, 5, 27)); cache.prune(15, 0); @@ -1969,11 +1944,7 @@ mod tests { // 23 // Testing fork 16, 19, 23, with root at 15, current slot at 23 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(23), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -1984,13 +1955,12 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 23)); - assert!(match_slot(&found, &program2, 11, 23)); - assert!(match_slot(&found, &program4, 15, 23)); + assert!(match_slot(&extracted, &program1, 0, 23)); + assert!(match_slot(&extracted, &program2, 11, 23)); + assert!(match_slot(&extracted, &program4, 15, 23)); // program3 was deployed on slot 25, which has been pruned - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); } #[test] @@ -2032,11 +2002,7 @@ mod tests { assert!(!cache.replenish(program3, new_test_loaded_program(25, 26)).0); // Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(12), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2046,18 +2012,13 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 12)); - assert!(match_slot(&found, &program2, 11, 12)); + assert!(match_slot(&extracted, &program1, 0, 12)); + assert!(match_slot(&extracted, &program2, 11, 12)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Test the same fork, but request the program modified at a later slot than what's in the cache. - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(12), vec![ ( @@ -2073,11 +2034,10 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program2, 11, 12)); + assert!(match_slot(&extracted, &program2, 11, 12)); - assert!(missing.contains(&(program1, 1))); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program1, false)); + assert!(match_missing(&extracted, &program3, false)); } #[test] @@ -2136,11 +2096,7 @@ mod tests { ); // Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(19), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2150,18 +2106,13 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 19)); - assert!(match_slot(&found, &program2, 11, 19)); + assert!(match_slot(&extracted, &program1, 0, 19)); + assert!(match_slot(&extracted, &program2, 11, 19)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(27), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2171,18 +2122,13 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 27)); - assert!(match_slot(&found, &program2, 11, 27)); + assert!(match_slot(&extracted, &program1, 0, 27)); + assert!(match_slot(&extracted, &program2, 11, 27)); - assert!(unloaded.contains(&(program3, 1))); - assert!(missing.is_empty()); + assert!(match_missing(&extracted, &program3, true)); // Testing fork 0 - 10 - 20 - 22 with current slot at 22 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(22), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2192,10 +2138,10 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 20, 22)); + assert!(match_slot(&extracted, &program1, 20, 22)); - assert!(missing.contains(&(program2, 1))); - assert!(unloaded.contains(&(program3, 1))); + assert!(match_missing(&extracted, &program2, false)); + assert!(match_missing(&extracted, &program3, true)); } #[test] @@ -2248,11 +2194,7 @@ mod tests { assert!(!cache.replenish(program1, test_program).0); // Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19 - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(12), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2263,19 +2205,14 @@ mod tests { ); // Program1 deployed at slot 11 should not be expired yet - assert!(match_slot(&found, &program1, 11, 12)); - assert!(match_slot(&found, &program2, 11, 12)); + assert!(match_slot(&extracted, &program1, 11, 12)); + assert!(match_slot(&extracted, &program2, 11, 12)); - assert!(missing.contains(&(program3, 1))); - assert!(unloaded.is_empty()); + assert!(match_missing(&extracted, &program3, false)); // Testing fork 0 - 5 - 11 - 12 - 15 - 16 - 19 - 21 - 23 with current slot at 15 // This would cause program4 deployed at slot 15 to be expired. - let ExtractedPrograms { - loaded: found, - missing, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(15), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2284,12 +2221,11 @@ mod tests { ] .into_iter(), ); - assert!(unloaded.is_empty()); - assert!(match_slot(&found, &program2, 11, 15)); + assert!(match_slot(&extracted, &program2, 11, 15)); - assert!(missing.contains(&(program1, 1))); - assert!(missing.contains(&(program3, 1))); + assert!(match_missing(&extracted, &program1, false)); + assert!(match_missing(&extracted, &program3, false)); // Test that the program still exists in the cache, even though it is expired. assert_eq!( @@ -2343,19 +2279,17 @@ mod tests { cache.prune(10, 0); - let ExtractedPrograms { - loaded: found, - missing: _, - unloaded, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(20), vec![(program1, (LoadedProgramMatchCriteria::NoCriteria, 1))].into_iter(), ); - assert!(unloaded.is_empty()); // The cache should have the program deployed at slot 0 assert_eq!( - found + extracted + .lock() + .unwrap() + .loaded .entries .get(&program1) .expect("Did not find the program") @@ -2391,11 +2325,7 @@ mod tests { let program2 = Pubkey::new_unique(); assert!(!cache.replenish(program2, new_test_loaded_program(10, 11)).0); - let ExtractedPrograms { - loaded: found, - missing: _, - unloaded: _, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(20), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2404,14 +2334,10 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 20)); - assert!(match_slot(&found, &program2, 10, 20)); + assert!(match_slot(&extracted, &program1, 0, 20)); + assert!(match_slot(&extracted, &program2, 10, 20)); - let ExtractedPrograms { - loaded: found, - missing, - unloaded: _, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(6), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2420,18 +2346,14 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 5, 6)); - assert!(missing.contains(&(program2, 1))); + assert!(match_slot(&extracted, &program1, 5, 6)); + assert!(match_missing(&extracted, &program2, false)); // Pruning slot 5 will remove program1 entry deployed at slot 5. // On fork chaining from slot 5, the entry deployed at slot 0 will become visible. cache.prune_by_deployment_slot(5); - let ExtractedPrograms { - loaded: found, - missing: _, - unloaded: _, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(20), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2440,14 +2362,10 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 20)); - assert!(match_slot(&found, &program2, 10, 20)); + assert!(match_slot(&extracted, &program1, 0, 20)); + assert!(match_slot(&extracted, &program2, 10, 20)); - let ExtractedPrograms { - loaded: found, - missing, - unloaded: _, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(6), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2456,18 +2374,14 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 6)); - assert!(missing.contains(&(program2, 1))); + assert!(match_slot(&extracted, &program1, 0, 6)); + assert!(match_missing(&extracted, &program2, false)); // Pruning slot 10 will remove program2 entry deployed at slot 10. // As there is no other entry for program2, extract() will return it as missing. cache.prune_by_deployment_slot(10); - let ExtractedPrograms { - loaded: found, - missing: _, - unloaded: _, - } = cache.extract( + let extracted = cache.extract( &TestWorkingSlot(20), vec![ (program1, (LoadedProgramMatchCriteria::NoCriteria, 1)), @@ -2476,8 +2390,8 @@ mod tests { .into_iter(), ); - assert!(match_slot(&found, &program1, 0, 20)); - assert!(missing.contains(&(program2, 1))); + assert!(match_slot(&extracted, &program1, 0, 20)); + assert!(match_missing(&extracted, &program2, false)); } #[test] diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 420ec7217dd129..a7acc0235e9645 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -195,7 +195,7 @@ use { AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering::{self, AcqRel, Acquire, Relaxed}, }, - Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard, + Arc, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, }, thread::Builder, time::{Duration, Instant}, @@ -5078,28 +5078,23 @@ impl Bank { let ExtractedPrograms { loaded: mut loaded_programs_for_txs, missing, - unloaded, } = { // Lock the global cache to figure out which programs need to be loaded let loaded_programs_cache = self.loaded_programs_cache.read().unwrap(); - loaded_programs_cache.extract(self, programs_and_slots.into_iter()) + Mutex::into_inner( + Arc::into_inner( + loaded_programs_cache.extract(self, programs_and_slots.into_iter()), + ) + .unwrap(), + ) + .unwrap() }; // Load missing programs while global cache is unlocked let missing_programs: Vec<(Pubkey, Arc)> = missing .iter() - .map(|(key, count)| { - let program = self.load_program(key, false, None); - program.tx_usage_counter.store(*count, Ordering::Relaxed); - (*key, program) - }) - .collect(); - - // Reload unloaded programs while global cache is unlocked - let unloaded_programs: Vec<(Pubkey, Arc)> = unloaded - .iter() - .map(|(key, count)| { - let program = self.load_program(key, true, None); + .map(|(key, (count, reloading))| { + let program = self.load_program(key, *reloading, None); program.tx_usage_counter.store(*count, Ordering::Relaxed); (*key, program) }) @@ -5112,12 +5107,6 @@ impl Bank { // Use the returned entry as that might have been deduplicated globally loaded_programs_for_txs.replenish(key, entry); } - for (key, program) in unloaded_programs { - let (_was_occupied, entry) = loaded_programs_cache.replenish(key, program); - // Use the returned entry as that might have been deduplicated globally - loaded_programs_for_txs.replenish(key, entry); - } - loaded_programs_for_txs }