Keep demand compilation responsive

This commit is contained in:
udo
2026-07-18 11:14:40 +00:00
parent 495a8ae5f0
commit 0e0c0b6ccc
2 changed files with 104 additions and 49 deletions
+8 -1
View File
@@ -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
+96 -48
View File
@@ -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);