Atomic Counters in OCaml

Here’s the translation of the atomic counters example from Go to OCaml:

Our primary mechanism for managing state in OCaml is through mutable references and modules like Atomic. In this example, we’ll look at using the Atomic module for atomic counters accessed by multiple threads.

open Lwt
open Lwt.Infix

(* We'll use an atomic integer to represent our (always-positive) counter. *)
let ops = Atomic.make 0

(* A function to increment the counter 1000 times *)
let increment_counter () =
  for _ = 1 to 1000 do
    Atomic.incr ops
  done;
  Lwt.return_unit

let main () =
  (* We'll start 50 threads that each increment the counter exactly 1000 times. *)
  let threads = List.init 50 (fun _ -> increment_counter ()) in
  
  (* Wait until all the threads are done. *)
  Lwt.join threads >>= fun () ->

  (* Here no threads are writing to 'ops', but it's safe to atomically read a value. *)
  Printf.printf "ops: %d\n" (Atomic.get ops);
  Lwt.return_unit

let () = Lwt_main.run (main ())

To run this program, you’ll need to have the lwt library installed. You can compile and run it using the following commands:

$ ocamlfind ocamlopt -o atomic_counters -linkpkg -package lwt.unix atomic_counters.ml
$ ./atomic_counters
ops: 50000

We expect to get exactly 50,000 operations. The use of Atomic ensures that the increments are performed atomically, preventing race conditions that could occur if we used a regular mutable reference.

In this OCaml version:

  1. We use the Atomic module to create an atomic counter.
  2. Instead of goroutines, we use Lwt threads for concurrency.
  3. The increment_counter function increments the counter 1000 times.
  4. We create 50 threads using List.init and increment_counter.
  5. Lwt.join is used to wait for all threads to complete.
  6. Finally, we print the value of the counter using Atomic.get.

This approach provides similar guarantees to the original Go version, ensuring that our counter operations are atomic and thread-safe.

Next, we’ll look at mutexes, another tool for managing state in concurrent programs.