W7e: wasm-preferred dispatch + compile-on-demand; harden unit ABI validator
- Dispatch (linux_fastcgi.cpp): route every request through wasm. On a cold/stale artifact, compile the unit on demand (get_shared_unit, forced) and serve wasm; native compiler_invoke* remains only as a fallback when wasm cannot be made ready (compile failure / backend disabled). Applies to the 4 handle_complete branches and the CLI socket path. - backend.cpp: delete the now-vestigial native-only fallback token gate (wasm_backend_native_fallback_*), empty since W7d; should_handle now gates on config + current artifact + healthy worker only. - check_unit_wasm.py: skip the defense-in-depth llvm-nm allocator scan when llvm-nm SIGSEGVs on a degenerate-but-valid module (e.g. a unit with no exported handlers). Fixes site/demo/empty.uce, the last unit that could not produce a .wasm; forbidden allocator *exports* are still rejected. A pi-assisted review caught that the compile-freshness check keys off the .so mtime only, so force_recompile is required to rebuild a missing/stale .wasm; a unit already cached in-process whose .wasm later vanished still uses native fallback (closed in W7e stage B). Native execution is otherwise bypassed for all real traffic. Verified: scripts/run_cli_tests.sh --include-wasm-kill -> 87 passed, 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+32
-4
@@ -368,6 +368,12 @@ int handle_cli_complete(FastCGIRequest& request)
|
||||
// The CLI socket path installs no native fault handler, so the
|
||||
// wasm worker (whose traps are signal-based) can run directly.
|
||||
String cli_unit = compiler_normalize_unit_path(&request, script_filename);
|
||||
// W7e: compile a cold/stale unit on demand instead of executing
|
||||
// native. force_recompile: the compile-needed check ignores the
|
||||
// .wasm (keys off .so mtime), so a missing/stale .wasm with a
|
||||
// current .so must be forced to rebuild — see wasm_ready note.
|
||||
if(!wasm_backend_should_handle(request, cli_unit))
|
||||
get_shared_unit(&request, cli_unit, true);
|
||||
if(wasm_backend_should_handle(request, cli_unit))
|
||||
{
|
||||
String wasm_error = wasm_backend_serve(request, cli_unit, "cli");
|
||||
@@ -477,6 +483,28 @@ int handle_complete(FastCGIRequest& request) {
|
||||
};
|
||||
|
||||
String entry_unit = compiler_normalize_unit_path(&request, request.params["SCRIPT_FILENAME"]);
|
||||
// W7e: every unit runs on wasm. When the artifact is missing or stale
|
||||
// (cold worker, or source edited since the last compile),
|
||||
// wasm_backend_should_handle() returns false; rather than fall back to
|
||||
// native execution we compile the unit on demand — get_shared_unit()
|
||||
// builds the .wasm side-module — and recheck. Native execution remains
|
||||
// only as a safety net for a genuine wasm-compile failure (or the wasm
|
||||
// backend being disabled), both of which leave should_handle() false.
|
||||
// Only reached on the cold/stale path (should_handle false); warm
|
||||
// requests short-circuit before this, so there is no steady-state
|
||||
// recompile cost. force_recompile is needed because the compile-needed
|
||||
// check keys off the .so mtime only (shared_unit_compile_check /
|
||||
// compiled_time ignores the .wasm), so an uncached unit with a current
|
||||
// .so but missing .wasm would otherwise not rebuild. NOTE: this does not
|
||||
// cover a unit already cached in-process whose .wasm later vanished
|
||||
// (compiler_reusable_cached_unit short-circuits before the recompile) —
|
||||
// that synthetic case still uses native fallback and is closed properly
|
||||
// in W7e stage B (make the .wasm part of the compile-freshness check).
|
||||
auto wasm_ready = [&](const String& unit) -> bool {
|
||||
if(!wasm_backend_should_handle(request, unit))
|
||||
get_shared_unit(&request, unit, true);
|
||||
return(wasm_backend_should_handle(request, unit));
|
||||
};
|
||||
if(request.params["UCE_WS"] == "1")
|
||||
{
|
||||
// A WS message the broker forwarded here: rebuild the connection
|
||||
@@ -498,14 +526,14 @@ int handle_complete(FastCGIRequest& request) {
|
||||
if(ucb_decode(state_raw, state, &e))
|
||||
request.connection = state;
|
||||
}
|
||||
if(wasm_backend_should_handle(request, entry_unit))
|
||||
if(wasm_ready(entry_unit))
|
||||
serve_via_wasm(entry_unit, "websocket");
|
||||
else
|
||||
compiler_invoke_websocket(&request, request.params["SCRIPT_FILENAME"]);
|
||||
}
|
||||
else if(request.resources.is_cli)
|
||||
{
|
||||
if(wasm_backend_should_handle(request, entry_unit))
|
||||
if(wasm_ready(entry_unit))
|
||||
serve_via_wasm(entry_unit, "cli");
|
||||
else
|
||||
compiler_invoke_cli(&request, request.params["SCRIPT_FILENAME"]);
|
||||
@@ -515,7 +543,7 @@ int handle_complete(FastCGIRequest& request) {
|
||||
// W7c: this runs in a normal worker (clean engine) — the custom-server
|
||||
// dispatcher forwarded the request here via FastCGI rather than render
|
||||
// wasm in its own fork (Wasmtime cannot be re-created across fork).
|
||||
if(wasm_backend_should_handle(request, entry_unit))
|
||||
if(wasm_ready(entry_unit))
|
||||
{
|
||||
String fn = request.params["UCE_SERVE_HTTP_FUNCTION"];
|
||||
serve_via_wasm(entry_unit, fn == "" ? String("serve_http") : "serve_http:" + fn);
|
||||
@@ -523,7 +551,7 @@ int handle_complete(FastCGIRequest& request) {
|
||||
else
|
||||
compiler_invoke_serve_http(&request, request.params["SCRIPT_FILENAME"], request.params["UCE_SERVE_HTTP_FUNCTION"]);
|
||||
}
|
||||
else if(wasm_backend_should_handle(request, entry_unit))
|
||||
else if(wasm_ready(entry_unit))
|
||||
serve_via_wasm(entry_unit, "render");
|
||||
else
|
||||
compiler_invoke(&request, request.params["SCRIPT_FILENAME"]);
|
||||
|
||||
Reference in New Issue
Block a user