@@ -180,12 +180,12 @@ impl LoadedProgram {
180
180
}
181
181
}
182
182
183
- pub fn new_tombstone ( deployment_slot : Slot ) -> Self {
183
+ pub fn new_tombstone ( slot : Slot ) -> Self {
184
184
Self {
185
185
program : LoadedProgramType :: Invalid ,
186
186
account_size : 0 ,
187
- deployment_slot,
188
- effective_slot : deployment_slot ,
187
+ deployment_slot : slot ,
188
+ effective_slot : slot ,
189
189
usage_counter : AtomicU64 :: default ( ) ,
190
190
}
191
191
}
@@ -219,8 +219,10 @@ pub enum LoadedProgramEntry {
219
219
}
220
220
221
221
impl LoadedPrograms {
222
- /// Inserts a single entry
223
- pub fn insert_entry ( & mut self , key : Pubkey , entry : Arc < LoadedProgram > ) -> LoadedProgramEntry {
222
+ /// Refill the cache with a single entry. It's typically called during transaction processing,
223
+ /// when the cache doesn't contain the entry corresponding to program `key`.
224
+ /// The function dedupes the cache, in case some other thread replenished the the entry in parallel.
225
+ pub fn replenish ( & mut self , key : Pubkey , entry : Arc < LoadedProgram > ) -> LoadedProgramEntry {
224
226
let second_level = self . entries . entry ( key) . or_insert_with ( Vec :: new) ;
225
227
let index = second_level
226
228
. iter ( )
@@ -239,29 +241,28 @@ impl LoadedPrograms {
239
241
LoadedProgramEntry :: WasVacant ( entry)
240
242
}
241
243
242
- /// Insert or replace the current entry with a tombstone.
243
- /// This behaves differently than `insert_entry()` in case there's currently a program at the
244
- /// `deploytment_slot`. It'll replace the current entry with a tombstone, whereas `insert_entry()`
245
- /// would retain and return the current entry.
246
- pub fn set_tombstone ( & mut self , key : Pubkey , deployment_slot : Slot ) -> Arc < LoadedProgram > {
247
- let tombstone = Arc :: new ( LoadedProgram :: new_tombstone ( deployment_slot) ) ;
244
+ /// Assign the program `entry` to the given `key` in the cache.
245
+ /// This is typically called when a deployed program is managed (upgraded/un/reddeployed) via
246
+ /// bpf loader instructions.
247
+ /// The program management is not expected to overlap with initial program deployment slot.
248
+ /// Note: Do not call this function to replenish cache with a missing entry. As that use-case can
249
+ /// cause the cache to have duplicates. Use `replenish()` API for that use-case.
250
+ pub fn assign_program ( & mut self , key : Pubkey , entry : Arc < LoadedProgram > ) -> Arc < LoadedProgram > {
248
251
let second_level = self . entries . entry ( key) . or_insert_with ( Vec :: new) ;
249
252
let index = second_level
250
253
. iter ( )
251
- . position ( |at| at. effective_slot >= tombstone . effective_slot ) ;
254
+ . position ( |at| at. effective_slot >= entry . effective_slot ) ;
252
255
if let Some ( index) = index {
253
256
let existing = second_level
254
257
. get ( index)
255
258
. expect ( "Missing entry, even though position was found" ) ;
256
- if existing. deployment_slot == tombstone. deployment_slot
257
- && existing. effective_slot >= tombstone. effective_slot
258
- {
259
- second_level. splice ( index..=index, [ tombstone. clone ( ) ] ) ;
260
- return tombstone;
261
- }
259
+ assert ! (
260
+ existing. deployment_slot != entry. deployment_slot
261
+ || existing. effective_slot != entry. effective_slot
262
+ ) ;
262
263
}
263
- second_level. insert ( index. unwrap_or ( second_level. len ( ) ) , tombstone . clone ( ) ) ;
264
- tombstone
264
+ second_level. insert ( index. unwrap_or ( second_level. len ( ) ) , entry . clone ( ) ) ;
265
+ entry
265
266
}
266
267
267
268
/// Before rerooting the blockstore this removes all programs of orphan forks
@@ -369,7 +370,7 @@ mod tests {
369
370
370
371
let mut cache = LoadedPrograms :: default ( ) ;
371
372
let program1 = Pubkey :: new_unique ( ) ;
372
- let tombstone = cache. set_tombstone ( program1, 10 ) ;
373
+ let tombstone = cache. assign_program ( program1, 10 ) ;
373
374
let second_level = & cache
374
375
. entries
375
376
. get ( & program1)
@@ -382,7 +383,7 @@ mod tests {
382
383
// Add a program at slot 50, and a tombstone for the program at slot 60
383
384
let program2 = Pubkey :: new_unique ( ) ;
384
385
assert ! ( matches!(
385
- cache. insert_entry ( program2, new_test_builtin_program( 50 , 51 ) ) ,
386
+ cache. replenish ( program2, new_test_builtin_program( 50 , 51 ) ) ,
386
387
LoadedProgramEntry :: WasVacant ( _)
387
388
) ) ;
388
389
let second_level = & cache
@@ -392,7 +393,7 @@ mod tests {
392
393
assert_eq ! ( second_level. len( ) , 1 ) ;
393
394
assert ! ( !second_level. get( 0 ) . unwrap( ) . is_tombstone( ) ) ;
394
395
395
- let tombstone = cache. set_tombstone ( program2, 60 ) ;
396
+ let tombstone = cache. assign_program ( program2, 60 ) ;
396
397
let second_level = & cache
397
398
. entries
398
399
. get ( & program2)
@@ -405,7 +406,7 @@ mod tests {
405
406
assert_eq ! ( tombstone. effective_slot, 60 ) ;
406
407
407
408
// Replace the program at slot 50 with a tombstone
408
- let tombstone = cache. set_tombstone ( program2, 50 ) ;
409
+ let tombstone = cache. assign_program ( program2, 50 ) ;
409
410
let second_level = & cache
410
411
. entries
411
412
. get ( & program2)
@@ -604,52 +605,52 @@ mod tests {
604
605
605
606
let program1 = Pubkey :: new_unique ( ) ;
606
607
assert ! ( matches!(
607
- cache. insert_entry ( program1, new_test_loaded_program( 0 , 1 ) ) ,
608
+ cache. replenish ( program1, new_test_loaded_program( 0 , 1 ) ) ,
608
609
LoadedProgramEntry :: WasVacant ( _)
609
610
) ) ;
610
611
assert ! ( matches!(
611
- cache. insert_entry ( program1, new_test_loaded_program( 10 , 11 ) ) ,
612
+ cache. replenish ( program1, new_test_loaded_program( 10 , 11 ) ) ,
612
613
LoadedProgramEntry :: WasVacant ( _)
613
614
) ) ;
614
615
assert ! ( matches!(
615
- cache. insert_entry ( program1, new_test_loaded_program( 20 , 21 ) ) ,
616
+ cache. replenish ( program1, new_test_loaded_program( 20 , 21 ) ) ,
616
617
LoadedProgramEntry :: WasVacant ( _)
617
618
) ) ;
618
619
619
620
// Test: inserting duplicate entry return pre existing entry from the cache
620
621
assert ! ( matches!(
621
- cache. insert_entry ( program1, new_test_loaded_program( 20 , 21 ) ) ,
622
+ cache. replenish ( program1, new_test_loaded_program( 20 , 21 ) ) ,
622
623
LoadedProgramEntry :: WasOccupied ( _)
623
624
) ) ;
624
625
625
626
let program2 = Pubkey :: new_unique ( ) ;
626
627
assert ! ( matches!(
627
- cache. insert_entry ( program2, new_test_loaded_program( 5 , 6 ) ) ,
628
+ cache. replenish ( program2, new_test_loaded_program( 5 , 6 ) ) ,
628
629
LoadedProgramEntry :: WasVacant ( _)
629
630
) ) ;
630
631
assert ! ( matches!(
631
- cache. insert_entry ( program2, new_test_loaded_program( 11 , 12 ) ) ,
632
+ cache. replenish ( program2, new_test_loaded_program( 11 , 12 ) ) ,
632
633
LoadedProgramEntry :: WasVacant ( _)
633
634
) ) ;
634
635
635
636
let program3 = Pubkey :: new_unique ( ) ;
636
637
assert ! ( matches!(
637
- cache. insert_entry ( program3, new_test_loaded_program( 25 , 26 ) ) ,
638
+ cache. replenish ( program3, new_test_loaded_program( 25 , 26 ) ) ,
638
639
LoadedProgramEntry :: WasVacant ( _)
639
640
) ) ;
640
641
641
642
let program4 = Pubkey :: new_unique ( ) ;
642
643
assert ! ( matches!(
643
- cache. insert_entry ( program4, new_test_loaded_program( 0 , 1 ) ) ,
644
+ cache. replenish ( program4, new_test_loaded_program( 0 , 1 ) ) ,
644
645
LoadedProgramEntry :: WasVacant ( _)
645
646
) ) ;
646
647
assert ! ( matches!(
647
- cache. insert_entry ( program4, new_test_loaded_program( 5 , 6 ) ) ,
648
+ cache. replenish ( program4, new_test_loaded_program( 5 , 6 ) ) ,
648
649
LoadedProgramEntry :: WasVacant ( _)
649
650
) ) ;
650
651
// The following is a special case, where effective slot is 4 slots in the future
651
652
assert ! ( matches!(
652
- cache. insert_entry ( program4, new_test_loaded_program( 15 , 19 ) ) ,
653
+ cache. replenish ( program4, new_test_loaded_program( 15 , 19 ) ) ,
653
654
LoadedProgramEntry :: WasVacant ( _)
654
655
) ) ;
655
656
0 commit comments