Signals in Lisp

Here’s the translation of the Go code to Lisp, with explanations in Markdown format suitable for Hugo:

Our program demonstrates how to handle Unix signals in Lisp. We’ll show how to gracefully handle signals like SIGTERM or SIGINT.

(defpackage :signals
  (:use :cl)
  (:import-from :sb-unix #:sigint #:sigterm)
  (:import-from :sb-sys #:enable-interrupt))

(in-package :signals)

(defun main ()
  ;; Create a channel to receive signal notifications
  (let ((sigs (make-channel)))
    
    ;; Register the channel to receive SIGINT and SIGTERM
    (enable-interrupt sigint 
      (lambda (signal code scp) 
        (declare (ignore code scp))
        (send-signal sigs signal)))
    (enable-interrupt sigterm
      (lambda (signal code scp)
        (declare (ignore code scp))
        (send-signal sigs signal)))

    ;; Create a channel to notify when we're done
    (let ((done (make-channel)))
      
      ;; Start a separate thread to handle signals
      (bt:make-thread
       (lambda ()
         (let ((sig (receive-signal sigs)))
           (format t "~%~A~%" sig)
           (send done t))))

      ;; Wait for the signal
      (format t "awaiting signal~%")
      (receive done)
      (format t "exiting~%"))))

(defun make-channel ()
  (let ((mutex (bt:make-lock))
        (condition (bt:make-condition-variable))
        value)
    (list mutex condition (lambda () value) (lambda (v) (setf value v)))))

(defun send-signal (channel signal)
  (destructuring-bind (mutex condition getter setter) channel
    (bt:with-lock-held (mutex)
      (funcall setter signal)
      (bt:condition-notify condition))))

(defun receive-signal (channel)
  (destructuring-bind (mutex condition getter setter) channel
    (bt:with-lock-held (mutex)
      (loop until (funcall getter)
            do (bt:condition-wait condition mutex))
      (let ((value (funcall getter)))
        (funcall setter nil)
        value))))

(defun send (channel value)
  (destructuring-bind (mutex condition getter setter) channel
    (bt:with-lock-held (mutex)
      (funcall setter value)
      (bt:condition-notify condition))))

(defun receive (channel)
  (destructuring-bind (mutex condition getter setter) channel
    (bt:with-lock-held (mutex)
      (loop until (funcall getter)
            do (bt:condition-wait condition mutex))
      (let ((value (funcall getter)))
        (funcall setter nil)
        value))))

In this Lisp version, we use the SBCL (Steel Bank Common Lisp) implementation which provides Unix signal handling capabilities. We create a simple channel mechanism to mimic Go’s channels for communication between threads.

The main function sets up signal handlers for SIGINT and SIGTERM. It then starts a separate thread to wait for and handle these signals. The main thread waits for a notification that a signal has been received before exiting.

When we run this program, it will block waiting for a signal. By typing Ctrl-C (which the terminal shows as ^C), we can send a SIGINT signal, causing the program to print the signal name and then exit.

$ sbcl --load signals.lisp
awaiting signal
^C
SIGINT
exiting

This example demonstrates how to handle signals in a Lisp program, allowing for graceful shutdown or other custom behaviors in response to system signals.