sbcl executable crash when running a hunchentoot server

127 Views Asked by At

I want to create a http server with sbcl + hunchentoot. Everything goes well as long as I test the codes in a REPL or run the script file directly, but when I save the core as an executable, the server just crashes every time I try to access the web page of the hunchentoot server.

The testing code is fairly easy, in the server.lisp file, we have:

#!/usr/local/bin/sbcl --script

(require :asdf)
(asdf:load-system :hunchentoot)

(defpackage :http-server
  (:use :cl :hunchentoot))

(in-package :http-server)

(defvar *my-acceptor* (make-instance 'easy-acceptor :port 6161))

(defun main ()
  (format t "Starting http server on port ~a~%" (acceptor-port *my-acceptor*))
  (start *my-acceptor*)
  (handler-case
      (loop do (sleep 1000))
    (condition () nil)))

(defun hello ()
  (format nil "Hello, it works!"))

(push
 (create-prefix-dispatcher "/hello.html" #'hello)
 *dispatch-table*)

;; (sb-ext:save-lisp-and-die "http-server" :toplevel 'main :executable t)
(main)

Now, if I just run the script directly in the terminal, it works flawlessly. The hello.html page can be accessed. Then I change the last two lines to

(sb-ext:save-lisp-and-die "http-server" :toplevel 'main :executable t)
;; (main)

My intention is to run the script again, and it will generate an executable file http-server. Then we have problems.

I start the server by executing the executable file http-server, and every time I access "http://127.0.0.1:6161/hello.html", the http-server process just crashes, with following error messages:

Starting http server on port 6161
CORRUPTION WARNING in SBCL pid 206086 tid 206091:
Memory fault at 0x7f9c7c58b000 (pc=0x53617132 [code 0x53616f20+0x212 ID 0x43a3], fp=0x7f696af2dbc0, sp=0x7f696af2db68) tid 206091
The integrity of this image is possibly compromised.
Continuing with fingers crossed.
CORRUPTION WARNING in SBCL pid 206086 tid 206091:
Memory fault at 0x7f9c7c58b000 (pc=0x53617c03 [code 0x53617a30+0x1D3 ID 0x43a5], fp=0x7f696af2dc40, sp=0x7f696af2dc18) tid 206091
The integrity of this image is possibly compromised.
Continuing with fingers crossed.

Would some one please help me look into what is going on here? It just does not make sense the same codes work well in a REPL or run as a script, but do not work as an executable. Is there something wrong with how I generate the executable?

My OS is LinuxMint 21. sbcl version is 2.2.8, hunchentoot version is 1.3.0.

Many Thanks!

SOME UPDATES This issue can be easily reproduced with the latest SBCL 2.2.9 + latest hunchentoot 1.3.0. With a well prepared environment, just run the script and generate the executable file, and you are ready for the investigating. Since both SBCL and hunchentoot are very popular in the community, it just does not make sense we don't have an explanation of this problem.

ANOTHER UPDATE I change the method of generating the executable, and it WORKS this time. It turns out that I have to create the executable from a REPL, rather than use the script file above. I think it might relate to the shebang line, the "--script" will disable the ldb debugger, it is probably not an ideal way to generate executable. The lesson has been learned.

FINAL UPDATE Actually we still can generate the executable from a script file, as long as we don't use the "--script" option. For example, put these lines at the beginning of the script file, and it works (Remember remove the last two lines from the original script):

#|
# This is the correct way to generate the executable from this script file
sbcl --noinform --no-userinit --load "$0" --eval $'(sb-ext:save-lisp-and-die "http-server" :toplevel \'http-server::main :executable t)'
exit 0
|#
1

There are 1 best solutions below

0
Gwang-Jin Kim On

You could use Roswell.

Install Roswell following: https://towardsdatascience.com/how-to-set-up-common-lisp-ide-in-2021-5be70d88975b (I am the author of this article).

Generate a folder service and cd service into it.

I put your script into the file: service.lisp

(ql:quickload :hunchentoot)

(defpackage :http-server
  (:use :cl :hunchentoot))

(in-package :http-server)

(defvar *my-acceptor* (make-instance 'easy-acceptor :port 6161))

(defun main ()
  (format t "Starting http server on port ~a~%" (acceptor-port *my-acceptor*))
  (start *my-acceptor*)
  (handler-case
      (loop do (sleep 1000))
    (condition () nil)))

(defun hello ()
  (format nil "Hello, it works!"))

(push
 (create-prefix-dispatcher "/hello.html" #'hello)
 *dispatch-table*)

;; (sb-ext:save-lisp-and-die "http-server" :toplevel 'main :executable t)
(main)

Then, I run

$ ros init service

Which generates a file service.ros Which content I let be:

#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
  (ros:ensure-asdf)
  #+quicklisp(ql:quickload '() :silent t)
  )

(defpackage :ros.script.service.3874727090
  (:use :cl))
(in-package :ros.script.service.3874727090)

(defun main (&rest argv)
  (declare (ignorable argv))
  (load "service.lisp")
  (main))
;;; vim: set ft=lisp lisp:

Then, you have to install the script into roswell:

$ ros install service.ros

It will show a path. In my case sth like: ~/.roswell/bin/service. There is a linux program called service already. So I alias this: $ alias serveit="~/.roswell/bin/service"

Now I can fire: $ serveit

serveit
To load "hunchentoot":
  Load 1 ASDF system:
    hunchentoot
; Loading "hunchentoot"
....
Starting http server on port 6161

Roswell scripting is explained here: https://roswell.github.io/Roswell-as-a-Scripting-Environment.html