Recently I had to write a simple http service in prolog. A HTTP service just has to indefinitely listen for requests to come in and handle them. I found this surprisingly difficult to set up right though.
SWI-Prolog comes with a HTTP server bundled in:
❯ swipl -q
?- use_module(library(http/http_server)).
true.
?- http_server([port(4444)]).
true.
Now a HTTP server is running on port 4444. Great. However, it runs on a set of background threads, which go away the moment the toplevel finishes.
This means I can’t just start a HTTP server and do nothing else, as then the toplevel just immediately halts and takes the HTTP server with it:
❯ swipl -g 'use_module(library(http/http_server))' -g 'http_server:http_server([port(4444)])' -t halt
% Started server at http://localhost:4444/
>
instead I have to do something silly, like
❯ swipl -g 'use_module(library(http/http_server))' -g 'http_server:http_server([port(4444)])' -t 'sleep(10000000000)'
That will keep the service going for the next 300 years. But that can’t be the right way to do this.
Another trick (used by terminusdb) is to take one of the worker threads and thread_join
it like so:
main(_) :-
http_server([port(4444)]),
once(http_current_worker(4444, ThreadId)),
thread_join(ThreadId, _).
Since the worker threads presumably run indefinitely, so will the toplevel. Again, I can’t really say this sparks joy.
Worth mentioning here is probably SWI-Prolog -- library(http/http_unix_daemon): Run SWI-Prolog HTTP server as a Unix system daemon. This allows you to start a SWI-Prolog HTTP server which is immediately detached from the current console, allowing it to keep running after logout, and notably, not requiring any tricks to keep the process alive (though I don’t know how this is actually implemented, I assume it’s something with SWI-Prolog -- detach_IO/1).
This style of unix daemon is not really how things are now done in modern daemon management, cause we now have either something like systemd to manage a daemon, or daemons get containerized and run as PID1.
What I’m really looking for is a way to just suspend a thread, until a signal arrives. I’d like to do
main(_) :-
http_server([port(4444)]),
thread_suspend.
or
❯ swipl -g 'use_module(library(http/http_server))' -g 'http_server:http_server([port(4444)])' -t 'thread_suspend'
Does this exist? Or is there an easy technique that allows me to implement it myself with just prolog code?