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:
+6
-53
@@ -100,63 +100,16 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
|
||||
return(true);
|
||||
}
|
||||
|
||||
// Decide, by source inspection, whether a page must stay on the native
|
||||
// backend for now (W5 surfaces not yet behind the membrane).
|
||||
//
|
||||
// Caveats this deliberately accepts:
|
||||
// - ENTRY-UNIT ONLY: a clean entry page that component()s into a native-only
|
||||
// unit is still served by wasm. With signals_based_traps off (see
|
||||
// make_engine) the worst case is a clean wasm error or a stubbed-empty
|
||||
// result, never a worker crash — so this is a best-effort routing hint,
|
||||
// not a guarantee. Full coverage is a W5 concern (un-stub the APIs).
|
||||
// - SUBSTRING match, intentionally conservative: a token like "xml_" also
|
||||
// matches an identifier or doc string containing it. That only ever
|
||||
// over-routes to native (always correct), never the reverse.
|
||||
static bool wasm_backend_native_fallback_uncached(Request* context, const String& entry_unit)
|
||||
{
|
||||
String source = file_get_contents(entry_unit);
|
||||
// Supported by the wasm core, intentionally NOT in this list:
|
||||
// - regex_* (host PCRE2 hostcall), xml_*/yaml_*/markdown_* (compiled in),
|
||||
// - unit_render()/component() (host resolver).
|
||||
// W7d: every former native-only surface now goes through the membrane —
|
||||
// zip_* (uce_host_zip), unit introspection (uce_host_units: unit_info/
|
||||
// unit_call/units_list), regex/xml/yaml/markdown, filesystem, sqlite,
|
||||
// background tasks, sleep, sockets/custom servers, memcache, mysql. No unit
|
||||
// source token forces native anymore, so there is nothing left to scan for.
|
||||
// (The native fallback now only covers cold/stale artifacts; W7e removes it.)
|
||||
(void)source;
|
||||
return(false);
|
||||
}
|
||||
|
||||
static bool wasm_backend_native_fallback_needed(Request* context, const String& entry_unit)
|
||||
{
|
||||
if(!context || !context->server || entry_unit == "")
|
||||
return(true);
|
||||
// Cache the verdict per process, keyed on path + mtime: the source scan is
|
||||
// otherwise a file read + 20 substring searches on every page request.
|
||||
struct CachedVerdict { time_t mtime; bool fallback; };
|
||||
static std::map<String, CachedVerdict> cache;
|
||||
struct stat st;
|
||||
time_t mtime = (stat(entry_unit.c_str(), &st) == 0) ? st.st_mtime : 0;
|
||||
auto it = cache.find(entry_unit);
|
||||
if(it != cache.end() && it->second.mtime == mtime)
|
||||
return(it->second.fallback);
|
||||
bool fallback = wasm_backend_native_fallback_uncached(context, entry_unit);
|
||||
cache[entry_unit] = { mtime, fallback };
|
||||
return(fallback);
|
||||
}
|
||||
|
||||
// True if this request should be served by the wasm backend. Falls through to
|
||||
// native when disabled, when init failed, or when the unit has no wasm artifact
|
||||
// (e.g. units skip-listed for try/catch — automatic, graceful fallback). Mode-
|
||||
// agnostic: the caller (page render / cli / serve_http) decides which entry to
|
||||
// route here; this only gates on config, fallback tokens, artifact, and worker.
|
||||
// True if this request should be served by the wasm backend. Every unit now
|
||||
// runs on wasm (W7d retired the last native-only surfaces), so this only gates
|
||||
// on config, the presence of a current wasm artifact, and a healthy worker —
|
||||
// the remaining native fallback exists solely to cover a cold/stale artifact
|
||||
// (a unit not yet compiled, or whose source is newer than its .wasm). W7e
|
||||
// removes that last fallback once cold/stale triggers an on-demand wasm compile.
|
||||
bool wasm_backend_should_handle(Request& request, const String& entry_unit)
|
||||
{
|
||||
if(!wasm_backend_configured(&request))
|
||||
return(false);
|
||||
if(wasm_backend_native_fallback_needed(&request, entry_unit))
|
||||
return(false);
|
||||
if(!wasm_artifact_exists(&request, entry_unit))
|
||||
return(false);
|
||||
if(wasm_backend_ensure_started(&request) != "")
|
||||
|
||||
Reference in New Issue
Block a user