|
2 | 2 | macro_rules! alloc_tests { |
3 | 3 | ( $TestRegion:path ) => { |
4 | 4 | use libc::c_void; |
5 | | - use std::sync::Arc; |
| 5 | + use rand::rngs::StdRng; |
| 6 | + use rand::{thread_rng, Rng, SeedableRng}; |
| 7 | + use std::sync::{Arc, Mutex}; |
6 | 8 | use $TestRegion as TestRegion; |
7 | | - use $crate::alloc::{host_page_size, Limits, MINSIGSTKSZ}; |
| 9 | + use $crate::alloc::{host_page_size, AllocStrategy, Limits, MINSIGSTKSZ}; |
8 | 10 | use $crate::context::{Context, ContextHandle}; |
9 | 11 | use $crate::error::Error; |
10 | 12 | use $crate::instance::InstanceInternal; |
@@ -774,6 +776,173 @@ macro_rules! alloc_tests { |
774 | 776 | assert_eq!(region.used_slots(), 0); |
775 | 777 | } |
776 | 778 |
|
| 779 | + /// This test exercises the AllocStrategy::Random. In this scenario, |
| 780 | + /// the Region has a single slot which is "randomly" allocated and then dropped. |
| 781 | + #[test] |
| 782 | + fn slot_counts_work_with_random_alloc() { |
| 783 | + let module = MockModuleBuilder::new() |
| 784 | + .with_heap_spec(ONE_PAGE_HEAP) |
| 785 | + .build(); |
| 786 | + let region = TestRegion::create(1, &LIMITS).expect("region created"); |
| 787 | + assert_eq!(region.capacity(), 1); |
| 788 | + assert_eq!(region.free_slots(), 1); |
| 789 | + assert_eq!(region.used_slots(), 0); |
| 790 | + |
| 791 | + let inst = region |
| 792 | + .new_instance_builder(module.clone()) |
| 793 | + .with_alloc_strategy(AllocStrategy::Random) |
| 794 | + .build() |
| 795 | + .expect("new_instance succeeds"); |
| 796 | + assert_eq!(region.capacity(), 1); |
| 797 | + assert_eq!(region.free_slots(), 0); |
| 798 | + assert_eq!(region.used_slots(), 1); |
| 799 | + |
| 800 | + drop(inst); |
| 801 | + assert_eq!(region.capacity(), 1); |
| 802 | + assert_eq!(region.free_slots(), 1); |
| 803 | + assert_eq!(region.used_slots(), 0); |
| 804 | + } |
| 805 | + |
| 806 | + /// This test exercises the AllocStrategy::CustomRandom. In this scenario, |
| 807 | + /// the Region has 10 slots which are randomly allocated up to capacity |
| 808 | + /// and then dropped. The test is executed 100 times to exercise the |
| 809 | + /// random nature of the allocation strategy. |
| 810 | + #[test] |
| 811 | + fn slot_counts_work_with_custom_random_alloc() { |
| 812 | + let mut master_rng = thread_rng(); |
| 813 | + let seed: u64 = master_rng.gen(); |
| 814 | + eprintln!( |
| 815 | + "Seeding slot_counts_work_with_custom_random_alloc() with {}", |
| 816 | + seed |
| 817 | + ); |
| 818 | + |
| 819 | + let rng: StdRng = SeedableRng::seed_from_u64(seed); |
| 820 | + let shared_rng = Arc::new(Mutex::new(rng)); |
| 821 | + |
| 822 | + for _ in 0..100 { |
| 823 | + let mut inst_vec = Vec::new(); |
| 824 | + let module = MockModuleBuilder::new() |
| 825 | + .with_heap_spec(ONE_PAGE_HEAP) |
| 826 | + .build(); |
| 827 | + let total_slots = 10; |
| 828 | + let region = TestRegion::create(total_slots, &LIMITS).expect("region created"); |
| 829 | + assert_eq!(region.capacity(), total_slots); |
| 830 | + assert_eq!(region.free_slots(), 10); |
| 831 | + assert_eq!(region.used_slots(), 0); |
| 832 | + |
| 833 | + // Randomly allocate all of the slots in the region. |
| 834 | + for i in 1..=total_slots { |
| 835 | + let inst = region |
| 836 | + .new_instance_builder(module.clone()) |
| 837 | + .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) |
| 838 | + .build() |
| 839 | + .expect("new_instance succeeds"); |
| 840 | + |
| 841 | + assert_eq!(region.capacity(), total_slots); |
| 842 | + assert_eq!(region.free_slots(), total_slots - i); |
| 843 | + assert_eq!(region.used_slots(), i); |
| 844 | + inst_vec.push(inst); |
| 845 | + } |
| 846 | + |
| 847 | + // It's not possible to allocate just one more. Try |
| 848 | + // it and affirm that the error is handled gracefully. |
| 849 | + let wont_inst = region |
| 850 | + .new_instance_builder(module.clone()) |
| 851 | + .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) |
| 852 | + .build(); |
| 853 | + assert!(wont_inst.is_err()); |
| 854 | + |
| 855 | + // Drop all of the slots in the region. |
| 856 | + for i in 1..=total_slots { |
| 857 | + drop(inst_vec.pop()); |
| 858 | + assert_eq!(region.capacity(), total_slots); |
| 859 | + assert_eq!(region.free_slots(), total_slots - (total_slots - i)); |
| 860 | + assert_eq!(region.used_slots(), total_slots - i); |
| 861 | + } |
| 862 | + |
| 863 | + // Allocate just one more to make sure the drops took place |
| 864 | + // and the Region has capacity again. |
| 865 | + region |
| 866 | + .new_instance_builder(module.clone()) |
| 867 | + .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) |
| 868 | + .build() |
| 869 | + .expect("new_instance succeeds"); |
| 870 | + } |
| 871 | + } |
| 872 | + |
| 873 | + /// This test exercises a mixed AllocStrategy. In this scenario, |
| 874 | + /// the Region has 10 slots which are randomly and linearly allocated |
| 875 | + /// up to capacity and then dropped. The test is executed 100 times to |
| 876 | + /// exercise the random nature of the allocation strategy. |
| 877 | + #[test] |
| 878 | + fn slot_counts_work_with_mixed_alloc() { |
| 879 | + let mut master_rng = thread_rng(); |
| 880 | + let seed: u64 = master_rng.gen(); |
| 881 | + eprintln!("Seeding slot_counts_work_with_mixed_alloc() with {}", seed); |
| 882 | + |
| 883 | + let rng: StdRng = SeedableRng::seed_from_u64(seed); |
| 884 | + let shared_rng = Arc::new(Mutex::new(rng)); |
| 885 | + |
| 886 | + for _ in 0..100 { |
| 887 | + let mut inst_vec = Vec::new(); |
| 888 | + let module = MockModuleBuilder::new() |
| 889 | + .with_heap_spec(ONE_PAGE_HEAP) |
| 890 | + .build(); |
| 891 | + let total_slots = 10; |
| 892 | + let region = TestRegion::create(total_slots, &LIMITS).expect("region created"); |
| 893 | + assert_eq!(region.capacity(), total_slots); |
| 894 | + assert_eq!(region.free_slots(), 10); |
| 895 | + assert_eq!(region.used_slots(), 0); |
| 896 | + |
| 897 | + // Allocate all of the slots in the region. |
| 898 | + for i in 1..=total_slots { |
| 899 | + let inst; |
| 900 | + if i % 2 == 0 { |
| 901 | + inst = region |
| 902 | + .new_instance_builder(module.clone()) |
| 903 | + .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) |
| 904 | + .build() |
| 905 | + .expect("new_instance succeeds"); |
| 906 | + } else { |
| 907 | + inst = region |
| 908 | + .new_instance_builder(module.clone()) |
| 909 | + .with_alloc_strategy(AllocStrategy::Linear) |
| 910 | + .build() |
| 911 | + .expect("new_instance succeeds"); |
| 912 | + } |
| 913 | + |
| 914 | + assert_eq!(region.capacity(), total_slots); |
| 915 | + assert_eq!(region.free_slots(), total_slots - i); |
| 916 | + assert_eq!(region.used_slots(), i); |
| 917 | + inst_vec.push(inst); |
| 918 | + } |
| 919 | + |
| 920 | + // It's not possible to allocate just one more. Try |
| 921 | + // it and affirm that the error is handled gracefully. |
| 922 | + let wont_inst = region |
| 923 | + .new_instance_builder(module.clone()) |
| 924 | + .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) |
| 925 | + .build(); |
| 926 | + assert!(wont_inst.is_err()); |
| 927 | + |
| 928 | + // Drop all of the slots in the region. |
| 929 | + for i in 1..=total_slots { |
| 930 | + drop(inst_vec.pop()); |
| 931 | + assert_eq!(region.capacity(), total_slots); |
| 932 | + assert_eq!(region.free_slots(), total_slots - (total_slots - i)); |
| 933 | + assert_eq!(region.used_slots(), total_slots - i); |
| 934 | + } |
| 935 | + |
| 936 | + // Allocate just one more to make sure the drops took place |
| 937 | + // and the Region has capacity again. |
| 938 | + region |
| 939 | + .new_instance_builder(module.clone()) |
| 940 | + .with_alloc_strategy(AllocStrategy::Linear) |
| 941 | + .build() |
| 942 | + .expect("new_instance succeeds"); |
| 943 | + } |
| 944 | + } |
| 945 | + |
777 | 946 | fn do_nothing_module() -> Arc<dyn Module> { |
778 | 947 | extern "C" fn do_nothing(_vmctx: *mut lucet_vmctx) -> () {} |
779 | 948 |
|
|
0 commit comments