Closing Channels in Lisp
(defpackage :closing-channels
(:use :cl))
(in-package :closing-channels)
;; In this example we'll use a `jobs` channel to communicate work to be done
;; from the main thread to a worker thread. When we have no more jobs for
;; the worker we'll close the `jobs` channel.
(defun main ()
(let ((jobs (make-channel))
(done (make-channel)))
;; Here's the worker thread. It repeatedly receives from `jobs` with
;; `(multiple-value-bind (j more) (recv jobs) ...)`. In this special
;; 2-value form of receive, the `more` value will be `nil` if `jobs`
;; has been closed and all values in the channel have already been received.
;; We use this to notify on `done` when we've worked all our jobs.
(bt:make-thread
(lambda ()
(loop
(multiple-value-bind (j more) (recv jobs)
(if more
(format t "received job ~A~%" j)
(progn
(format t "received all jobs~%")
(send done t)
(return)))))))
;; This sends 3 jobs to the worker over the `jobs` channel, then closes it.
(dotimes (j 3)
(send jobs (1+ j))
(format t "sent job ~A~%" (1+ j)))
(close-channel jobs)
(format t "sent all jobs~%")
;; We await the worker using the synchronization approach we saw earlier.
(recv done)
;; Reading from a closed channel succeeds immediately, returning NIL.
;; The optional second return value is T if the value received was
;; delivered by a successful send operation to the channel, or NIL if
;; it was generated because the channel is closed and empty.
(multiple-value-bind (val ok) (recv jobs)
(declare (ignore val))
(format t "received more jobs: ~A~%" ok))))
;; To run the program:
;; (main)
This Lisp code demonstrates the concept of closing channels. Here’s a breakdown of the translation:
We define a package
closing-channels
to encapsulate our code.The
main
function creates two channels:jobs
anddone
.We create a worker thread using
bt:make-thread
. This thread continuously receives from thejobs
channel until it’s closed.The main thread sends 3 jobs to the worker, then closes the
jobs
channel.We use
(recv done)
to wait for the worker to finish processing all jobs.Finally, we attempt to receive from the closed
jobs
channel to demonstrate that it returns NIL and indicates that the channel is closed.
Note that Lisp doesn’t have built-in channels, so this code assumes the existence of a channel implementation with make-channel
, send
, recv
, and close-channel
functions. You would need to implement these or use a library that provides them.
To run this program, you would typically load it into your Lisp environment and then call (main)
.
The concept of closing channels leads naturally to our next example: iterating over channels.