Skip to main content

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}