Waitgroups in Lisp

Our example demonstrates how to wait for multiple threads to finish using a semaphore. In Lisp, we’ll use the bordeaux-threads library for threading capabilities.

First, let’s define our worker function:

(defun worker (id)
  (format t "Worker ~D starting~%" id)
  ;; Sleep to simulate an expensive task
  (sleep 1)
  (format t "Worker ~D done~%" id))

This function simulates a task by printing a start message, sleeping for a second, and then printing a completion message.

Now, let’s set up our main function:

(defun main ()
  (let ((semaphore (bt:make-semaphore :count 0)))
    ;; Launch several threads and increment the semaphore counter for each
    (dotimes (i 5)
      (bt:make-thread
       (lambda ()
         (unwind-protect
              (worker (1+ i))
           (bt:signal-semaphore semaphore)))
       :name (format nil "Worker-~D" (1+ i))))
    
    ;; Wait for all threads to finish
    (dotimes (i 5)
      (bt:wait-on-semaphore semaphore))))

Here’s what’s happening in the main function:

  1. We create a semaphore with an initial count of 0.
  2. We launch 5 threads, each running our worker function.
  3. Each thread is wrapped in an unwind-protect form, which ensures that the semaphore is signaled even if an error occurs.
  4. After launching all threads, we wait on the semaphore 5 times, effectively waiting for all threads to complete.

To run this program:

$ sbcl --load waitgroups.lisp
* (main)
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done
NIL
*

The order of workers starting up and finishing is likely to be different for each invocation.

Note that this approach doesn’t provide a straightforward way to propagate errors from workers. For more advanced use cases, you might need to implement additional error handling mechanisms.

Also, remember that you’ll need to have the bordeaux-threads library installed and loaded to run this code. You can install it using Quicklisp:

(ql:quickload :bordeaux-threads)

This example demonstrates how to use threads and semaphores in Lisp to achieve similar functionality to WaitGroups in other languages.