torvyn_engine/config.rs
1//! Configuration types for the Torvyn Wasm engine.
2//!
3//! [`WasmtimeEngineConfig`] is the primary configuration struct that controls
4//! how the Wasmtime engine compiles and executes WebAssembly components.
5
6use std::path::PathBuf;
7
8/// Compilation strategy for WebAssembly code.
9///
10/// # Examples
11/// ```
12/// use torvyn_engine::CompilationStrategy;
13///
14/// let strategy = CompilationStrategy::Cranelift;
15/// assert_eq!(format!("{:?}", strategy), "Cranelift");
16/// ```
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub enum CompilationStrategy {
19 /// Cranelift — the default, mature optimizing compiler.
20 #[default]
21 Cranelift,
22 /// Winch — the baseline (fast-compile, slower-execute) compiler.
23 Winch,
24}
25
26/// Configuration for the Wasmtime-based Wasm engine.
27///
28/// Per Doc 02, Section 3.2: controls fuel, memory limits,
29/// compilation strategy, SIMD, caching, and parallelism.
30///
31/// # Invariants
32/// - `default_fuel` must be > 0 when `fuel_enabled` is true.
33/// - `max_memory_bytes` must be > 0.
34///
35/// # Examples
36/// ```
37/// use torvyn_engine::WasmtimeEngineConfig;
38///
39/// let config = WasmtimeEngineConfig::default();
40/// assert!(config.fuel_enabled);
41/// assert_eq!(config.max_memory_bytes, 16 * 1024 * 1024);
42/// ```
43#[derive(Clone, Debug)]
44pub struct WasmtimeEngineConfig {
45 /// Enable fuel-based CPU budgeting.
46 ///
47 /// When enabled, each component invocation is given a fuel allocation.
48 /// Components that exceed their budget are preempted.
49 pub fuel_enabled: bool,
50
51 /// Default fuel budget per component invocation.
52 ///
53 /// Per Doc 02, Section 3.2: default is 1,000,000.
54 pub default_fuel: u64,
55
56 /// Fuel yield interval for async cooperative preemption.
57 ///
58 /// When set, Wasmtime yields back to the async executor after
59 /// consuming this many fuel units, enabling cooperative multitasking.
60 /// A value of 0 disables yield-on-fuel.
61 pub fuel_yield_interval: u64,
62
63 /// Maximum linear memory per component instance (bytes).
64 ///
65 /// Per Doc 02, Section 3.2: default is 16 MiB.
66 pub max_memory_bytes: usize,
67
68 /// Maximum number of Wasm table elements per component instance.
69 pub max_table_elements: u32,
70
71 /// Maximum number of instances per store.
72 pub max_instances: u32,
73
74 /// Compilation strategy.
75 pub strategy: CompilationStrategy,
76
77 /// Enable Wasm SIMD instructions.
78 pub simd_enabled: bool,
79
80 /// Enable Wasm multi-memory proposal.
81 pub multi_memory: bool,
82
83 /// Compilation cache directory for serialized native code.
84 ///
85 /// When `Some`, compiled components are serialized to disk and
86 /// deserialized on subsequent loads, avoiding recompilation.
87 pub cache_dir: Option<PathBuf>,
88
89 /// Number of parallel compilation threads.
90 ///
91 /// `None` uses Wasmtime's default (number of CPUs).
92 pub compilation_threads: Option<usize>,
93
94 /// Stack size for Wasm execution (bytes).
95 ///
96 /// Default: 1 MiB. Controls the maximum call stack depth.
97 pub stack_size: usize,
98}
99
100impl Default for WasmtimeEngineConfig {
101 /// Returns the default engine configuration.
102 ///
103 /// # COLD PATH — called once at startup.
104 fn default() -> Self {
105 Self {
106 fuel_enabled: true,
107 default_fuel: 1_000_000,
108 fuel_yield_interval: 10_000,
109 max_memory_bytes: 16 * 1024 * 1024, // 16 MiB
110 max_table_elements: 10_000,
111 max_instances: 10,
112 strategy: CompilationStrategy::default(),
113 simd_enabled: true,
114 multi_memory: false,
115 cache_dir: None,
116 compilation_threads: None,
117 stack_size: 1024 * 1024, // 1 MiB
118 }
119 }
120}
121
122impl WasmtimeEngineConfig {
123 /// Validate the configuration, returning a list of problems.
124 ///
125 /// # COLD PATH — called once at startup.
126 ///
127 /// # Examples
128 /// ```
129 /// use torvyn_engine::WasmtimeEngineConfig;
130 ///
131 /// let config = WasmtimeEngineConfig::default();
132 /// assert!(config.validate().is_empty());
133 /// ```
134 pub fn validate(&self) -> Vec<String> {
135 let mut problems = Vec::new();
136
137 if self.fuel_enabled && self.default_fuel == 0 {
138 problems.push(
139 "default_fuel must be > 0 when fuel is enabled. \
140 Set a fuel budget (recommended: 1_000_000) or disable fuel."
141 .into(),
142 );
143 }
144
145 if self.max_memory_bytes == 0 {
146 problems.push("max_memory_bytes must be > 0.".into());
147 }
148
149 if self.stack_size < 64 * 1024 {
150 problems.push(
151 "stack_size is below 64 KiB — this will likely cause \
152 stack overflows in most components."
153 .into(),
154 );
155 }
156
157 problems
158 }
159}
160
161// ---------------------------------------------------------------------------
162// Tests
163// ---------------------------------------------------------------------------
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_default_config_is_valid() {
171 let config = WasmtimeEngineConfig::default();
172 let problems = config.validate();
173 assert!(
174 problems.is_empty(),
175 "default config should be valid: {:?}",
176 problems
177 );
178 }
179
180 #[test]
181 fn test_default_config_values() {
182 let config = WasmtimeEngineConfig::default();
183 assert!(config.fuel_enabled);
184 assert_eq!(config.default_fuel, 1_000_000);
185 assert_eq!(config.max_memory_bytes, 16 * 1024 * 1024);
186 assert_eq!(config.strategy, CompilationStrategy::Cranelift);
187 assert!(config.simd_enabled);
188 }
189
190 #[test]
191 fn test_validation_catches_zero_fuel() {
192 let config = WasmtimeEngineConfig {
193 default_fuel: 0,
194 ..WasmtimeEngineConfig::default()
195 };
196 let problems = config.validate();
197 assert_eq!(problems.len(), 1);
198 assert!(problems[0].contains("default_fuel"));
199 }
200
201 #[test]
202 fn test_validation_catches_zero_memory() {
203 let config = WasmtimeEngineConfig {
204 max_memory_bytes: 0,
205 ..WasmtimeEngineConfig::default()
206 };
207 let problems = config.validate();
208 assert_eq!(problems.len(), 1);
209 assert!(problems[0].contains("max_memory_bytes"));
210 }
211
212 #[test]
213 fn test_validation_catches_small_stack() {
214 let config = WasmtimeEngineConfig {
215 stack_size: 1024, // 1 KiB — too small
216 ..WasmtimeEngineConfig::default()
217 };
218 let problems = config.validate();
219 assert_eq!(problems.len(), 1);
220 assert!(problems[0].contains("stack_size"));
221 }
222
223 #[test]
224 fn test_validation_fuel_disabled_zero_ok() {
225 let config = WasmtimeEngineConfig {
226 fuel_enabled: false,
227 default_fuel: 0,
228 ..WasmtimeEngineConfig::default()
229 };
230 let problems = config.validate();
231 assert!(
232 problems.is_empty(),
233 "zero fuel should be OK when fuel is disabled"
234 );
235 }
236
237 #[test]
238 fn test_compilation_strategy_default() {
239 assert_eq!(
240 CompilationStrategy::default(),
241 CompilationStrategy::Cranelift
242 );
243 }
244}