Atomic Counters in Lisp

(defpackage :atomic-counters
  (:use :cl :bordeaux-threads)
  (:export :main))

(in-package :atomic-counters)

(defun main ()
  ;; We'll use an atomic integer to represent our (always-positive) counter.
  (let ((ops (make-atomic-integer :value 0)))
    
    ;; We'll start 50 threads that each increment the counter exactly 1000 times.
    (let ((threads (loop repeat 50
                         collect (make-thread
                                  (lambda ()
                                    (dotimes (i 1000)
                                      ;; To atomically increment the counter we use atomic-incf
                                      (atomic-incf (atomic-integer-value ops))))))))
      
      ;; Wait until all the threads are done.
      (mapc #'join-thread threads)
      
      ;; Here no threads are writing to 'ops', but it's safe to atomically read
      ;; a value even while other threads might be updating it.
      (format t "ops: ~A~%" (atomic-integer-value ops)))))

This Lisp code demonstrates the use of atomic operations for managing a shared counter across multiple threads. Here’s a breakdown of the key elements:

  1. We use the bordeaux-threads library for multi-threading support in Common Lisp.

  2. Instead of Go’s atomic.Uint64, we use an atomic-integer from the bordeaux-threads library.

  3. The sync.WaitGroup is replaced with a list of threads that we join at the end.

  4. Go’s goroutines are replaced with Lisp threads created using make-thread.

  5. The atomic increment operation ops.Add(1) is replaced with (atomic-incf (atomic-integer-value ops)).

  6. We use mapc #'join-thread threads to wait for all threads to complete, similar to wg.Wait() in the Go version.

  7. The final value is printed using format instead of fmt.Println.

To run this program:

$ sbcl --load atomic-counters.lisp
* (atomic-counters:main)
ops: 50000

We expect to get exactly 50,000 operations. The use of atomic operations ensures that the counter is incremented correctly even with concurrent access from multiple threads.

This example demonstrates how to use atomic operations in Lisp to safely manage state across multiple threads, similar to using the sync/atomic package in Go.