Skip to content

Commit f69598f

Browse files
[loader-v2] More tests for global manager (aptos-labs#15563)
1 parent 5711729 commit f69598f

File tree

1 file changed

+230
-24
lines changed

1 file changed

+230
-24
lines changed

aptos-move/block-executor/src/code_cache_global_manager.rs

Lines changed: 230 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,15 @@ mod test {
312312
state_store::{state_key::StateKey, state_value::StateValue, MockStateView},
313313
};
314314
use claims::assert_ok;
315-
use std::collections::HashMap;
315+
use move_core_types::identifier::Identifier;
316+
use move_vm_types::{
317+
code::{mock_verified_code, MockExtension},
318+
loaded_data::runtime_types::StructIdentifier,
319+
};
320+
use std::{
321+
collections::HashMap,
322+
sync::atomic::{AtomicU64, Ordering},
323+
};
316324

317325
#[test]
318326
fn test_prefetch_existing_aptos_framework() {
@@ -346,19 +354,48 @@ mod test {
346354
assert_eq!(module_cache.num_modules(), 0);
347355
}
348356

349-
#[allow(dead_code)]
350-
fn state_view_with_changed_feature_flag(
351-
feature_flag: Option<FeatureFlag>,
352-
) -> MockStateView<StateKey> {
357+
fn add_struct_identifier<K, D, V, E>(manager: &mut ModuleCacheManager<K, D, V, E>, name: &str)
358+
where
359+
K: Hash + Eq + Clone,
360+
V: Deref<Target = Arc<D>>,
361+
E: WithSize,
362+
{
363+
assert_ok!(manager
364+
.environment
365+
.as_mut()
366+
.unwrap()
367+
.runtime_environment()
368+
.struct_name_to_idx_for_test(StructIdentifier {
369+
module: ModuleId::new(AccountAddress::ZERO, Identifier::new("m").unwrap()),
370+
name: Identifier::new(name).unwrap()
371+
}));
372+
}
373+
374+
fn assert_struct_name_index_map_size_eq<K, D, V, E>(
375+
manager: &ModuleCacheManager<K, D, V, E>,
376+
expected: usize,
377+
) where
378+
K: Hash + Eq + Clone,
379+
V: Deref<Target = Arc<D>>,
380+
E: WithSize,
381+
{
382+
let actual = assert_ok!(manager
383+
.environment
384+
.as_ref()
385+
.unwrap()
386+
.runtime_environment()
387+
.struct_name_index_map_size());
388+
assert_eq!(actual, expected);
389+
}
390+
391+
fn state_view_with_changed_feature_flag(feature_flag: FeatureFlag) -> MockStateView<StateKey> {
353392
// Tweak feature flags to force a different config.
354393
let mut features = Features::default();
355394

356-
if let Some(feature_flag) = feature_flag {
357-
if features.is_enabled(feature_flag) {
358-
features.disable(feature_flag);
359-
} else {
360-
features.enable(feature_flag);
361-
}
395+
if features.is_enabled(feature_flag) {
396+
features.disable(feature_flag);
397+
} else {
398+
features.enable(feature_flag);
362399
}
363400

364401
MockStateView::new(HashMap::from([(
@@ -368,28 +405,197 @@ mod test {
368405
}
369406

370407
#[test]
371-
fn test_check_ready_sets_transaction_slice_metadata() {
408+
fn test_check_ready() {
409+
let mut manager = ModuleCacheManager::new();
410+
assert_eq!(
411+
manager.transaction_slice_metadata,
412+
TransactionSliceMetadata::Unknown
413+
);
414+
assert_eq!(manager.module_cache.num_modules(), 0);
415+
372416
let state_view = MockStateView::empty();
373417
let config = BlockExecutorModuleCacheLocalConfig {
374418
prefetch_framework_code: false,
375-
max_module_cache_size_in_bytes: 8,
419+
max_module_cache_size_in_bytes: 32,
376420
max_struct_name_index_map_num_entries: 2,
377421
};
378422

423+
// Populate the cache for testing.
424+
manager
425+
.module_cache
426+
.insert(0, mock_verified_code(0, MockExtension::new(8)));
427+
manager
428+
.module_cache
429+
.insert(1, mock_verified_code(1, MockExtension::new(8)));
430+
manager
431+
.module_cache
432+
.insert(2, mock_verified_code(2, MockExtension::new(8)));
433+
434+
// Case 1: Initial set-up, modules should not be cached. Metadata and environment are set.
435+
let metadata_1 = TransactionSliceMetadata::block_from_u64(0, 1);
436+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_1));
437+
assert_eq!(manager.transaction_slice_metadata, metadata_1);
438+
assert!(manager.environment.is_some());
439+
assert_eq!(manager.module_cache.num_modules(), 0);
440+
441+
add_struct_identifier(&mut manager, "foo");
442+
assert_struct_name_index_map_size_eq(&manager, 1);
443+
manager
444+
.module_cache
445+
.insert(0, mock_verified_code(0, MockExtension::new(8)));
446+
manager
447+
.module_cache
448+
.insert(1, mock_verified_code(1, MockExtension::new(8)));
449+
assert_eq!(manager.module_cache.num_modules(), 2);
450+
451+
// Case 2: Different metadata => cache is flushed. Here we pass a deep copy of environment.
452+
let metadata_2 = TransactionSliceMetadata::block_from_u64(2, 3);
453+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_2));
454+
assert_eq!(manager.transaction_slice_metadata, metadata_2);
455+
assert!(manager.environment.is_some());
456+
assert_eq!(manager.module_cache.num_modules(), 0);
457+
assert_struct_name_index_map_size_eq(&manager, 0);
458+
459+
add_struct_identifier(&mut manager, "foo");
460+
add_struct_identifier(&mut manager, "bar");
461+
assert_struct_name_index_map_size_eq(&manager, 2);
462+
manager
463+
.module_cache
464+
.insert(0, mock_verified_code(0, MockExtension::new(8)));
465+
manager
466+
.module_cache
467+
.insert(1, mock_verified_code(1, MockExtension::new(8)));
468+
manager
469+
.module_cache
470+
.insert(2, mock_verified_code(2, MockExtension::new(8)));
471+
manager
472+
.module_cache
473+
.insert(3, mock_verified_code(3, MockExtension::new(8)));
474+
assert_eq!(manager.module_cache.num_modules(), 4);
475+
476+
// Case 3: Metadata follows immediately after and environment is the same. Cache is not
477+
// flushed.
478+
let metadata_3 = TransactionSliceMetadata::block_from_u64(3, 4);
479+
assert!(metadata_3.is_immediately_after(&metadata_2));
480+
481+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_3));
482+
assert_eq!(manager.transaction_slice_metadata, metadata_3);
483+
assert!(manager.environment.is_some());
484+
assert_eq!(manager.module_cache.num_modules(), 4);
485+
assert_eq!(manager.module_cache.size_in_bytes(), 32);
486+
assert_struct_name_index_map_size_eq(&manager, 2);
487+
488+
manager
489+
.module_cache
490+
.insert(4, mock_verified_code(4, MockExtension::new(8)));
491+
assert_eq!(manager.module_cache.num_modules(), 5);
492+
assert_eq!(manager.module_cache.size_in_bytes(), 40);
493+
494+
// Case 4: Too many modules cached.
495+
let metadata_4 = TransactionSliceMetadata::block_from_u64(4, 5);
496+
assert!(metadata_4.is_immediately_after(&metadata_3));
497+
498+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_4));
499+
assert_eq!(manager.transaction_slice_metadata, metadata_4);
500+
assert!(manager.environment.is_some());
501+
assert_eq!(manager.module_cache.num_modules(), 0);
502+
assert_struct_name_index_map_size_eq(&manager, 2);
503+
504+
manager
505+
.module_cache
506+
.insert(0, mock_verified_code(0, MockExtension::new(8)));
507+
manager
508+
.module_cache
509+
.insert(1, mock_verified_code(1, MockExtension::new(8)));
510+
assert_eq!(manager.module_cache.num_modules(), 2);
511+
512+
// Case 5: Environment changes.
513+
let metadata_5 = TransactionSliceMetadata::block_from_u64(5, 6);
514+
assert!(metadata_5.is_immediately_after(&metadata_4));
515+
516+
let state_view = state_view_with_changed_feature_flag(FeatureFlag::EMIT_FEE_STATEMENT);
517+
518+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_5));
519+
assert_eq!(manager.transaction_slice_metadata, metadata_5);
520+
assert!(manager.environment.is_some());
521+
assert_eq!(manager.module_cache.num_modules(), 0);
522+
assert_struct_name_index_map_size_eq(&manager, 0);
523+
524+
add_struct_identifier(&mut manager, "foo");
525+
add_struct_identifier(&mut manager, "bar");
526+
add_struct_identifier(&mut manager, "baz");
527+
assert_struct_name_index_map_size_eq(&manager, 3);
528+
manager
529+
.module_cache
530+
.insert(0, mock_verified_code(0, MockExtension::new(8)));
531+
manager
532+
.module_cache
533+
.insert(1, mock_verified_code(1, MockExtension::new(8)));
534+
assert_eq!(manager.module_cache.num_modules(), 2);
535+
assert_eq!(manager.module_cache.size_in_bytes(), 16);
536+
537+
// Case 6: Type cache is too large.
538+
let metadata_6 = TransactionSliceMetadata::block_from_u64(6, 5);
539+
assert!(metadata_6.is_immediately_after(&metadata_5));
540+
541+
assert_ok!(manager.check_ready(AptosEnvironment::new(&state_view), &config, metadata_6));
542+
assert_eq!(manager.transaction_slice_metadata, metadata_6);
543+
assert!(manager.environment.is_some());
544+
assert_eq!(manager.module_cache.num_modules(), 0);
545+
assert_struct_name_index_map_size_eq(&manager, 0);
546+
}
547+
548+
#[test]
549+
fn test_try_lock_inner_single_thread() {
379550
let manager = AptosModuleCacheManager::new();
380-
assert_eq!(
381-
manager.inner.lock().transaction_slice_metadata,
382-
TransactionSliceMetadata::Unknown
383-
);
384551

385-
let metadata_1 = TransactionSliceMetadata::block_from_u64(0, 1);
386-
assert_ok!(manager.try_lock(&state_view, &config, metadata_1));
387-
assert_eq!(manager.inner.lock().transaction_slice_metadata, metadata_1);
552+
let state_view = MockStateView::empty();
553+
let config = BlockExecutorModuleCacheLocalConfig::default();
554+
let metadata = TransactionSliceMetadata::block_from_u64(0, 1);
388555

389-
let metadata_2 = TransactionSliceMetadata::block_from_u64(1, 2);
390-
assert_ok!(manager.try_lock(&state_view, &config, metadata_2));
391-
assert_eq!(manager.inner.lock().transaction_slice_metadata, metadata_2);
556+
let guard = assert_ok!(manager.try_lock(&state_view, &config, metadata));
557+
assert!(matches!(guard, AptosModuleCacheManagerGuard::Guard { .. }));
392558
}
393559

394-
// TODO(loader_v2): Add more unit tests like with previous commits.
560+
#[test]
561+
fn test_try_lock_inner_multiple_threads() {
562+
let manager = Arc::new(AptosModuleCacheManager::new());
563+
564+
let state_view = Arc::new(MockStateView::empty());
565+
let config = Arc::new(BlockExecutorModuleCacheLocalConfig::default());
566+
let metadata = TransactionSliceMetadata::block_from_u64(0, 1);
567+
568+
let counter = Arc::new(AtomicU64::new(0));
569+
let num_threads = 8;
570+
let mut handles = Vec::with_capacity(num_threads);
571+
572+
for _ in 0..num_threads {
573+
let handle = std::thread::spawn({
574+
let manager = manager.clone();
575+
let state_view = state_view.clone();
576+
let config = config.clone();
577+
let counter = counter.clone();
578+
579+
move || {
580+
let guard = assert_ok!(manager.try_lock_inner(&state_view, &config, metadata));
581+
582+
// Wait for all threads to complete.
583+
counter.fetch_add(1, Ordering::SeqCst);
584+
loop {
585+
if counter.load(Ordering::SeqCst) == num_threads as u64 {
586+
break;
587+
}
588+
}
589+
if matches!(guard, AptosModuleCacheManagerGuard::Guard { .. }) {
590+
1
591+
} else {
592+
0
593+
}
594+
}
595+
});
596+
handles.push(handle);
597+
}
598+
let sum = handles.into_iter().map(|h| h.join().unwrap()).sum::<i32>();
599+
assert_eq!(sum, 1);
600+
}
395601
}

0 commit comments

Comments
 (0)