mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2026-01-22 01:06:41 -05:00
64 lines
8.8 KiB
HTML
64 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Atomic (ocaml.Stdlib.Atomic)</title><meta charset="utf-8"/><link rel="stylesheet" href="../../../_odoc-theme/odoc.css"/><meta name="generator" content="odoc 2.4.4"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="../index.html">Up</a> – <a href="../../index.html">ocaml</a> » <a href="../index.html">Stdlib</a> » Atomic</nav><header class="odoc-preamble"><h1>Module <code><span>Stdlib.Atomic</span></code></h1><p>Atomic references.</p><p>See <a href="#examples" title="examples">the examples</a> below. See 'Memory model: The hard bits' chapter in the manual.</p><ul class="at-tags"><li class="since"><span class="at-tag">since</span> 4.12</li></ul></header><nav class="odoc-toc"><ul><li><a href="#examples">Examples</a><ul><li><a href="#basic-thread-coordination">Basic Thread Coordination</a></li><li><a href="#treiber-stack">Treiber Stack</a></li></ul></li></ul></nav><div class="odoc-content"><div class="odoc-spec"><div class="spec type anchored" id="type-t"><a href="#type-t" class="anchor"></a><code><span><span class="keyword">type</span> <span>!'a t</span></span></code></div><div class="spec-doc"><p>An atomic (mutable) reference to a value of type <code>'a</code>.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-make"><a href="#val-make" class="anchor"></a><code><span><span class="keyword">val</span> make : <span><span class="type-var">'a</span> <span class="arrow">-></span></span> <span><span class="type-var">'a</span> <a href="#type-t">t</a></span></span></code></div><div class="spec-doc"><p>Create an atomic reference.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-get"><a href="#val-get" class="anchor"></a><code><span><span class="keyword">val</span> get : <span><span><span class="type-var">'a</span> <a href="#type-t">t</a></span> <span class="arrow">-></span></span> <span class="type-var">'a</span></span></code></div><div class="spec-doc"><p>Get the current value of the atomic reference.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-set"><a href="#val-set" class="anchor"></a><code><span><span class="keyword">val</span> set : <span><span><span class="type-var">'a</span> <a href="#type-t">t</a></span> <span class="arrow">-></span></span> <span><span class="type-var">'a</span> <span class="arrow">-></span></span> unit</span></code></div><div class="spec-doc"><p>Set a new value for the atomic reference.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-exchange"><a href="#val-exchange" class="anchor"></a><code><span><span class="keyword">val</span> exchange : <span><span><span class="type-var">'a</span> <a href="#type-t">t</a></span> <span class="arrow">-></span></span> <span><span class="type-var">'a</span> <span class="arrow">-></span></span> <span class="type-var">'a</span></span></code></div><div class="spec-doc"><p>Set a new value for the atomic reference, and return the current value.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-compare_and_set"><a href="#val-compare_and_set" class="anchor"></a><code><span><span class="keyword">val</span> compare_and_set : <span><span><span class="type-var">'a</span> <a href="#type-t">t</a></span> <span class="arrow">-></span></span> <span><span class="type-var">'a</span> <span class="arrow">-></span></span> <span><span class="type-var">'a</span> <span class="arrow">-></span></span> bool</span></code></div><div class="spec-doc"><p><code>compare_and_set r seen v</code> sets the new value of <code>r</code> to <code>v</code> only if its current value is physically equal to <code>seen</code> -- the comparison and the set occur atomically. Returns <code>true</code> if the comparison succeeded (so the set happened) and <code>false</code> otherwise.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-fetch_and_add"><a href="#val-fetch_and_add" class="anchor"></a><code><span><span class="keyword">val</span> fetch_and_add : <span><span>int <a href="#type-t">t</a></span> <span class="arrow">-></span></span> <span>int <span class="arrow">-></span></span> int</span></code></div><div class="spec-doc"><p><code>fetch_and_add r n</code> atomically increments the value of <code>r</code> by <code>n</code>, and returns the current value (before the increment).</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-incr"><a href="#val-incr" class="anchor"></a><code><span><span class="keyword">val</span> incr : <span><span>int <a href="#type-t">t</a></span> <span class="arrow">-></span></span> unit</span></code></div><div class="spec-doc"><p><code>incr r</code> atomically increments the value of <code>r</code> by <code>1</code>.</p></div></div><div class="odoc-spec"><div class="spec value anchored" id="val-decr"><a href="#val-decr" class="anchor"></a><code><span><span class="keyword">val</span> decr : <span><span>int <a href="#type-t">t</a></span> <span class="arrow">-></span></span> unit</span></code></div><div class="spec-doc"><p><code>decr r</code> atomically decrements the value of <code>r</code> by <code>1</code>.</p></div></div><h2 id="examples"><a href="#examples" class="anchor"></a>Examples</h2><h3 id="basic-thread-coordination"><a href="#basic-thread-coordination" class="anchor"></a>Basic Thread Coordination</h3><p>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.</p><p>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.</p><p>Of course the output below is a sample run and will change every time the program is run.</p><pre class="language-ocaml"><code>(* 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</code></pre><h3 id="treiber-stack"><a href="#treiber-stack" class="anchor"></a>Treiber Stack</h3><p>Another example is a basic <a href="https://en.wikipedia.org/wiki/Treiber_stack">Treiber stack</a> (a thread-safe stack) that can be safely shared between threads.</p><p>Note how both <code>push</code> and <code>pop</code> 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, <code>push stack x</code> gets the old stack <code>l</code>, and hopes that by the time it tries to replace <code>l</code> with <code>x::l</code>, nobody else has had time to modify the list. If the <code>compare_and_set</code> fails it means we were too optimistic, and must try again.</p><pre class="language-ocaml"><code>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</code></pre></div></body></html>
|