Pre-instantiate WASM core workers

This commit is contained in:
udo
2026-07-18 01:58:01 +00:00
parent f89e33e241
commit 72fd0dd53a
5 changed files with 103 additions and 219 deletions
+79 -28
View File
@@ -8,8 +8,7 @@
//
// Designed for amalgamation-include (like uce_lib.cpp): the including TU must
// provide String/DValue/ucb_encode/ucb_decode (types.cpp + dvalue.cpp) and
// wasm_trace.h. W4 includes this from the FastCGI worker build; the W3 CLI
// driver (w3_driver.cpp) is the first consumer.
// wasm_trace.h. The production FastCGI worker includes this through backend.cpp.
//
// Loader rules carried from the spike phases, now enforced as policy:
// - import discipline: units may import only env / GOT.mem / GOT.func
@@ -117,6 +116,14 @@ struct WasmUnitModuleLoadProfile
u64 classify_us = 0;
};
struct WasmInstancePreDeleter
{
void operator()(wasmtime_instance_pre_t* instance_pre) const
{
wasmtime_instance_pre_delete(instance_pre);
}
};
struct WasmWorkerConfig
{
String core_wasm_path = "bin/wasm/core.wasm";
@@ -602,8 +609,8 @@ static bool uce_job_cancel_value(u64 id)
// ---- module byte parsing (hardened; carried from the phase 3 spike) -------
// included into both w3_driver.cpp and the native server TU (via backend.cpp);
// file-scope helpers are static so each TU gets its own copy with no clash
// Included into the native server TU through backend.cpp. File-scope helpers
// remain static to keep their linkage local to the wasm backend object.
static bool wasm_read_uleb(const std::vector<u8>& buf, size_t& pos, size_t end, u64& out)
{
out = 0;
@@ -965,6 +972,7 @@ public:
wasmtime::Engine engine;
std::optional<wasmtime::Module> core_module;
std::optional<wasmtime::Linker> core_linker;
std::unique_ptr<wasmtime_instance_pre_t, WasmInstancePreDeleter> core_instance_pre;
struct CoreImport
{
String module;
@@ -1411,38 +1419,75 @@ public:
return(error);
worker.core_imports.push_back({ mod, name, false });
}
}
if(!worker.core_table_imported)
return("core does not import __indirect_function_table — rebuild core with --import-table");
std::vector<wasmtime::Extern> imports;
u32 total = worker.core_table_min + worker.cfg.table_headroom;
wasmtime::TableType table_type(wasmtime::ValType::funcref(), total, total);
auto table_created = wasmtime::Table::create(cx, table_type, wasmtime::Val(std::optional<wasmtime::Func>()));
if(!table_created)
return("table create failed: " + String(table_created.err().message()));
table.emplace(table_created.ok());
table_next_free = worker.core_table_min;
for(auto& import : worker.core_imports)
{
if(import.table)
if(!worker.core_table_imported)
{
imports.push_back(*table);
continue;
wasmtime_instance_pre_t* instance_pre = 0;
wasmtime_error_t* pre_error = wasmtime_linker_instantiate_pre(
worker.core_linker->capi(), module.capi(), &instance_pre);
if(pre_error)
return("core pre-instantiation failed: " + String(wasmtime::Error(pre_error).message()));
worker.core_instance_pre.reset(instance_pre);
}
}
std::vector<wasmtime::Extern> imports;
if(worker.core_table_imported)
{
u32 total = worker.core_table_min + worker.cfg.table_headroom;
wasmtime::TableType table_type(wasmtime::ValType::funcref(), total, total);
auto table_created = wasmtime::Table::create(cx, table_type, wasmtime::Val(std::optional<wasmtime::Func>()));
if(!table_created)
return("table create failed: " + String(table_created.err().message()));
table.emplace(table_created.ok());
table_next_free = worker.core_table_min;
for(auto& import : worker.core_imports)
{
if(import.table)
{
imports.push_back(*table);
continue;
}
auto host_import = worker.core_linker->get(cx, import.module, import.name);
if(!host_import)
return("core host import unavailable: " + import.module + "." + import.name);
imports.push_back(*host_import);
}
auto host_import = worker.core_linker->get(cx, import.module, import.name);
if(!host_import)
return("core host import unavailable: " + import.module + "." + import.name);
imports.push_back(*host_import);
}
hostcall_operation_slots = worker.hostcall_operation_names.size();
birth_import_us = phase_us();
auto created = wasmtime::Instance::create(cx, module, imports);
if(!created)
return("core instantiation failed: " + trap_text(created.err()));
core.emplace(created.ok());
if(worker.core_instance_pre)
{
wasmtime_instance_t instance;
wasm_trap_t* trap = 0;
wasmtime_error_t* instantiate_error = wasmtime_instance_pre_instantiate(
worker.core_instance_pre.get(), cx.capi(), &instance, &trap);
if(instantiate_error)
return("core instantiation failed: " + String(wasmtime::Error(instantiate_error).message()));
if(trap)
return("core instantiation trapped: " + String(wasmtime::Trap(trap).message()));
core.emplace(instance);
}
else
{
auto created = wasmtime::Instance::create(cx, module, imports);
if(!created)
return("core instantiation failed: " + trap_text(created.err()));
core.emplace(created.ok());
}
birth_instantiate_us = phase_us();
if(!table)
{
auto exported_table = core->get(cx, "__indirect_function_table");
if(!exported_table || !std::get_if<wasmtime::Table>(&*exported_table))
return("core does not export __indirect_function_table");
table.emplace(std::get<wasmtime::Table>(*exported_table));
auto grown = table->grow(cx, worker.cfg.table_headroom, wasmtime::Val(std::optional<wasmtime::Func>()));
if(!grown)
return("table grow failed: " + String(grown.err().message()));
table_next_free = (u32)grown.ok();
}
auto exported_memory = core->get(cx, "memory");
if(!exported_memory || !std::get_if<wasmtime::Memory>(&*exported_memory))
return("core does not export memory");
@@ -3706,6 +3751,12 @@ private:
}
};
inline String wasm_worker_prepare(WasmWorker& worker)
{
WasmWorkspace workspace(worker);
return(workspace.birth());
}
// ---- public entry: one request through one workspace -----------------------
inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_tree, const String& entry_source_path,