From b1856c1725970b31027383a7ae79c2140a62a896 Mon Sep 17 00:00:00 2001 From: udo Date: Sat, 13 Jun 2026 22:32:02 +0000 Subject: [PATCH] chore: narrow wasm backend entrypoint API --- src/linux_fastcgi.cpp | 8 ++------ src/wasm/backend.cpp | 39 +++++++++------------------------------ src/wasm/backend.h | 13 ++++--------- src/wasm/worker.cpp | 25 +++++++------------------ 4 files changed, 22 insertions(+), 63 deletions(-) diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp index 70bf78e..81a25b1 100644 --- a/src/linux_fastcgi.cpp +++ b/src/linux_fastcgi.cpp @@ -958,10 +958,10 @@ int handle_complete(FastCGIRequest& request) { // wasm invocation: Wasmtime uses host signals internally to implement // guest traps, and the native handler would otherwise turn a clean // guest trap into a native fatal signal. Sets failure_* on error. - auto serve_via_wasm = [&](const String& entry_unit, int32_t kind, const String& handler_name = "") { + auto serve_via_wasm = [&](const String& entry_unit, int32_t kind) { request_fault_active = 0; restore_request_fault_handlers(); - String wasm_error = wasm_backend_serve(request, entry_unit, kind, handler_name); + String wasm_error = wasm_backend_serve(request, entry_unit, kind); install_request_fault_handlers(); request_fault_active = 1; if(wasm_error != "") @@ -1251,10 +1251,6 @@ void custom_server_http_dispatcher_loop(String key) custom_server_dispatcher_key = key; close_inherited_server_sockets(); install_process_fault_handlers(); - // This broker is forked from a worker that may already hold a wasm engine; - // drop the inherited (unusable post-fork) engine so it re-inits its own. - wasm_backend_reset_after_fork(); - StringMap cfg = custom_server_read_config(key); if(cfg["type"] != "http" || cfg["bind"] == "") { diff --git a/src/wasm/backend.cpp b/src/wasm/backend.cpp index 03c9bee..bdde4aa 100644 --- a/src/wasm/backend.cpp +++ b/src/wasm/backend.cpp @@ -168,12 +168,11 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit) } // Serve a request through a wasm workspace using the unit handler selected by -// `kind` (page render / cli / serve_http). Populates the native Request +// `kind` (page render or cli). Populates the native Request // (status/headers/cookies/session/body) so the existing transport writes the // response unchanged. Returns "" on success, or a collapsed error/trace string // for the caller to route into the configured error page. -String wasm_backend_serve(Request& request, const String& entry_unit, int32_t kind = WasmWorkspace::RESOLVE_RENDER, - const String& handler_name = "") +String wasm_backend_serve(Request& request, const String& entry_unit, int32_t kind = WasmWorkspace::RESOLVE_RENDER) { DValue ctx; auto copy_map = [&](const StringMap& source, const char* key) { @@ -187,20 +186,19 @@ String wasm_backend_serve(Request& request, const String& entry_unit, int32_t ki copy_map(request.session, "session"); ctx["entry_unit"] = entry_unit; // Raw request body: cli_input() parses a JSON CLI payload from context.in, - // and ws_message() exposes the websocket frame from it — both need it carried - // into the workspace, not just the form-decoded post map. + // carried into the workspace, not just the form-decoded post map. ctx["in"] = request.in; - WasmResponse response = wasm_worker_serve(*g_wasm_worker, ctx, entry_unit, kind, handler_name); + WasmResponse response = wasm_worker_serve(*g_wasm_worker, ctx, entry_unit, kind); if(!response.ok) return(response.error == "" ? String("wasm workspace failed") : response.error); - // A cli/serve_http unit that does not export the requested handler is a 404, - // matching native compiler_invoke_cli ("CLI Entry Point Not Found"). For page - // render a missing handler is simply an empty body (native parity). - if(!response.handler_present && kind != WasmWorkspace::RESOLVE_RENDER) + // A cli unit that does not export __uce_cli is a 404, matching native + // compiler_invoke_cli ("CLI Entry Point Not Found"). For page render a + // missing handler is simply an empty body (native parity). + if(!response.handler_present && kind == WasmWorkspace::RESOLVE_CLI) { - request.set_status(404, kind == WasmWorkspace::RESOLVE_CLI ? "CLI Entry Point Not Found" : "Handler Not Found"); + request.set_status(404, "CLI Entry Point Not Found"); return(""); } @@ -258,22 +256,3 @@ void wasm_backend_shutdown() g_wasm_epoch_ticker = 0; } } - -// A connection-broker child (the custom-server HTTP dispatcher, the websocket -// exec child) is forked from a worker that may already have brought up its wasm -// engine. The Wasmtime engine and the epoch-ticker thread must not cross fork: -// the child inherits the pointer/flags but only the forking thread survives, so -// the ticker is a phantom and the engine state is unsafe. Reset the per-process -// statics so the child lazily initializes its own engine + ticker on first use. -// The inherited std::thread refers to a thread that does not exist in the child; -// discard the inherited pointer without touching the pointed-to object. Joining, -// deleting, or destroying a joinable std::thread after fork can terminate the -// child; the child will allocate its own ticker on first wasm use. -void wasm_backend_reset_after_fork() -{ - g_wasm_worker = 0; - g_wasm_init_attempted = false; - g_wasm_init_error = ""; - g_wasm_epoch_running.store(false); - g_wasm_epoch_ticker = 0; -} diff --git a/src/wasm/backend.h b/src/wasm/backend.h index 37c4154..5f182e4 100644 --- a/src/wasm/backend.h +++ b/src/wasm/backend.h @@ -9,14 +9,13 @@ #include // Resolve-kind selector for wasm_backend_serve, mirrored from -// WasmWorkspace::ResolveKind in worker.cpp. Kept in sync there; the values are -// the wasm unit-handler dispatch kinds the host loader understands. +// WasmWorkspace::ResolveKind in worker.cpp. Only the entry kinds actually wired +// to the wasm backend live here; WebSocket/serve_http re-enter when W7b/W7c land +// (via the broker→worker-pool forwarding model, not in-fork rendering). namespace wasm_kind { enum { RENDER = 1, CLI = 4, - WEBSOCKET = 5, - SERVE_HTTP = 6, }; } @@ -27,11 +26,7 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit); // Serve a request through a wasm workspace using the unit handler for `kind`, // populating the native Request. Returns "" on success or a collapsed error. String wasm_backend_serve(Request& request, const String& entry_unit, - int32_t kind = wasm_kind::RENDER, const String& handler_name = ""); + int32_t kind = wasm_kind::RENDER); // Join the per-process epoch ticker before the worker process exits. void wasm_backend_shutdown(); - -// Drop the inherited (post-fork unusable) engine so a broker child re-inits its -// own; call right after fork in a long-lived child. -void wasm_backend_reset_after_fork(); diff --git a/src/wasm/worker.cpp b/src/wasm/worker.cpp index cc1941b..ad6dff5 100644 --- a/src/wasm/worker.cpp +++ b/src/wasm/worker.cpp @@ -487,43 +487,32 @@ public: // provided, reports whether the unit actually exports that handler — the // caller maps a missing render to an empty body (native parity) and a // missing cli/serve handler to a 404. - String invoke_entry(const String& entry_source_path, int32_t kind, bool* handler_present = 0, - const String& handler_name = "") + String invoke_entry(const String& entry_source_path, int32_t kind, bool* handler_present = 0) { entry_dir = dir_of(entry_source_path); size_t unit_index = 0; String error = load_unit(entry_source_path, unit_index); if(error != "") return(error); - // Base handler export for the kind, plus the named suffix (serve_http / - // render / component can route to a named entry, e.g. __uce_serve_http_x). - String symbol = kind == RESOLVE_CLI ? "__uce_cli" - : kind == RESOLVE_WEBSOCKET ? "__uce_websocket" - : kind == RESOLVE_SERVE_HTTP ? "__uce_serve_http" - : "__uce_render"; - if(handler_name != "") - symbol += "_" + sanitize_symbol_suffix(handler_name); + String symbol = kind == RESOLVE_CLI ? "__uce_cli" : "__uce_render"; bool present = (bool)unit_func(unit_index, symbol); if(handler_present) *handler_present = present; if(!present) return(""); - // The core parses a "path:name" target into the named suffix; the bare - // path above loaded the module, this drives the handler resolution. - String target = handler_name == "" ? entry_source_path : entry_source_path + ":" + handler_name; // Invoke through the core so the entry gets the same ONCE() + dispatch // path as components (unit is pre-loaded above; resolution is cached). auto entry = core_func("uce_wasm_invoke_entry"); if(!entry) return("core does not export uce_wasm_invoke_entry"); int32_t guest_ptr = 0; - error = call_core("uce_alloc", { (int32_t)target.size() }, &guest_ptr); + error = call_core("uce_alloc", { (int32_t)entry_source_path.size() }, &guest_ptr); if(error != "" || guest_ptr == 0) return(error == "" ? String("guest uce_alloc failed for entry path") : error); - error = guest_write((u32)guest_ptr, target); + error = guest_write((u32)guest_ptr, entry_source_path); if(error != "") return(error); - auto result = entry->call(ctx(), { wasmtime::Val(guest_ptr), wasmtime::Val((int32_t)target.size()), wasmtime::Val(kind) }); + auto result = entry->call(ctx(), { wasmtime::Val(guest_ptr), wasmtime::Val((int32_t)entry_source_path.size()), wasmtime::Val(kind) }); call_core("uce_free", { guest_ptr }, 0); if(!result) return(trap_text(result.err())); @@ -1765,7 +1754,7 @@ private: // ---- public entry: one request through one workspace ----------------------- inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_tree, const String& entry_source_path, - int32_t kind = WasmWorkspace::RESOLVE_RENDER, const String& handler_name = "") + int32_t kind = WasmWorkspace::RESOLVE_RENDER) { WasmResponse response; WasmWorkspace workspace(worker); @@ -1776,7 +1765,7 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_ if(error == "") error = workspace.apply_context(context_tree); if(error == "") - error = workspace.invoke_entry(entry_source_path, kind, &response.handler_present, handler_name); + error = workspace.invoke_entry(entry_source_path, kind, &response.handler_present); if(error == "") error = workspace.collect(response); response.workspace_birth_us = workspace.workspace_birth_us;