]> Repositorios git - sula.git/commitdiff
Failure-driven loop
authorJavier Sagredo <[email protected]>
Sun, 31 May 2026 23:53:44 +0000 (01:53 +0200)
committerJavier Sagredo <[email protected]>
Mon, 1 Jun 2026 00:00:12 +0000 (02:00 +0200)
sula.pl

diff --git a/sula.pl b/sula.pl
index 8b911d7b6f0214f29344934eecaa1accc3235c6b..14dcbbaf5870e33414ab4d3958f51094ea4bb705 100755 (executable)
--- 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", []).