wasmcloud_runtime/
runtime.rs1use crate::{experimental::Features, ComponentConfig};
2
3use core::fmt;
4use core::fmt::Debug;
5use core::time::Duration;
6
7use std::thread;
8
9use anyhow::Context;
10use wasmtime::{InstanceAllocationStrategy, PoolingAllocationConfig};
11
12pub const MAX_LINEAR_MEMORY: u64 = 256 * 1024 * 1024;
14pub const MAX_COMPONENT_SIZE: u64 = 50 * 1024 * 1024;
16pub const MAX_COMPONENTS: u32 = 10_000;
18
19#[derive(Clone, Default)]
21pub struct RuntimeBuilder {
22 engine_config: wasmtime::Config,
23 max_components: u32,
24 max_component_size: u64,
25 max_linear_memory: u64,
26 max_execution_time: Duration,
27 component_config: ComponentConfig,
28 force_pooling_allocator: bool,
29 experimental_features: Features,
30}
31
32impl RuntimeBuilder {
33 #[must_use]
35 pub fn new() -> Self {
36 let mut engine_config = wasmtime::Config::default();
37 engine_config.async_support(true);
38 engine_config.epoch_interruption(true);
39 engine_config.wasm_component_model(true);
40
41 Self {
42 engine_config,
43 max_components: MAX_COMPONENTS,
44 max_component_size: MAX_COMPONENT_SIZE,
47 max_linear_memory: MAX_LINEAR_MEMORY,
48 max_execution_time: Duration::from_secs(10 * 60),
49 component_config: ComponentConfig::default(),
50 force_pooling_allocator: false,
51 experimental_features: Features::default(),
52 }
53 }
54
55 #[must_use]
57 pub fn component_config(self, component_config: ComponentConfig) -> Self {
58 Self {
59 component_config,
60 ..self
61 }
62 }
63
64 #[must_use]
66 pub fn max_components(self, max_components: u32) -> Self {
67 Self {
68 max_components,
69 ..self
70 }
71 }
72
73 #[must_use]
75 pub fn max_component_size(self, max_component_size: u64) -> Self {
76 Self {
77 max_component_size,
78 ..self
79 }
80 }
81
82 #[must_use]
84 pub fn max_linear_memory(self, max_linear_memory: u64) -> Self {
85 Self {
86 max_linear_memory,
87 ..self
88 }
89 }
90
91 #[must_use]
95 pub fn max_execution_time(self, max_execution_time: Duration) -> Self {
96 Self {
97 max_execution_time: max_execution_time.max(Duration::from_secs(1)),
98 ..self
99 }
100 }
101
102 #[must_use]
104 pub fn force_pooling_allocator(self) -> Self {
105 Self {
106 force_pooling_allocator: true,
107 ..self
108 }
109 }
110
111 #[must_use]
113 pub fn experimental_features(self, experimental_features: Features) -> Self {
114 Self {
115 experimental_features,
116 ..self
117 }
118 }
119
120 #[allow(clippy::type_complexity)]
126 pub fn build(mut self) -> anyhow::Result<(Runtime, thread::JoinHandle<Result<(), ()>>)> {
127 let mut pooling_config = PoolingAllocationConfig::default();
128
129 let memories_per_component = 1;
134 let tables_per_component = 1;
135 let max_core_instances_per_component = 30;
136 let table_elements = 15000;
137
138 #[allow(clippy::cast_possible_truncation)]
139 pooling_config
140 .total_component_instances(self.max_components)
141 .total_core_instances(self.max_components)
142 .total_gc_heaps(self.max_components)
143 .total_stacks(self.max_components)
144 .max_component_instance_size(self.max_component_size as usize)
145 .max_core_instances_per_component(max_core_instances_per_component)
146 .max_tables_per_component(20)
147 .table_elements(table_elements)
148 .max_memories_per_component(max_core_instances_per_component * memories_per_component)
152 .total_memories(self.max_components * memories_per_component)
153 .total_tables(self.max_components * tables_per_component)
154 .max_memory_size(self.max_linear_memory as usize)
160 .linear_memory_keep_resident(10 * 1024)
162 .table_keep_resident(10 * 1024);
163 self.engine_config
164 .allocation_strategy(InstanceAllocationStrategy::Pooling(pooling_config));
165 let engine = match wasmtime::Engine::new(&self.engine_config)
166 .context("failed to construct engine")
167 {
168 Ok(engine) => engine,
169 Err(e) if self.force_pooling_allocator => {
170 anyhow::bail!("failed to construct engine with pooling allocator: {}", e)
171 }
172 Err(e) => {
173 tracing::warn!(err = %e, "failed to construct engine with pooling allocator, falling back to dynamic allocator which may result in slower startup and execution of components.");
174 self.engine_config
175 .allocation_strategy(InstanceAllocationStrategy::OnDemand);
176 wasmtime::Engine::new(&self.engine_config).context("failed to construct engine")?
177 }
178 };
179 let epoch = {
180 let engine = engine.weak();
181 thread::spawn(move || loop {
182 thread::sleep(Duration::from_secs(1));
183 let Some(engine) = engine.upgrade() else {
184 return Ok(());
185 };
186 engine.increment_epoch();
187 })
188 };
189 Ok((
190 Runtime {
191 engine,
192 component_config: self.component_config,
193 max_execution_time: self.max_execution_time,
194 experimental_features: self.experimental_features,
195 },
196 epoch,
197 ))
198 }
199}
200
201impl TryFrom<RuntimeBuilder> for (Runtime, thread::JoinHandle<Result<(), ()>>) {
202 type Error = anyhow::Error;
203
204 fn try_from(builder: RuntimeBuilder) -> Result<Self, Self::Error> {
205 builder.build()
206 }
207}
208
209#[derive(Clone)]
211pub struct Runtime {
212 pub(crate) engine: wasmtime::Engine,
213 pub(crate) component_config: ComponentConfig,
214 pub(crate) max_execution_time: Duration,
215 pub(crate) experimental_features: Features,
216}
217
218impl Debug for Runtime {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 f.debug_struct("Runtime")
221 .field("component_config", &self.component_config)
222 .field("runtime", &"wasmtime")
223 .field("max_execution_time", &"max_execution_time")
224 .finish_non_exhaustive()
225 }
226}
227
228impl Runtime {
229 #[allow(clippy::type_complexity)]
235 pub fn new() -> anyhow::Result<(Self, thread::JoinHandle<Result<(), ()>>)> {
236 Self::builder().try_into()
237 }
238
239 #[must_use]
241 pub fn builder() -> RuntimeBuilder {
242 RuntimeBuilder::new()
243 }
244
245 #[must_use]
247 pub fn version(&self) -> &'static str {
248 env!("CARGO_PKG_VERSION")
249 }
250
251 pub(crate) fn skip_feature_gated_instance(&self, instance: &str) -> bool {
253 match instance {
254 "wasmcloud:messaging/producer@0.3.0"
255 | "wasmcloud:messaging/request-reply@0.3.0"
256 | "wasmcloud:messaging/types@0.3.0"
257 if self.experimental_features.wasmcloud_messaging_v3 =>
258 {
259 true
260 }
261 "wasmcloud:identity/store@0.0.1"
262 if self.experimental_features.workload_identity_interface =>
263 {
264 true
265 }
266 _ => false,
267 }
268 }
269}