Bound request-time Wasm compilation

This commit is contained in:
udo
2026-07-19 03:02:15 +00:00
parent a7576f3db7
commit d95fb38183
15 changed files with 1002 additions and 147 deletions
+69 -16
View File
@@ -6,6 +6,7 @@
// Minimal FastCGI client: connection brokers forward to a clean-engine worker.
#include "lib/fcgi_forward.h"
#include <csetjmp>
#include <chrono>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
@@ -43,6 +44,16 @@ static volatile sig_atomic_t request_fault_frame_count = 0;
void close_inherited_server_sockets();
u64 request_seed_from_time(f64 time_value);
using WasmInvocationClock = std::chrono::steady_clock;
u64 wasm_invocation_remaining_ms(WasmInvocationClock::time_point deadline)
{
auto now = WasmInvocationClock::now();
if(now >= deadline)
return(0);
return((u64)std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now).count());
}
void prepare_request_body_maps(Request& request);
Request* set_active_request(Request& request)
@@ -73,7 +84,8 @@ void clear_request_output(Request& request)
request.ob_start();
}
bool render_wasm_error_page(Request& request, String config_key, s32 status_code, String status_reason, DValue error_info)
bool render_wasm_error_page(Request& request, String config_key, s32 status_code, String status_reason, DValue error_info,
WasmInvocationClock::time_point invocation_deadline, u64 invocation_budget_ms)
{
if(!(request.params["REQUEST_METHOD"] != "" && request.ob && request.ob_stack.size() > 0))
return(false);
@@ -93,13 +105,22 @@ bool render_wasm_error_page(Request& request, String config_key, s32 status_code
request.header["Content-Type"] = first(request.server->config["CONTENT_TYPE"], "text/html; charset=utf-8");
String unit = compiler_normalize_unit_path(&request, unit_file);
bool compile_timed_out = false;
if(!wasm_backend_should_handle(request, unit))
get_shared_unit(&request, unit);
{
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
if(remaining_ms == 0)
compile_timed_out = true;
else
get_shared_unit_bounded(&request, unit, remaining_ms, &compile_timed_out);
}
ob_start();
String wasm_error = "";
if(wasm_backend_should_handle(request, unit))
wasm_error = wasm_backend_serve(request, unit, "render");
if(compile_timed_out)
wasm_error = "UCE_INVOCATION_TIMEOUT: wasm invocation exceeded " + std::to_string(invocation_budget_ms) + " ms";
else if(wasm_backend_should_handle(request, unit))
wasm_error = wasm_backend_serve(request, unit, "render", wasm_invocation_remaining_ms(invocation_deadline));
else
wasm_error = "error page wasm unit unavailable after compile: " + unit_file;
String html = ob_get_close();
@@ -122,7 +143,8 @@ bool render_wasm_error_page(Request& request, String config_key, s32 status_code
return(true);
}
void render_request_failure(Request& request, String title, String details, String trace, int status_code = 500)
void render_request_failure(Request& request, String title, String details, String trace,
WasmInvocationClock::time_point invocation_deadline, u64 invocation_budget_ms, int status_code = 500)
{
request.response_code = request_status_line(request, status_code, "Internal Server Error");
request.header.clear();
@@ -149,7 +171,8 @@ void render_request_failure(Request& request, String title, String details, Stri
error_info["signal_name"] = signal_name((int)request_fault_signal);
}
error_info["trace"] = trace;
if(render_wasm_error_page(request, "page_runtime_error", status_code, "Internal Server Error", error_info))
if(render_wasm_error_page(request, "page_runtime_error", status_code, "Internal Server Error", error_info,
invocation_deadline, invocation_budget_ms))
{
request.err = "UCE runtime error: " + title + (details != "" ? " (" + details + ")" : "");
restore_active_request(previous_context);
@@ -388,6 +411,8 @@ int handle_cli_complete(FastCGIRequest& request)
request.random_index = 0;
request.random_seed = request_seed_from_time(request.stats.time_start);
request.ob_start();
u64 invocation_budget_ms = wasm_backend_invocation_timeout_ms(request);
auto invocation_deadline = WasmInvocationClock::now() + std::chrono::milliseconds(invocation_budget_ms);
String method = trim(request.params["REQUEST_METHOD"]);
String command = trim(first(request.params["DOCUMENT_URI"], request.params["REQUEST_URI"]));
@@ -441,15 +466,26 @@ int handle_cli_complete(FastCGIRequest& request)
// been removed, so a unit that still cannot be served by wasm is a
// request failure instead of a fallback path.
SharedUnit* cli_compile_state = 0;
bool cli_compile_timed_out = false;
bool cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
if(!cli_wasm_ready)
{
cli_compile_state = get_shared_unit(&request, cli_unit);
cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
if(remaining_ms == 0)
cli_compile_timed_out = true;
else
cli_compile_state = get_shared_unit_bounded(&request, cli_unit, remaining_ms, &cli_compile_timed_out);
if(!cli_compile_timed_out)
cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
}
if(cli_wasm_ready)
if(cli_compile_timed_out)
{
String wasm_error = wasm_backend_serve(request, cli_unit, "cli");
request.set_status(500, "Internal Server Error");
print("UCE_INVOCATION_TIMEOUT: wasm invocation exceeded ", invocation_budget_ms, " ms\n");
}
else if(cli_wasm_ready)
{
String wasm_error = wasm_backend_serve(request, cli_unit, "cli", wasm_invocation_remaining_ms(invocation_deadline));
if(wasm_error != "")
{
request.set_status(500, "Internal Server Error");
@@ -476,11 +512,13 @@ int handle_cli_complete(FastCGIRequest& request)
}
catch(const std::exception& e)
{
render_request_failure(request, "uncaught exception during CLI request", e.what(), backtrace_capture(32, 1), 500);
render_request_failure(request, "uncaught exception during CLI request", e.what(), backtrace_capture(32, 1),
invocation_deadline, invocation_budget_ms, 500);
}
catch(...)
{
render_request_failure(request, "unknown uncaught exception during CLI request", "", backtrace_capture(32, 1), 500);
render_request_failure(request, "unknown uncaught exception during CLI request", "", backtrace_capture(32, 1),
invocation_deadline, invocation_budget_ms, 500);
}
for(auto &f : request.uploaded_files)
@@ -522,6 +560,8 @@ int handle_complete(FastCGIRequest& request) {
request.random_index = 0;
request.random_seed = request_seed_from_time(request.stats.time_start);
request.ob_start();
u64 invocation_budget_ms = wasm_backend_invocation_timeout_ms(request);
auto invocation_deadline = WasmInvocationClock::now() + std::chrono::milliseconds(invocation_budget_ms);
request_fault_request = &request;
request_fault_active = 1;
request_fault_signal = 0;
@@ -557,7 +597,7 @@ int handle_complete(FastCGIRequest& request) {
request_fault_active = 0;
restore_request_fault_handlers();
request.stats.wasm_backend_started = time_precise();
String wasm_error = wasm_backend_serve(request, entry_unit, handler);
String wasm_error = wasm_backend_serve(request, entry_unit, handler, wasm_invocation_remaining_ms(invocation_deadline));
request.stats.wasm_backend_finished = time_precise();
install_request_fault_handlers();
request_fault_active = 1;
@@ -605,13 +645,25 @@ int handle_complete(FastCGIRequest& request) {
// recheck. Native execution has been removed, so a unit that still cannot
// be served by wasm becomes a clean 500 request failure.
SharedUnit* entry_compile_state = 0;
bool entry_compile_timed_out = false;
auto wasm_ready = [&](const String& unit) -> bool {
if(wasm_backend_should_handle(request, unit))
return(true);
entry_compile_state = get_shared_unit(&request, unit);
return(wasm_backend_should_handle(request, unit));
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
if(remaining_ms == 0)
entry_compile_timed_out = true;
else
entry_compile_state = get_shared_unit_bounded(&request, unit, remaining_ms, &entry_compile_timed_out);
return(!entry_compile_timed_out && wasm_backend_should_handle(request, unit));
};
auto fail_wasm_unavailable = [&](const String& handler) {
if(entry_compile_timed_out)
{
failure_title = "wasm invocation timeout";
failure_details = "";
failure_trace = "UCE_INVOCATION_TIMEOUT: wasm invocation exceeded " + std::to_string(invocation_budget_ms) + " ms";
return;
}
failure_title = "wasm unit unavailable after compile";
String compile_error = entry_compile_state ? first(entry_compile_state->compiler_messages, entry_compile_state->compile_error_status) : String("");
failure_details = compile_error != "" ? compile_error : handler + " handler could not be served by wasm";
@@ -691,7 +743,8 @@ int handle_complete(FastCGIRequest& request) {
restore_request_fault_handlers();
if(failure_title != "")
render_request_failure(request, failure_title, failure_details, failure_trace, 500);
render_request_failure(request, failure_title, failure_details, failure_trace,
invocation_deadline, invocation_budget_ms, 500);
for( auto &f : request.uploaded_files)
{