diff --git a/docs/wasm-runtime-architecture.md b/docs/wasm-runtime-architecture.md index e2a3850..304a190 100644 --- a/docs/wasm-runtime-architecture.md +++ b/docs/wasm-runtime-architecture.md @@ -145,6 +145,25 @@ module lives in the configured writable cache root rather than beside the possibly root-owned deployed `core.wasm`; freshness still uses the deployed artifact's metadata. +Packaged deployments use systemd socket activation for FastCGI. The socket unit +owns `/run/uce/fastcgi.sock`; UCE validates and adopts the single named listener +after exec. The listener therefore remains connectable and queues requests while +the service and its post-fork workers restart. Direct launches without systemd +activation retain the existing configured Unix/TCP listener behavior. +On termination the parent asks render workers to close their listeners, finish +accepted connections within the bounded worker drain interval, and only then +exits. This prevents an accepted FastCGI request from being reset at handoff; +the socket unit queues later connections for the replacement workers. +The graceful signal handler belongs to the parent and render workers. Generic +`task()` children restore default termination signals after fork so +`task_kill()` and `server_stop()` retain their immediate stop contract. + +Epoch interruption measures uninterrupted guest CPU segments. The common +hostcall membrane re-arms the store deadline after every native call, excluding +blocking I/O, process waits, hashing, and other host work without weakening a +guest loop that makes no hostcalls. Keeping this at the membrane also covers new +hostcalls without per-import timeout bookkeeping. + `request_perf()` reports worker module-cache hits and misses and divides a miss into artifact lookup, wasm read, custom-section parse, serialized-module deserialization or wasm compilation, and immutable import classification. This diff --git a/scripts/deb/postinst b/scripts/deb/postinst index aa75b1d..d89f230 100644 --- a/scripts/deb/postinst +++ b/scripts/deb/postinst @@ -4,7 +4,8 @@ set -e if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload >/dev/null 2>&1 || true if [ "$1" = "configure" ]; then - systemctl enable uce.service >/dev/null 2>&1 || true + systemctl enable uce.socket uce.service >/dev/null 2>&1 || true + systemctl start uce.socket >/dev/null 2>&1 || true systemctl restart uce.service >/dev/null 2>&1 || true fi fi diff --git a/scripts/deb/prerm b/scripts/deb/prerm index 109b144..b0ad2fa 100644 --- a/scripts/deb/prerm +++ b/scripts/deb/prerm @@ -3,7 +3,7 @@ set -e if [ "$1" = "remove" ] || [ "$1" = "deconfigure" ]; then if command -v systemctl >/dev/null 2>&1; then - systemctl disable --now uce.service >/dev/null 2>&1 || true + systemctl disable --now uce.service uce.socket >/dev/null 2>&1 || true fi fi diff --git a/scripts/deb/uce.service b/scripts/deb/uce.service index bea986a..fde74c1 100644 --- a/scripts/deb/uce.service +++ b/scripts/deb/uce.service @@ -1,7 +1,8 @@ [Unit] Description=UCE FastCGI Runtime -After=network-online.target mariadb.service memcached.service +After=network-online.target mariadb.service memcached.service uce.socket Wants=network-online.target +Requires=uce.socket [Service] Type=simple @@ -10,12 +11,10 @@ RuntimeDirectory=uce StateDirectory=uce CacheDirectory=uce ExecStartPre=/usr/bin/mkdir -p /var/cache/uce/work /var/lib/uce/uploads /var/lib/uce/sessions -ExecStartPre=/usr/bin/rm -f /run/uce/fastcgi.sock ExecStart=/usr/lib/uce/bin/uce_fastcgi.linux.bin -ExecStopPost=/usr/bin/rm -f /run/uce/fastcgi.sock Restart=always RestartSec=2 -TimeoutStopSec=15 +TimeoutStopSec=30 KillMode=mixed StandardOutput=journal StandardError=journal diff --git a/scripts/deb/uce.socket b/scripts/deb/uce.socket new file mode 100644 index 0000000..1208be9 --- /dev/null +++ b/scripts/deb/uce.socket @@ -0,0 +1,15 @@ +[Unit] +Description=UCE FastCGI socket + +[Socket] +ListenStream=/run/uce/fastcgi.sock +FileDescriptorName=fastcgi +SocketUser=www-data +SocketGroup=www-data +SocketMode=0660 +DirectoryMode=0755 +Backlog=100 +Service=uce.service + +[Install] +WantedBy=sockets.target diff --git a/scripts/make_deb.sh b/scripts/make_deb.sh index 6a32fc3..3983d7f 100755 --- a/scripts/make_deb.sh +++ b/scripts/make_deb.sh @@ -205,6 +205,7 @@ bundle_wasmtime "$STAGE_DIR" write_packaged_settings "$STAGE_DIR/etc/uce/settings.cfg" "$WEBROOT" install -m 0644 "$DEB_ASSET_DIR/uce.service" "$STAGE_DIR/lib/systemd/system/uce.service" +install -m 0644 "$DEB_ASSET_DIR/uce.socket" "$STAGE_DIR/lib/systemd/system/uce.socket" install -m 0644 "$DEB_ASSET_DIR/conffiles" "$DEBIAN_DIR/conffiles" install -m 0755 "$DEB_ASSET_DIR/postinst" "$DEBIAN_DIR/postinst" install -m 0755 "$DEB_ASSET_DIR/prerm" "$DEBIAN_DIR/prerm" diff --git a/scripts/make_rpm.sh b/scripts/make_rpm.sh index 75cdf0a..be3878f 100755 --- a/scripts/make_rpm.sh +++ b/scripts/make_rpm.sh @@ -179,6 +179,7 @@ bundle_wasi_sdk "$STAGE_DIR" bundle_wasmtime "$STAGE_DIR" write_packaged_settings "$STAGE_DIR/etc/uce/settings.cfg" "$WEBROOT" install -m 0644 "$REPO_ROOT/scripts/deb/uce.service" "$STAGE_DIR/usr/lib/systemd/system/uce.service" +install -m 0644 "$REPO_ROOT/scripts/deb/uce.socket" "$STAGE_DIR/usr/lib/systemd/system/uce.socket" ( cd "$STAGE_DIR" @@ -225,13 +226,14 @@ cp -a %{_builddir}/$PACKAGE_NAME-$VERSION/. %{buildroot}/ %post if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload >/dev/null 2>&1 || true - systemctl enable uce.service >/dev/null 2>&1 || true + systemctl enable uce.socket uce.service >/dev/null 2>&1 || true + systemctl start uce.socket >/dev/null 2>&1 || true systemctl restart uce.service >/dev/null 2>&1 || true fi %preun if [ "\$1" = "0" ] && command -v systemctl >/dev/null 2>&1; then - systemctl disable --now uce.service >/dev/null 2>&1 || true + systemctl disable --now uce.service uce.socket >/dev/null 2>&1 || true fi %postun @@ -245,6 +247,7 @@ fi %config(noreplace) /etc/uce/settings.cfg /usr/lib/uce /usr/lib/systemd/system/uce.service +/usr/lib/systemd/system/uce.socket $WEBROOT %dir /var/cache/uce %dir /var/lib/uce diff --git a/scripts/run_cli_tests.sh b/scripts/run_cli_tests.sh index 5e3e40f..367c696 100755 --- a/scripts/run_cli_tests.sh +++ b/scripts/run_cli_tests.sh @@ -73,4 +73,5 @@ if [[ "$action" == "run" ]]; then scripts/test_mysql_epoch_refresh.sh scripts/test_mysql_persistent_pool.sh scripts/test_log_timeliness.sh + scripts/test_socket_activation.sh fi diff --git a/scripts/systemd/uce.service b/scripts/systemd/uce.service index 53cc91c..30995f5 100644 --- a/scripts/systemd/uce.service +++ b/scripts/systemd/uce.service @@ -1,7 +1,8 @@ [Unit] Description=UCE FastCGI Runtime -After=network-online.target mariadb.service memcached.service +After=network-online.target mariadb.service memcached.service uce.socket Wants=network-online.target +Requires=uce.socket [Service] Type=simple @@ -10,13 +11,11 @@ RuntimeDirectory=uce StateDirectory=uce CacheDirectory=uce ExecStartPre=/usr/bin/mkdir -p /var/cache/uce/work /var/lib/uce/uploads /var/lib/uce/sessions -ExecStartPre=/usr/bin/rm -f /run/uce/fastcgi.sock ExecStartPre=/usr/bin/bash /Code/uce.openfu.com/uce/scripts/build_linux.sh ExecStart=/Code/uce.openfu.com/uce/bin/uce_fastcgi.linux.bin -ExecStopPost=/usr/bin/rm -f /run/uce/fastcgi.sock Restart=always RestartSec=2 -TimeoutStopSec=15 +TimeoutStopSec=30 KillMode=mixed StandardOutput=journal StandardError=journal diff --git a/scripts/systemd/uce.socket b/scripts/systemd/uce.socket new file mode 100644 index 0000000..1208be9 --- /dev/null +++ b/scripts/systemd/uce.socket @@ -0,0 +1,15 @@ +[Unit] +Description=UCE FastCGI socket + +[Socket] +ListenStream=/run/uce/fastcgi.sock +FileDescriptorName=fastcgi +SocketUser=www-data +SocketGroup=www-data +SocketMode=0660 +DirectoryMode=0755 +Backlog=100 +Service=uce.service + +[Install] +WantedBy=sockets.target diff --git a/scripts/test_socket_activation.sh b/scripts/test_socket_activation.sh new file mode 100755 index 0000000..f23f798 --- /dev/null +++ b/scripts/test_socket_activation.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +service_name="${UCE_TEST_SERVICE:-uce.service}" +socket_name="${UCE_TEST_SOCKET_SERVICE:-uce.socket}" +http_host="${UCE_TEST_HTTP_HOST:-uce.openfu.com}" +http_path="${UCE_TEST_HTTP_PATH:-/info/}" +requests="${UCE_TEST_RESTART_REQUESTS:-100}" +statuses=$(mktemp) +trap 'rm -f "$statuses"' EXIT + +systemctl is-active --quiet "$socket_name" +systemctl is-active --quiet "$service_name" +socket_path=$(systemctl show -p Listen --value "$socket_name" | sed -n 's/ \+(Stream)$//p') +[[ -S "$socket_path" ]] +inode_before=$(stat -c %i "$socket_path") + +( + for ((i = 0; i < requests; i++)); do + curl -sS --max-time 15 -o /dev/null -w '%{http_code}\n' \ + -H "Host: $http_host" "http://127.0.0.1${http_path}" + done +) >"$statuses" & +load_pid=$! +sleep 0.05 +systemctl restart "$service_name" +wait "$load_pid" + +inode_after=$(stat -c %i "$socket_path") +[[ "$inode_after" = "$inode_before" ]] +[[ $(wc -l <"$statuses") -eq "$requests" ]] +if [[ $(grep -c '^200$' "$statuses") -ne "$requests" ]]; then + sort "$statuses" | uniq -c >&2 + exit 1 +fi +systemctl is-active --quiet "$socket_name" +systemctl is-active --quiet "$service_name" +echo "socket activation restart passed across $requests HTTP requests" diff --git a/src/fastcgi/src/fcgicc.cc b/src/fastcgi/src/fcgicc.cc index 7a09a24..e21d449 100644 --- a/src/fastcgi/src/fcgicc.cc +++ b/src/fastcgi/src/fcgicc.cc @@ -206,7 +206,6 @@ FastCGIServer::shutdown() { printf("Closing server socket %i\n", *it); close(*it); - sleep(1); } for (std::vector::iterator it = listen_unlink.begin(); @@ -223,7 +222,6 @@ FastCGIServer::shutdown() delete it->second; } - sleep(1); server_sockets.clear(); } @@ -264,6 +262,22 @@ FastCGIServer::listen(unsigned tcp_port) return(listen(tcp_port, "0.0.0.0")); } +int +FastCGIServer::adopt_listener(int socket_handle, char type) +{ + int accepting = 0; + socklen_t accepting_size = sizeof(accepting); + if(socket_handle < 0 || getsockopt(socket_handle, SOL_SOCKET, SO_ACCEPTCONN, + &accepting, &accepting_size) != 0 || !accepting) + throw std::runtime_error("inherited descriptor is not a listening socket"); + set_socket_nonblocking(socket_handle); + server_sockets.push_back(socket_handle); + server_socket_types[socket_handle] = type; + printf("(P) adopted #%i inherited %s listener\n", socket_handle, + type == 'F' ? "FastCGI" : "server"); + return(socket_handle); +} + int FastCGIServer::listen(unsigned tcp_port, const std::string& bind_address) { diff --git a/src/fastcgi/src/fcgicc.h b/src/fastcgi/src/fcgicc.h index 178e4b8..79a25d3 100644 --- a/src/fastcgi/src/fcgicc.h +++ b/src/fastcgi/src/fcgicc.h @@ -57,6 +57,7 @@ public: int listen_http(unsigned tcp_port, const std::string& bind_address); int listen_cli(const std::string& local_path); int listen(const std::string& local_path); + int adopt_listener(int socket_handle, char type = 'F'); void process(int timeout_ms = -1); // timeout_ms<0 blocks forever void process_forever(); diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp index 9ff7ffe..4d5849d 100644 --- a/src/lib/sys.cpp +++ b/src/lib/sys.cpp @@ -1549,6 +1549,12 @@ pid_t task(String key, std::function exec_after_spawn, u64 timeout) { close_locked_file(lock_fd); my_pid = getpid(); + // The FastCGI worker handles termination to drain accepted requests. + // Generic task children do not run that drain loop, so inheriting those + // handlers would turn task_kill(SIGTERM) into a no-op. + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); signal(SIGALRM, SIG_DFL); if(timeout > 0) alarm(timeout); diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp index 5eb9d62..08e949a 100644 --- a/src/linux_fastcgi.cpp +++ b/src/linux_fastcgi.cpp @@ -699,15 +699,7 @@ volatile bool termination_signal_received = false; void on_terminate(int sig) { - if(termination_signal_received) - return; termination_signal_received = true; - if(getpid() != parent_pid) - exit(1); - printf("Terminating... PID %i:%i\n", getpid(), parent_pid); - wasm_backend_shutdown(); - server.shutdown(); - exit(1); } void clear_shared_unit_cache(ServerState& state) @@ -1069,7 +1061,7 @@ void run_ws_broker() ws_broker.listen(server_state.config["WS_BROKER_SOCKET_PATH"]); chmod(server_state.config["WS_BROKER_SOCKET_PATH"].c_str(), S_IRWXU | S_IRGRP | S_IWGRP); } - for(;;) + while(!termination_signal_received) { ws_broker.process(50); ws_broker_drain_outbound(ws_broker_outbound_timeout_seconds); @@ -1254,7 +1246,7 @@ void run_proactive_compiler() } next_scan_at = time_precise(); - for(;;) + while(!termination_signal_received) { try { @@ -1397,10 +1389,21 @@ void listen_for_connections() printf("(P) wasm worker ready: PID %i in %.3f ms\n", getpid(), wasm_ms); else fprintf(stderr, "(!) wasm worker initialization failed: PID %i in %.3f ms: %s\n", getpid(), wasm_ms, wasm_error.c_str()); - for(;;) + while(!termination_signal_received) { server.process(-1); } + close_inherited_server_sockets(); + f64 drain_deadline = time_precise() + (f64)to_u64(server_state.config["WORKER_DRAIN_TIMEOUT_SECONDS"], 10); + while(!server.client_sockets.empty() && time_precise() < drain_deadline) + server.process(100); + if(!server.client_sockets.empty()) + fprintf(stderr, "(!) worker PID %i drain deadline reached with %zu client connections\n", + getpid(), server.client_sockets.size()); + else + printf("(P) worker PID %i drained cleanly\n", getpid()); + wasm_backend_shutdown(); + exit(server.client_sockets.empty() ? 0 : 1); } mode_t configured_socket_mode(String value, mode_t fallback) @@ -1424,6 +1427,25 @@ void chmod_configured_socket(String path, String mode_value, mode_t fallback) fprintf(stderr, "(!) Could not chmod socket %s to %04o: %s\n", path.c_str(), (unsigned int)mode, strerror(errno)); } +int systemd_fastcgi_listener() +{ + const char* listen_pid = getenv("LISTEN_PID"); + const char* listen_fds = getenv("LISTEN_FDS"); + if(!listen_pid || !listen_fds) + return(-1); + if(to_u64(listen_pid, 0) != (u64)getpid()) + return(-1); + if(to_u64(listen_fds, 0) != 1) + throw std::runtime_error("UCE requires exactly one systemd FastCGI listener"); + const char* names = getenv("LISTEN_FDNAMES"); + if(names && names[0] != '\0' && String(names) != "fastcgi") + throw std::runtime_error("systemd listener must be named fastcgi"); + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); + return(3); +} + StringMap redacted_server_config_for_log(const StringMap& config) { StringMap redacted = config; @@ -1443,13 +1465,16 @@ void init_base_process() server_state.config = make_server_settings(); server_state.config["COMPILER_SYS_PATH"] = cwd_get(); printf("Compiler base path: %s\n", server_state.config["COMPILER_SYS_PATH"].c_str()); + int inherited_fastcgi = systemd_fastcgi_listener(); + if(inherited_fastcgi >= 0) + server.adopt_listener(inherited_fastcgi, 'F'); if(server_state.config["FCGI_PORT"] != "") server.listen(int_val(server_state.config["FCGI_PORT"])); printf("%s\n", var_dump(redacted_server_config_for_log(server_state.config)).c_str()); - if(server_state.config["FCGI_SOCKET_PATH"] != "") + if(inherited_fastcgi < 0 && server_state.config["FCGI_SOCKET_PATH"] != "") { server.listen(server_state.config["FCGI_SOCKET_PATH"]); chmod_configured_socket(server_state.config["FCGI_SOCKET_PATH"], server_state.config["FCGI_SOCKET_MODE"], 0666); @@ -1469,7 +1494,9 @@ void init_base_process() mkdir(server_state.config["SESSION_PATH"]); signal(SIGCHLD, on_child_exit); + signal(SIGTERM, on_terminate); signal(SIGINT, on_terminate); + signal(SIGHUP, on_terminate); signal(SIGPIPE, SIG_IGN); srand(time()); } @@ -1489,7 +1516,7 @@ int main(int argc, char** argv) ensure_proactive_compiler(); ensure_ws_broker(); - for(;;) + while(!termination_signal_received) { if(!proactive_compiler_alive()) proactive_compiler_pid = 0; @@ -1512,5 +1539,22 @@ int main(int argc, char** argv) sleep(1); } + printf("(P) draining %zu workers before shutdown\n", workers.size()); + for(auto& worker : workers) + kill(worker.first, SIGTERM); + if(proactive_compiler_pid > 0) + kill(proactive_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); + while(!workers.empty() && time_precise() < drain_deadline) + { + on_child_exit(0); + usleep(10000); + } + for(auto& worker : workers) + kill(worker.first, SIGKILL); + server.shutdown(); + return 0; } diff --git a/src/wasm/worker.cpp b/src/wasm/worker.cpp index a2203ba..2eba71a 100644 --- a/src/wasm/worker.cpp +++ b/src/wasm/worker.cpp @@ -2106,6 +2106,11 @@ private: auto profiled = [self, callback, profile_name](Caller caller, Span args, Span results) mutable -> Result { auto started = std::chrono::steady_clock::now(); auto result = callback(caller, args, results); + // Epoch interruption limits guest CPU, not time spent in native I/O, + // process management, hashing, or other host work. Re-arm at the one + // membrane every hostcall crosses so newly added blocking imports + // cannot silently consume the next guest segment's budget. + caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); if(profile_name != "uce_host_request_perf") { u64 elapsed = (u64)std::chrono::duration_cast(std::chrono::steady_clock::now() - started).count(); @@ -2288,9 +2293,9 @@ private: if(mod == "env" && name == "uce_host_crypto_equal") return(add([self](Caller, Span args, Span results) -> Result { String a,b; self->hostcall_read(args[0].i32(), args[1].i32(), a); self->hostcall_read(args[2].i32(), args[3].i32(), b); results[0]=Val((int32_t)(crypto_equal_native(a,b)?1:0)); return(std::monostate()); })); if(mod == "env" && name == "uce_host_password_hash") - return(add([self](Caller caller, Span args, Span results) -> Result { String password; self->hostcall_read(args[0].i32(), args[1].i32(), password); String out=password_hash_native(password); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); + return(add([self](Caller, Span args, Span results) -> Result { String password; self->hostcall_read(args[0].i32(), args[1].i32(), password); String out=password_hash_native(password); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); if(mod == "env" && name == "uce_host_password_verify") - return(add([self](Caller caller, Span args, Span results) -> Result { String password,encoded; self->hostcall_read(args[0].i32(), args[1].i32(), password); self->hostcall_read(args[2].i32(), args[3].i32(), encoded); bool valid=password_verify_native(password,encoded); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)(valid?1:0)); return(std::monostate()); })); + return(add([self](Caller, Span args, Span results) -> Result { String password,encoded; self->hostcall_read(args[0].i32(), args[1].i32(), password); self->hostcall_read(args[2].i32(), args[3].i32(), encoded); bool valid=password_verify_native(password,encoded); results[0]=Val((int32_t)(valid?1:0)); return(std::monostate()); })); if(mod == "env" && name == "uce_host_password_needs_rehash") return(add([self](Caller, Span args, Span results) -> Result { String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); results[0]=Val((int32_t)(password_needs_rehash_native(encoded)?1:0)); return(std::monostate()); })); if(mod == "env" && name == "uce_host_log") @@ -2316,7 +2321,6 @@ private: } if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)out.size()); return(std::monostate()); })); @@ -2324,7 +2328,7 @@ private: return(add([self](Caller caller, Span args, Span results) -> Result { String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); String out; String stage_key="http:"+encoded; if(!self->hostcall_staged(stage_key,out)) { DValue req,response; String err; if(ucb_decode(encoded,req,&err)) response=uce_http_request_value(req); else response["error"]="http_request decode failed: "+err; out=ucb_encode(response); if(buf==0) self->hostcall_stage(stage_key,out); } - if(buf&&cap>=out.size()) self->hostcall_write(buf,out); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate()); + if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); if(mod == "env" && name == "uce_host_http_request_async") return(add([self](Caller, Span args, Span results) -> Result { String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); DValue req; String err; u64 id=0; if(ucb_decode(encoded,req,&err)) id=uce_http_spawn_spec(req); results[0]=Val((int64_t)id); return(std::monostate()); })); @@ -2335,7 +2339,6 @@ private: String out; String stage_key="shell_dv:"+encoded; if(!self->hostcall_staged(stage_key,out)) { DValue spec, response; String err; if(ucb_decode(encoded,spec,&err)) response=uce_shell_exec_spec(spec); else response["error"]="shell_exec spec decode failed: "+err; out=ucb_encode(response); if(buf==0) self->hostcall_stage(stage_key,out); } if(buf&&cap>=out.size()) self->hostcall_write(buf,out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); if(mod == "env" && name == "uce_host_shell_spawn") @@ -2347,7 +2350,7 @@ private: if(mod == "env" && name == "uce_host_job_result") return(add([self](Caller, Span args, Span results) -> Result { String out=ucb_encode(uce_job_result_value((u64)args[0].i64(), 100)); u32 cap=(u32)args[2].i32(); int32_t buf=args[1].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); if(mod == "env" && name == "uce_host_job_await") - return(add([self](Caller caller, Span args, Span results) -> Result { u64 timeout=std::min((u64)args[1].i64(), 30000); String out=ucb_encode(uce_job_result_value((u64)args[0].i64(), timeout)); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); + return(add([self](Caller, Span args, Span results) -> Result { u64 timeout=std::min((u64)args[1].i64(), 30000); String out=ucb_encode(uce_job_result_value((u64)args[0].i64(), timeout)); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); })); if(mod == "env" && name == "uce_host_job_cancel") return(add([](Caller, Span args, Span results) -> Result { results[0]=Val((int32_t)(uce_job_cancel_value((u64)args[0].i64())?1:0)); return(std::monostate()); })); if(mod == "env" && name == "uce_host_path_real") @@ -2823,7 +2826,6 @@ private: } if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)out.size()); return(std::monostate()); })); @@ -2922,7 +2924,6 @@ private: } if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)out.size()); return(std::monostate()); })); @@ -3033,7 +3034,6 @@ private: } if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)out.size()); return(std::monostate()); })); @@ -3074,7 +3074,6 @@ private: context->resources.sockets.push_back(fd); } results[0] = Val((int64_t)(fd > 0 ? fd : 0)); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); return(std::monostate()); })); if(mod == "env" && name == "uce_host_socket_close") @@ -3115,7 +3114,6 @@ private: } if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)out.size()); return(std::monostate()); })); @@ -3208,7 +3206,6 @@ private: unsigned int remaining = ::sleep((unsigned int)(usec / 1000000ull)); if(remaining != 0) { - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)remaining); return(std::monostate()); } @@ -3216,7 +3213,6 @@ private: } if(usec > 0) ::usleep((useconds_t)usec); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0] = Val((int32_t)0); return(std::monostate()); })); @@ -3275,7 +3271,6 @@ private: self->hostcall_write(args[6].i32(), resolved); } results[0] = Val(slot); - caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); return(std::monostate()); }));