diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 740eac7fc..6da84b0d1 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -382,6 +382,20 @@ impl LoadedProgram { } pub fn to_unloaded(&self) -> Option { + match &self.program { + LoadedProgramType::LegacyV0(_) + | LoadedProgramType::LegacyV1(_) + | LoadedProgramType::Typed(_) => {} + #[cfg(test)] + LoadedProgramType::TestLoaded(_) => {} + LoadedProgramType::FailedVerification(_) + | LoadedProgramType::Closed + | LoadedProgramType::DelayVisibility + | LoadedProgramType::Unloaded(_) + | LoadedProgramType::Builtin(_) => { + return None; + } + } Some(Self { program: LoadedProgramType::Unloaded(self.program.get_environment()?.clone()), account_size: self.account_size, @@ -1054,31 +1068,6 @@ impl LoadedPrograms { } } - fn unload_program(&mut self, id: &Pubkey) { - if let Some(second_level) = self.entries.get_mut(id) { - for entry in second_level.slot_versions.iter_mut() { - if let Some(unloaded) = entry.to_unloaded() { - *entry = Arc::new(unloaded); - self.stats - .evictions - .entry(*id) - .and_modify(|c| saturating_add_assign!(*c, 1)) - .or_insert(1); - } else { - error!( - "Failed to create an unloaded cache entry for a program type {:?}", - entry.program - ); - } - } - } - } - - pub fn unload_all_programs(&mut self) { - let keys = self.entries.keys().copied().collect::>(); - keys.iter().for_each(|key| self.unload_program(key)); - } - /// This function removes the given entry for the given program from the cache. /// The function expects that the program and entry exists in the cache. Otherwise it'll panic. fn unload_program_entry(&mut self, program: &Pubkey, remove_entry: &Arc) { @@ -2390,6 +2379,52 @@ mod tests { assert!(match_missing(&missing, &program3, true)); } + #[test] + fn test_unloaded() { + let mut cache = new_mock_cache::(); + for loaded_program_type in [ + LoadedProgramType::FailedVerification(cache.environments.program_runtime_v1.clone()), + LoadedProgramType::Closed, + LoadedProgramType::DelayVisibility, // Never inserted in the global cache + LoadedProgramType::Unloaded(cache.environments.program_runtime_v1.clone()), + LoadedProgramType::Builtin(BuiltinProgram::new_mock()), + ] { + let entry = Arc::new(LoadedProgram { + program: loaded_program_type, + account_size: 0, + deployment_slot: 0, + effective_slot: 0, + tx_usage_counter: AtomicU64::default(), + ix_usage_counter: AtomicU64::default(), + latest_access_slot: AtomicU64::default(), + }); + assert!(entry.to_unloaded().is_none()); + + // Check that unload_program_entry() does nothing for this entry + let program_id = Pubkey::new_unique(); + cache.assign_program(program_id, entry.clone()); + cache.unload_program_entry(&program_id, &entry); + assert_eq!( + cache.entries.get(&program_id).unwrap().slot_versions.len(), + 1 + ); + assert!(cache.stats.evictions.is_empty()); + } + + let entry = new_test_loaded_program_with_usage(1, 2, AtomicU64::new(3)); + let unloaded_entry = entry.to_unloaded().unwrap(); + assert_eq!(unloaded_entry.deployment_slot, 1); + assert_eq!(unloaded_entry.effective_slot, 2); + assert_eq!(unloaded_entry.latest_access_slot.load(Ordering::Relaxed), 1); + assert_eq!(unloaded_entry.tx_usage_counter.load(Ordering::Relaxed), 3); + + // Check that unload_program_entry() does its work + let program_id = Pubkey::new_unique(); + cache.assign_program(program_id, entry.clone()); + cache.unload_program_entry(&program_id, &entry); + assert!(cache.stats.evictions.get(&program_id).is_some()); + } + #[test] fn test_fork_prune_find_first_ancestor() { let mut cache = new_mock_cache::(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 1a9f1d8bb..972d89551 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -4525,13 +4525,6 @@ impl Bank { balances } - pub fn clear_program_cache(&self) { - self.loaded_programs_cache - .write() - .unwrap() - .unload_all_programs(); - } - #[allow(clippy::type_complexity)] pub fn load_and_execute_transactions( &self,