1 //! Generate instance limits for the pooling allocation strategy.
2 
3 use arbitrary::{Arbitrary, Unstructured};
4 use wasmtime::Enabled;
5 
6 /// Configuration for `wasmtime::PoolingAllocationStrategy`.
7 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
8 #[expect(missing_docs, reason = "self-describing field names")]
9 pub struct PoolingAllocationConfig {
10     pub total_component_instances: u32,
11     pub total_core_instances: u32,
12     pub total_memories: u32,
13     pub total_tables: u32,
14     pub total_stacks: u32,
15 
16     pub max_memory_size: usize,
17     pub table_elements: usize,
18 
19     pub component_instance_size: usize,
20     pub max_memories_per_component: u32,
21     pub max_tables_per_component: u32,
22 
23     pub core_instance_size: usize,
24     pub max_memories_per_module: u32,
25     pub max_tables_per_module: u32,
26 
27     pub table_keep_resident: usize,
28     pub linear_memory_keep_resident: usize,
29 
30     pub decommit_batch_size: usize,
31     pub max_unused_warm_slots: u32,
32 
33     pub async_stack_keep_resident: usize,
34 
35     pub memory_protection_keys: Enabled,
36     pub max_memory_protection_keys: usize,
37 
38     pub pagemap_scan: Enabled,
39 }
40 
41 impl PoolingAllocationConfig {
42     /// Convert the generated limits to Wasmtime limits.
configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions)43     pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {
44         cfg.opts.pooling_total_component_instances = Some(self.total_component_instances);
45         cfg.opts.pooling_total_core_instances = Some(self.total_core_instances);
46         cfg.opts.pooling_total_memories = Some(self.total_memories);
47         cfg.opts.pooling_total_tables = Some(self.total_tables);
48         cfg.opts.pooling_total_stacks = Some(self.total_stacks);
49 
50         cfg.opts.pooling_max_memory_size = Some(self.max_memory_size);
51         cfg.opts.pooling_table_elements = Some(self.table_elements);
52 
53         cfg.opts.pooling_max_component_instance_size = Some(self.component_instance_size);
54         cfg.opts.pooling_max_memories_per_component = Some(self.max_memories_per_component);
55         cfg.opts.pooling_max_tables_per_component = Some(self.max_tables_per_component);
56 
57         cfg.opts.pooling_max_core_instance_size = Some(self.core_instance_size);
58         cfg.opts.pooling_max_memories_per_module = Some(self.max_memories_per_module);
59         cfg.opts.pooling_max_tables_per_module = Some(self.max_tables_per_module);
60 
61         cfg.opts.pooling_table_keep_resident = Some(self.table_keep_resident);
62         cfg.opts.pooling_memory_keep_resident = Some(self.linear_memory_keep_resident);
63 
64         cfg.opts.pooling_decommit_batch_size = Some(self.decommit_batch_size);
65         cfg.opts.pooling_max_unused_warm_slots = Some(self.max_unused_warm_slots);
66 
67         cfg.opts.pooling_async_stack_keep_resident = Some(self.async_stack_keep_resident);
68 
69         cfg.opts.pooling_memory_protection_keys = Some(self.memory_protection_keys);
70         cfg.opts.pooling_max_memory_protection_keys = Some(self.max_memory_protection_keys);
71 
72         cfg.opts.pooling_pagemap_scan = Some(self.pagemap_scan);
73     }
74 }
75 
76 impl<'a> Arbitrary<'a> for PoolingAllocationConfig {
arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self>77     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
78         const MAX_COUNT: u32 = 100;
79         const MAX_TABLES: u32 = 100;
80         const MAX_MEMORIES: u32 = 100;
81         const MAX_ELEMENTS: usize = 1000;
82         const MAX_MEMORY_SIZE: usize = 10 * (1 << 20); // 10 MiB
83         const MAX_SIZE: usize = 1 << 20; // 1 MiB
84         const MAX_INSTANCE_MEMORIES: u32 = 10;
85         const MAX_INSTANCE_TABLES: u32 = 10;
86 
87         let total_memories = u.int_in_range(1..=MAX_MEMORIES)?;
88 
89         Ok(Self {
90             total_component_instances: u.int_in_range(1..=MAX_COUNT)?,
91             total_core_instances: u.int_in_range(1..=MAX_COUNT)?,
92             total_memories,
93             total_tables: u.int_in_range(1..=MAX_TABLES)?,
94             total_stacks: u.int_in_range(1..=MAX_COUNT)?,
95 
96             max_memory_size: u.int_in_range(0..=MAX_MEMORY_SIZE)?,
97             table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
98 
99             component_instance_size: u.int_in_range(0..=MAX_SIZE)?,
100             max_memories_per_component: u.int_in_range(1..=MAX_INSTANCE_MEMORIES)?,
101             max_tables_per_component: u.int_in_range(1..=MAX_INSTANCE_TABLES)?,
102 
103             core_instance_size: u.int_in_range(0..=MAX_SIZE)?,
104             max_memories_per_module: u.int_in_range(1..=MAX_INSTANCE_MEMORIES)?,
105             max_tables_per_module: u.int_in_range(1..=MAX_INSTANCE_TABLES)?,
106 
107             table_keep_resident: u.int_in_range(0..=1 << 20)?,
108             linear_memory_keep_resident: u.int_in_range(0..=1 << 20)?,
109 
110             decommit_batch_size: u.int_in_range(1..=1000)?,
111             max_unused_warm_slots: u.int_in_range(0..=total_memories + 10)?,
112 
113             async_stack_keep_resident: u.int_in_range(0..=1 << 20)?,
114 
115             memory_protection_keys: *u.choose(&[Enabled::Auto, Enabled::No])?,
116             max_memory_protection_keys: u.int_in_range(1..=20)?,
117 
118             pagemap_scan: *u.choose(&[Enabled::Auto, Enabled::No])?,
119         })
120     }
121 }
122