From 888017b07f2e2442d8aa3f8e084605b10c72e46b Mon Sep 17 00:00:00 2001 From: Javier Sagredo Date: Mon, 1 Jun 2026 01:53:44 +0200 Subject: [PATCH] Failure-driven loop --- sula.pl | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/sula.pl b/sula.pl index 8b911d7..14dcbba 100755 --- a/sula.pl +++ b/sula.pl @@ -69,22 +69,41 @@ with_socket(Context, Kont, Kont2) :- ). with_connection_loop(Context, Socket, Kont) :- - catch( - setup_call_cleanup( - ( - log_msg("tcp", "Accepting connections...~n", []), - socket_server_accept(Socket, Client, S0, []), - log_msg("tcp", "Connected client ~q~n", [Client]) + % Failure-driven loop: Scryer has no heap GC and only reclaims the global + % stack on backtracking. Handling each connection inside `repeat`/`fail` + % means we backtrack to `repeat` after every request, which truncates the + % heap (including the whole file slurped by phrase_from_file) back to a + % fixed point. A plain recursive loop would never reclaim it and RSS would + % grow without bound. The if-then-else commits the connection handler's + % choicepoints whether it succeeds or fails, then `fail` triggers the + % heap-reclaiming backtrack to `repeat`. + % + % NOTE: a small residual growth (~4-8 KB/request) remains and is expected. + % Each connection allocates TCP + TLS stream objects in Scryer's arena, + % which is append-only and only freed at process exit (no incremental + % sweep, and backtracking does not reclaim it). This is a Scryer + % limitation, not a bug here; mitigate operationally (periodic restart or + % a systemd MemoryMax= with Restart=always). + repeat, + ( catch( + setup_call_cleanup( + ( + log_msg("tcp", "Accepting connections...~n", []), + socket_server_accept(Socket, Client, S0, []), + log_msg("tcp", "Connected client ~q~n", [Client]) + ), + with_tls_connection(S0, Context, Kont), + ( close(S0), + log_msg("tcp", "Closed connection for client ~q~n", [Client]) + ) ), - with_tls_connection(S0, Context, Kont), - ( close(S0), - log_msg("tcp", "Closed connection for client ~q~n", [Client]) - ) - ), - Error, - handle_conn_error(Error) + Error, + handle_conn_error(Error) + ) + -> true + ; true ), - with_connection_loop(Context, Socket, Kont). + fail. handle_conn_error(error(permission_error(open, source_sink, _), tls_server_negotiate/3)) :- !, log_msg("error", "TLS handshake failed~n", []). -- 2.54.0