Module Atomic
: sigend
Atomic references.
See Atomic.examples below. See 'Memory model: The hard bits' chapter in the manual.
Since 4.12
type!'at
An atomic (mutable) reference to a value of type 'a .
valmake : 'a->'at
Create an atomic reference.
valmake_contended : 'a->'at
Create an atomic reference that is alone on a cache line. It occupies 4-16x the memory of one allocated
with makev .
The primary purpose is to prevent false-sharing and the resulting performance degradation. When a CPU
performs an atomic operation, it temporarily takes ownership of an entire cache line that contains the
atomic reference. If multiple atomic references share the same cache line, modifying these disjoint
memory regions simultaneously becomes impossible, which can create a bottleneck. Hence, as a general
guideline, if an atomic reference is experiencing contention, assigning it its own cache line may enhance
performance.
valget : 'at->'a
Get the current value of the atomic reference.
valset : 'at->'a->unit
Set a new value for the atomic reference.
valexchange : 'at->'a->'a
Set a new value for the atomic reference, and return the current value.
valcompare_and_set : 'at->'a->'a->boolcompare_and_setrseenv 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.
valfetch_and_add : intt->int->intfetch_and_addrn atomically increments the value of r by n , and returns the current value (before the
increment).
valincr : intt->unitincrr atomically increments the value of r by 1 .
valdecr : intt->unitdecrr atomically decrements the value of r by 1 .
ExamplesBasicThreadCoordination
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.
(*usefortermination*)letstop_all_threads=Atomic.makefalse(*totalnumberofindividualattemptstofindanumber*)letnum_attempts=Atomic.make0(*findanumberthatsatisfies[p],by...tryingrandomnumbersuntilonefits.*)letfind_number_where(p:int->bool)=letrand=Random.State.make_self_init()inwhilenot(Atomic.getstop_all_threads)doletn=Random.State.full_intrandmax_intinignore(Atomic.fetch_and_addnum_attempts1:int);ifp(Hashtbl.hashn)then(Printf.printf"found%d(hash=%d)\n%!"n(Hashtbl.hashn);Atomic.setstop_all_threadstrue;(*signalallthreadstostop*))done;;(*runmultipledomainstosearchfora[n]where[hashn<=100]*)let()=letcriterionn=n<=100inletthreads=Array.init8(fun_->Domain.spawn(fun()->find_number_wherecriterion))inArray.iterDomain.jointhreads;Printf.printf"totalnumberofattempts:%d\n%!"(Atomic.getnum_attempts);;-:unit=()found1651745641680046833(hash=33)totalnumberofattempts:30230350TreiberStack
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, pushstackx 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'astack='alistAtomic.tletrecpush(stack:_stack)elt:unit=letcur=Atomic.getstackinletsuccess=Atomic.compare_and_setstackcur(elt::cur)inifnotsuccessthenpushstackeltletrecpop(stack:_stack):_option=letcur=Atomic.getstackinmatchcurwith|[]->None|x::tail->letsuccess=Atomic.compare_and_setstackcurtailinifsuccessthenSomexelsepopstack#letst=Atomic.make[]#pushst1-:unit=()#pushst2-:unit=()#popst-:intoption=Some2#popst-:intoption=Some1#popst-:intoption=None
OCamldoc 2025-06-12 Atomic(3o)