Atomic(3) | OCaml library | Atomic(3) |

# NAME

Atomic - Atomic references.

# Module

Module Atomic

# Documentation

Module **Atomic**

: **sig end**

Atomic references.

See **Atomic.examples** below. See 'Memory model: The hard
bits' chapter in the manual.

**Since** 4.12

*type* **!'a** *t*

An atomic (mutable) reference to a value of type **'a** .

*val make* : **'a -> 'a t**

Create an atomic reference.

*val get* : **'a t -> 'a**

Get the current value of the atomic reference.

*val set* : **'a t -> 'a -> unit**

Set a new value for the atomic reference.

*val exchange* : **'a t -> 'a -> 'a**

Set a new value for the atomic reference, and return the current value.

*val compare_and_set* : **'a t -> 'a -> 'a ->
bool**

**compare_and_set r seen v** sets the new value of **r** to
**v** only if its current value is physically equal to **seen** -- the
comparison and the set occur atomically. Returns **true** if the
comparison succeeded (so the set happened) and **false** otherwise.

*val fetch_and_add* : **int t -> int -> int**

**fetch_and_add r n** atomically increments the value of
**r** by **n** , and returns the current value (before the
increment).

*val incr* : **int t -> unit**

**incr r** atomically increments the value of **r** by
**1** .

*val decr* : **int t -> unit**

**decr r** atomically decrements the value of **r** by
**1** .

## Examples

## Basic Thread Coordination

A basic use case is to have global counters that are updated in a thread-safe way, for example to keep some sorts of metrics over IOs performed by the program. Another basic use case is to coordinate the termination of threads in a given program, for example when one thread finds an answer, or when the program is shut down by the user.

Here, for example, we're going to try to find a number whose hash satisfies a basic property. To do that, we'll run multiple threads which will try random numbers until they find one that works.

Of course the output below is a sample run and will change every time the program is run.

(* use for termination *)

let stop_all_threads = Atomic.make false

(* total number of individual attempts to find a number *)

let num_attempts = Atomic.make 0

(* find a number that satisfies [p], by... trying random numbers

until one fits. *)

let find_number_where (p:int -> bool) =

let rand = Random.State.make_self_init() in

while not (Atomic.get stop_all_threads) do

let n = Random.State.full_int rand max_int in

ignore (Atomic.fetch_and_add num_attempts 1 : int);

if p (Hashtbl.hash n) then (

Printf.printf "found %d (hash=%d)\n%!" n (Hashtbl.hash n);

Atomic.set stop_all_threads true; (* signal all threads to stop *)

)

done;;

(* run multiple domains to search for a [n] where [hash n <= 100] *)

let () =

let criterion n = n <= 100 in

let threads =

Array.init 8

(fun _ -> Domain.spawn (fun () -> find_number_where criterion))

in

Array.iter Domain.join threads;

Printf.printf "total number of attempts: %d\n%!"

(Atomic.get num_attempts) ;;

- : unit = ()

found 1651745641680046833 (hash=33)

total number of attempts: 30230350

## Treiber Stack

Another example is a basic Treiber stack (a thread-safe stack) that can be safely shared between threads.

Note how both **push** and **pop** are recursive, because
they attempt to swap the new stack (with one more, or one fewer, element)
with the old stack. This is optimistic concurrency: each iteration of, say,
**push stack x** gets the old stack **l** , and hopes that by the time
it tries to replace **l** with **x::l** , nobody else has had time to
modify the list. If the **compare_and_set** fails it means we were too
optimistic, and must try again.

type 'a stack = 'a list Atomic.t

let rec push (stack: _ stack) elt : unit =

let cur = Atomic.get stack in

let success = Atomic.compare_and_set stack cur (elt :: cur) in

if not success then

push stack elt

let rec pop (stack: _ stack) : _ option =

let cur = Atomic.get stack in

match cur with

| [] -> None

| x :: tail ->

let success = Atomic.compare_and_set stack cur tail in

if success then Some x

else pop stack

# let st = Atomic.make []

# push st 1

- : unit = ()

# push st 2

- : unit = ()

# pop st

- : int option = Some 2

# pop st

- : int option = Some 1

# pop st

- : int option = None

2023-11-11 | OCamldoc |