Skip to content

Commit 049397d

Browse files
authored
Merge pull request #153 from technosophos/feat/load-modules-on-startup
Load modules at startup, and clone per request
2 parents 4d261cf + cb1b830 commit 049397d

File tree

4 files changed

+75
-68
lines changed

4 files changed

+75
-68
lines changed

src/dispatcher.rs

+54-26
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use std::net::SocketAddr;
2+
use std::path::Path;
3+
use std::sync::Arc;
24

35
use hyper::{
46
http::request::Parts,
57
Body, Request, Response, StatusCode,
68
};
79
use sha2::{Digest, Sha256};
810
use tracing::{instrument};
11+
use wasmtime::{Engine, Config};
912

1013
use crate::dynamic_route::{DynamicRoutes, interpret_routes};
1114
use crate::emplacer::Bits;
@@ -16,7 +19,7 @@ use crate::request::{RequestContext, RequestGlobalContext};
1619

1720
use crate::bindle_util::{WagiHandlerInfo};
1821
use crate::wagi_config::{LoadedHandlerConfiguration, ModuleMapConfigurationEntry};
19-
use crate::wasm_module::WasmModuleSource;
22+
use crate::wasm_module::CompiledWasmModule;
2023
use crate::wasm_runner::{RunWasmResult, prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance_if_present, WasmLinkOptions};
2124

2225
#[derive(Clone, Debug)]
@@ -89,12 +92,33 @@ impl RoutingTableEntry {
8992
self.route_pattern.is_match(uri_fragment)
9093
}
9194

92-
fn build_from_modules_toml(source: &Loaded<ModuleMapConfigurationEntry>) -> anyhow::Result<RoutingTableEntry> {
95+
/// Create a new Wasm Engine and configure it.
96+
fn new_engine(cache_config_path: &Path) -> anyhow::Result<Engine> {
97+
let mut config = Config::default();
98+
99+
// Enable multi memory and module linking support.
100+
config.wasm_multi_memory(true);
101+
config.wasm_module_linking(true);
102+
103+
if let Ok(p) = std::fs::canonicalize(cache_config_path) {
104+
config.cache_config_load(p)?;
105+
};
106+
107+
Engine::new(&config)
108+
}
109+
110+
fn compile_module(data: Arc<Vec<u8>>, cache_config_path: &Path) -> anyhow::Result<CompiledWasmModule> {
111+
let engine = RoutingTableEntry::new_engine(cache_config_path)?;
112+
let module = wasmtime::Module::new(&engine, &**data)?;
113+
Ok(CompiledWasmModule::Object(module, engine))
114+
}
115+
116+
fn build_from_modules_toml(source: &Loaded<ModuleMapConfigurationEntry>, global_context: &RequestGlobalContext,) -> anyhow::Result<RoutingTableEntry> {
93117
let route_pattern = RoutePattern::parse(&source.metadata.route);
94118

95-
let wasm_source = WasmModuleSource::Blob(source.content.clone());
119+
let wasm_module = RoutingTableEntry::compile_module(source.content.clone(), &global_context.cache_config_path)?;
96120
let wasm_route_handler = WasmRouteHandler {
97-
wasm_module_source: wasm_source,
121+
wasm_module_source: wasm_module,
98122
wasm_module_name: source.metadata.module.clone(),
99123
entrypoint: source.metadata.entrypoint.clone().unwrap_or_else(|| DEFAULT_ENTRYPOINT.to_owned()),
100124
volumes: source.metadata.volumes.clone().unwrap_or_default(),
@@ -109,25 +133,29 @@ impl RoutingTableEntry {
109133
})
110134
}
111135

112-
fn build_from_bindle_entry(source: &(WagiHandlerInfo, Bits)) -> Option<anyhow::Result<RoutingTableEntry>> {
136+
fn build_from_bindle_entry(source: &(WagiHandlerInfo, Bits), global_context: &RequestGlobalContext,) -> Option<anyhow::Result<RoutingTableEntry>> {
113137
let (wagi_handler, bits) = source;
114138

115139
let route_pattern = RoutePattern::parse(&wagi_handler.route);
116-
let wasm_source = WasmModuleSource::Blob(bits.wasm_module.clone());
117-
let wasm_route_handler = WasmRouteHandler {
118-
wasm_module_source: wasm_source,
119-
wasm_module_name: wagi_handler.parcel.label.name.clone(),
120-
entrypoint: wagi_handler.entrypoint.clone().unwrap_or_else(|| DEFAULT_ENTRYPOINT.to_owned()),
121-
volumes: bits.volume_mounts.clone(),
122-
allowed_hosts: wagi_handler.allowed_hosts.clone(),
123-
http_max_concurrency: None,
124-
};
125-
let handler_info = RouteHandler::Wasm(wasm_route_handler);
140+
match RoutingTableEntry::compile_module(bits.wasm_module.clone(), &global_context.cache_config_path) {
141+
Err(e) => Some(Err(e)), // Not clear what we are supposed to return here.
142+
Ok(wasm_module) => {
143+
let wasm_route_handler = WasmRouteHandler {
144+
wasm_module_source: wasm_module,
145+
wasm_module_name: wagi_handler.parcel.label.name.clone(),
146+
entrypoint: wagi_handler.entrypoint.clone().unwrap_or_else(|| DEFAULT_ENTRYPOINT.to_owned()),
147+
volumes: bits.volume_mounts.clone(),
148+
allowed_hosts: wagi_handler.allowed_hosts.clone(),
149+
http_max_concurrency: None,
150+
};
151+
let handler_info = RouteHandler::Wasm(wasm_route_handler);
126152

127-
Some(Ok(Self {
128-
route_pattern,
129-
handler_info,
130-
}))
153+
Some(Ok(Self {
154+
route_pattern,
155+
handler_info,
156+
}))
157+
}
158+
}
131159
}
132160

133161
fn inbuilt(path: &str, handler: RouteHandler) -> Self {
@@ -263,9 +291,9 @@ impl RoutingTable {
263291
pub fn build(source: &LoadedHandlerConfiguration, global_context: RequestGlobalContext) -> anyhow::Result<RoutingTable> {
264292
let user_entries = match source {
265293
LoadedHandlerConfiguration::ModuleMapFile(module_map_entries) =>
266-
Self::build_from_modules_toml(module_map_entries),
294+
Self::build_from_modules_toml(module_map_entries, &global_context),
267295
LoadedHandlerConfiguration::Bindle(bindle_entries) =>
268-
Self::build_from_bindle_entries(bindle_entries),
296+
Self::build_from_bindle_entries(bindle_entries, &global_context),
269297
}?;
270298
let full_user_entries = augment_dynamic_routes(user_entries, &global_context)?;
271299

@@ -278,18 +306,18 @@ impl RoutingTable {
278306
})
279307
}
280308

281-
fn build_from_modules_toml(module_map_entries: &[Loaded<ModuleMapConfigurationEntry>]) -> anyhow::Result<Vec<RoutingTableEntry>> {
309+
fn build_from_modules_toml(module_map_entries: &[Loaded<ModuleMapConfigurationEntry>], global_context: &RequestGlobalContext) -> anyhow::Result<Vec<RoutingTableEntry>> {
282310
// TODO: look for `_routes` function
283311
module_map_entries
284312
.iter()
285-
.map(|e| RoutingTableEntry::build_from_modules_toml(e))
313+
.map(|e| RoutingTableEntry::build_from_modules_toml(e, global_context))
286314
.collect()
287315
}
288316

289-
fn build_from_bindle_entries(bindle_entries: &[(WagiHandlerInfo, Bits)]) -> anyhow::Result<Vec<RoutingTableEntry>> {
317+
fn build_from_bindle_entries(bindle_entries: &[(WagiHandlerInfo, Bits)], global_context: &RequestGlobalContext) -> anyhow::Result<Vec<RoutingTableEntry>> {
290318
bindle_entries
291319
.iter()
292-
.filter_map(|e| RoutingTableEntry::build_from_bindle_entry(e))
320+
.filter_map(|e| RoutingTableEntry::build_from_bindle_entry(e, global_context))
293321
.collect()
294322
}
295323

@@ -318,7 +346,7 @@ fn augment_one_wasm_with_dynamic_routes(routing_table_entry: &RoutingTableEntry,
318346

319347
let ctx = build_wasi_context_for_dynamic_route_query(redirects.streams);
320348
let link_options = WasmLinkOptions::none();
321-
let (store, instance) = prepare_wasm_instance(global_context, ctx, &wasm_route_handler.wasm_module_source, link_options)?;
349+
let (store, instance) = prepare_wasm_instance(ctx, &wasm_route_handler.wasm_module_source, link_options)?;
322350

323351
match run_prepared_wasm_instance_if_present(instance, store, "_routes") {
324352
RunWasmResult::WasmError(e) => Err(e),

src/handlers.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::dispatcher::RoutePattern;
1616
use crate::http_util::{internal_error, parse_cgi_headers};
1717
use crate::request::{RequestContext, RequestGlobalContext};
1818

19-
use crate::wasm_module::WasmModuleSource;
19+
use crate::wasm_module::CompiledWasmModule;
2020
use crate::wasm_runner::{prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance, WasmLinkOptions};
2121

2222
#[derive(Clone, Debug)]
@@ -27,7 +27,7 @@ pub enum RouteHandler {
2727

2828
#[derive(Clone, Debug)]
2929
pub struct WasmRouteHandler {
30-
pub wasm_module_source: WasmModuleSource,
30+
pub wasm_module_source: CompiledWasmModule,
3131
pub wasm_module_name: String,
3232
pub entrypoint: String,
3333
pub volumes: HashMap<String, String>,
@@ -60,7 +60,7 @@ impl WasmRouteHandler {
6060

6161
let ctx = self.build_wasi_context_for_request(req, headers, redirects.streams)?;
6262

63-
let (store, instance) = self.prepare_wasm_instance(global_context, ctx)?;
63+
let (store, instance) = self.prepare_wasm_instance(ctx)?;
6464

6565
// Drop manually to get instantiation time
6666
drop(startup_span);
@@ -103,11 +103,11 @@ impl WasmRouteHandler {
103103
Ok(ctx)
104104
}
105105

106-
fn prepare_wasm_instance(&self, global_context: &RequestGlobalContext, ctx: WasiCtx) -> Result<(Store<WasiCtx>, Instance), Error> {
106+
fn prepare_wasm_instance(&self, ctx: WasiCtx) -> Result<(Store<WasiCtx>, Instance), Error> {
107107
debug!("Preparing Wasm instance.");
108108
let link_options = WasmLinkOptions::default()
109109
.with_http(self.allowed_hosts.clone(), self.http_max_concurrency);
110-
prepare_wasm_instance(global_context, ctx, &self.wasm_module_source, link_options)
110+
prepare_wasm_instance(ctx, &self.wasm_module_source, link_options)
111111
}
112112
}
113113

src/wasm_module.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,18 @@ use std::{fmt::Debug, sync::{Arc, RwLock}};
22

33
use wasi_common::pipe::{ReadPipe, WritePipe};
44
use wasmtime::*;
5-
use wasmtime_wasi::*;
65

76
// In future this might be pre-instantiated or something like that, so we will
87
// just abstract it to be safe.
98
#[derive(Clone)]
10-
pub enum WasmModuleSource {
11-
Blob(Arc<Vec<u8>>),
9+
pub enum CompiledWasmModule {
10+
Object(Module, Engine)
1211
}
1312

14-
impl WasmModuleSource {
15-
pub fn load_module(&self, store: &Store<WasiCtx>) -> anyhow::Result<wasmtime::Module> {
16-
match self {
17-
Self::Blob(bytes) => wasmtime::Module::new(store.engine(), &**bytes),
18-
}
19-
}
20-
}
21-
22-
impl Debug for WasmModuleSource {
13+
impl Debug for CompiledWasmModule {
2314
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2415
match self {
25-
Self::Blob(v) => f.write_fmt(format_args!("Blob(length={})", v.len())),
16+
Self::Object(m, _) => f.write_fmt(format_args!("Object(Module={:?})", m.name())),
2617
}
2718
}
2819
}

src/wasm_runner.rs

+12-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::path::Path;
21
use std::sync::{Arc, RwLock};
32

43
use wasi_common::pipe::{ReadPipe, WritePipe};
@@ -8,7 +7,7 @@ use wasmtime_wasi::*;
87
use tracing::debug;
98

109
use crate::request::RequestGlobalContext;
11-
use crate::wasm_module::WasmModuleSource;
10+
use crate::wasm_module::CompiledWasmModule;
1211

1312
const STDERR_FILE: &str = "module.stderr";
1413

@@ -80,39 +79,28 @@ pub fn prepare_stdio_streams(
8079
})
8180
}
8281

83-
pub fn new_store_and_engine(
84-
cache_config_path: &Path,
85-
ctx: WasiCtx,
86-
) -> Result<(Store<WasiCtx>, Engine), anyhow::Error> {
87-
let mut config = Config::default();
88-
89-
// Enable multi memory and module linking support.
90-
config.wasm_multi_memory(true);
91-
config.wasm_module_linking(true);
9282

93-
if let Ok(p) = std::fs::canonicalize(cache_config_path) {
94-
config.cache_config_load(p)?;
95-
};
96-
97-
let engine = Engine::new(&config)?;
98-
Ok((Store::new(&engine, ctx), engine))
83+
pub fn new_store(
84+
ctx: WasiCtx,
85+
engine: &Engine
86+
) -> Result<Store<WasiCtx>, anyhow::Error> {
87+
Ok(Store::new(engine, ctx))
9988
}
10089

10190
pub fn prepare_wasm_instance(
102-
global_context: &RequestGlobalContext,
10391
ctx: WasiCtx,
104-
wasm_module_source: &WasmModuleSource,
92+
wasm_module: &CompiledWasmModule,
10593
link_options: WasmLinkOptions,
10694
) -> Result<(Store<WasiCtx>, Instance), Error> {
107-
debug!("Creating store, engine, and linker.");
108-
let (mut store, engine) = new_store_and_engine(&global_context.cache_config_path, ctx)?;
95+
debug!("Cloning module object");
96+
let (module, engine) = match wasm_module { CompiledWasmModule::Object(m, e) => (m.clone(), e.clone()) };
97+
let mut store = new_store(ctx, &engine)?;
98+
99+
debug!("Configuring linker");
109100
let mut linker = Linker::new(&engine);
110101
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx)?;
111-
112102
link_options.apply_to(&mut linker)?;
113103

114-
debug!("loading module from store");
115-
let module = wasm_module_source.load_module(&store)?;
116104
debug!("instantiating module in linker");
117105
let instance = linker.instantiate(&mut store, &module)?;
118106
Ok((store, instance))

0 commit comments

Comments
 (0)