module Atomic:`sig`

..`end`

Atomic references.

See the 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`

.

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
```

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
```