diff --git a/docs/wasm-runtime-architecture.md b/docs/wasm-runtime-architecture.md index b0f4aba..c9ed59e 100644 --- a/docs/wasm-runtime-architecture.md +++ b/docs/wasm-runtime-architecture.md @@ -233,7 +233,14 @@ whose compilation outlasted the guest CPU budget would immediately trap in the following allocator/relocation call even though no guest loop consumed it. The proactive compiler and request workers coordinate through per-unit file -locks and a lock-protected demand-priority queue under `BIN_DIRECTORY`. Unit +locks and a lock-protected demand-priority queue under `BIN_DIRECTORY`. A small +priority-only compiler process drains that queue independently of the full-site +scanner. This matters when the scanner is already inside a long transitive C++ +compile: a requested stale component can rebuild immediately instead of waiting +for that unrelated compile to finish. Both compiler processes use the same +per-unit lock, so concurrent demand and scan discovery cannot publish duplicate +artifacts. The priority worker is idle when there is no demand and never scans +the site on its own. Unit compilation writes and validates a process-unique temporary wasm file, then publishes it with an atomic rename. When proactive compilation is enabled, read-only HTTP requests keep using the last complete artifact while requesting diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp index 9c2d090..80d23db 100644 --- a/src/linux_fastcgi.cpp +++ b/src/linux_fastcgi.cpp @@ -16,6 +16,7 @@ ServerState server_state; FastCGIServer server; pid_t proactive_compiler_pid = 0; +pid_t priority_compiler_pid = 0; // The central WS broker process: owns the WS port + every connection, forwards // renders to the worker pool over uce.sock, and applies ws_* commands flushed @@ -1217,6 +1218,39 @@ void proactive_compile_queue_push(StringList& queue, String file_name) queue.push_back(file_name); } +bool proactive_compile_unit(Request& context, String file_name, bool& source_missing) +{ + bool failed = false; + String wasm_path = server_state.config["BIN_DIRECTORY"] + file_name + ".wasm"; + if(compiler_unit_needs_recompile(&context, file_name, &source_missing)) + { + printf("(i) proactive compile %s\n", file_name.c_str()); + auto su = get_shared_unit(&context, file_name); + failed = !su || su->compiler_messages != ""; + if(su) + wasm_path = su->wasm_name; + } + else if(source_missing) + { + printf("(i) proactive compiler forget removed unit %s\n", file_name.c_str()); + compiler_untrack_known_unit(&context, file_name); + } + if(!source_missing && !failed && wasm_serialized_module_needs_refresh(wasm_path)) + { + printf("(i) proactive serialize %s\n", file_name.c_str()); + String serialize_error = wasm_serialize_module_artifact(wasm_path); + if(serialize_error != "") + { + printf("(!) proactive serialize failed for %s: %s\n", file_name.c_str(), serialize_error.c_str()); + failed = true; + } + } + context.session.clear(); + context.session_loaded_hash = ""; + clear_shared_unit_cache(server_state); + return(failed); +} + void run_proactive_compiler() { Request background_context; @@ -1317,39 +1351,13 @@ void run_proactive_compiler() auto retry_it = retry_after.find(file_name); if(retry_it != retry_after.end() && time_precise() < retry_it->second) continue; - bool failed = false; - String wasm_path = server_state.config["BIN_DIRECTORY"] + file_name + ".wasm"; - if(compiler_unit_needs_recompile(&background_context, file_name, &source_missing)) - { - printf("(i) proactive compile %s\n", file_name.c_str()); - auto su = get_shared_unit(&background_context, file_name); - failed = !su || su->compiler_messages != ""; - if(su) - wasm_path = su->wasm_name; - } - else if(source_missing) - { - printf("(i) proactive compiler forget removed unit %s\n", file_name.c_str()); - compiler_untrack_known_unit(&background_context, file_name); + bool failed = proactive_compile_unit(background_context, file_name, source_missing); + if(source_missing) retry_after.erase(file_name); - } - if(!source_missing && !failed && wasm_serialized_module_needs_refresh(wasm_path)) - { - printf("(i) proactive serialize %s\n", file_name.c_str()); - String serialize_error = wasm_serialize_module_artifact(wasm_path); - if(serialize_error != "") - { - printf("(!) proactive serialize failed for %s: %s\n", file_name.c_str(), serialize_error.c_str()); - failed = true; - } - } - if(!source_missing && failed) + else if(failed) retry_after[file_name] = time_precise() + failure_retry_interval; - else if(!source_missing) + else retry_after.erase(file_name); - background_context.session.clear(); - background_context.session_loaded_hash = ""; - clear_shared_unit_cache(server_state); usleep(250000); continue; } @@ -1371,11 +1379,61 @@ void run_proactive_compiler() } } +void run_priority_compiler() +{ + Request background_context; + background_context.server = &server_state; + set_active_request(background_context); + my_pid = getpid(); + close_inherited_server_sockets(); + signal(SIGSEGV, on_segfault); + signal(SIGABRT, on_segfault); + signal(SIGBUS, on_segfault); + signal(SIGILL, on_segfault); + signal(SIGFPE, on_segfault); + signal(SIGPIPE, SIG_IGN); + setpriority(PRIO_PROCESS, 0, 5); + while(!termination_signal_received) + { + auto units = compiler_take_priority_units(&background_context); + if(units.empty()) + { + usleep(100000); + continue; + } + for(auto& file_name : units) + { + if(termination_signal_received) + break; + bool source_missing = false; + proactive_compile_unit(background_context, file_name, source_missing); + } + } +} + bool proactive_compiler_alive() { return(proactive_compiler_pid > 0 && task_kill(proactive_compiler_pid, 0) == 0); } +pid_t spawn_compiler(const char* label, void (*runner)()) +{ + pid_t p = fork(); + if(p < 0) + { + perror((String("fork ") + label).c_str()); + return(0); + } + if(p == 0) + { + prctl(PR_SET_PDEATHSIG, SIGHUP); + runner(); + exit(0); + } + printf("(P) %s spawned: PID %i\n", label, p); + return(p); +} + void ensure_proactive_compiler() { if(!to_bool(server_state.config["PROACTIVE_COMPILE_ENABLED"], true)) @@ -1383,24 +1441,10 @@ void ensure_proactive_compiler() if(float_val(server_state.config["PROACTIVE_COMPILE_CHECK_INTERVAL"]) <= 0) return; - if(proactive_compiler_alive()) - return; - - pid_t p = fork(); - if(p < 0) - { - perror("fork proactive compiler"); - return; - } - if(p == 0) - { - prctl(PR_SET_PDEATHSIG, SIGHUP); - run_proactive_compiler(); - exit(0); - } - - proactive_compiler_pid = p; - printf("(P) proactive compiler spawned: PID %i\n", p); + if(!proactive_compiler_alive()) + proactive_compiler_pid = spawn_compiler("proactive compiler", run_proactive_compiler); + if(priority_compiler_pid <= 0 || task_kill(priority_compiler_pid, 0) != 0) + priority_compiler_pid = spawn_compiler("priority compiler", run_priority_compiler); } void listen_for_connections() @@ -1557,6 +1601,8 @@ int main(int argc, char** argv) { if(!proactive_compiler_alive()) proactive_compiler_pid = 0; + if(priority_compiler_pid > 0 && task_kill(priority_compiler_pid, 0) != 0) + priority_compiler_pid = 0; if(!termination_signal_received) ensure_proactive_compiler(); @@ -1581,6 +1627,8 @@ int main(int argc, char** argv) kill(worker.first, SIGTERM); if(proactive_compiler_pid > 0) kill(proactive_compiler_pid, SIGTERM); + if(priority_compiler_pid > 0) + kill(priority_compiler_pid, SIGTERM); if(ws_broker_pid > 0) kill(ws_broker_pid, SIGTERM); f64 drain_deadline = time_precise() + (f64)to_u64(server_state.config["WORKER_DRAIN_TIMEOUT_SECONDS"], 10);