@@ -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