Module Picos_std_awaitable.Awaitable

An awaitable atomic location.

This module provides a superset of the Stdlib Atomic API with more or less identical performance. The main difference is that a non-padded awaitable location takes an extra word of memory. Additionally a futex-like API provides the ability to await until an awaitable location is explicitly signaled to potentially have a different value.

Awaitable locations can be used to implement many kinds of synchronization and communication primitives.

Atomic API

type !'a t

Represents an awaitable atomic location.

val make : ?padded:bool -> 'a -> 'a t

make initial creates a new awaitable atomic location with the given initial value.

val make_contended : 'a -> 'a t

make_contended initial is equivalent to make ~padded:true initial.

val get : 'a t -> 'a

get awaitable is essentially equivalent to Atomic.get awaitable.

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

compare_and_set awaitable before after is essentially equivalent to Atomic.compare_and_set awaitable before after.

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

exchange awaitable after is essentially equivalent to Atomic.exchange awaitable after.

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

set awaitable value is equivalent to exchange awaitable value |> ignore.

val fetch_and_add : int t -> int -> int

fetch_and_add awaitable delta is essentially equivalent to Atomic.fetch_and_add awaitable delta.

val incr : int t -> unit

incr awaitable is equivalent to fetch_and_add awaitable (+1) |> ignore.

val decr : int t -> unit

incr awaitable is equivalent to fetch_and_add awaitable (-1) |> ignore.

Futex API

val signal : 'a t -> unit

signal awaitable tries to wake up one fiber awaitin on the awaitable location.

🐌 Generally speaking one should avoid calling signal too frequently, because the queue of awaiters is stored separately from the awaitable location and it takes a bit of effort to locate it. For example, calling signal every time a value is added to an empty data structure might not be optimal. In many cases it is faster to explicitly mark the potential presence of awaiters in the data structure and avoid calling signal when it is definitely known that there are no awaiters.

val broadcast : 'a t -> unit

broadcast awaitable tries to wake up all fibers awaiting on the awaitable location.

🐌 The same advice as with signal applies to broadcast. In addition, it is typically a good idea to avoid potentially waking up large numbers of fibers as it can easily lead to the thundering herd phenomena.

val await : 'a t -> 'a -> unit

await awaitable before suspends the current fiber until the awaitable is explicitly signaled and has a value other than before.

⚠️ This operation is subject to the ABA problem. An await for value other than A may not return after the awaitable is signaled while having the value B, because at a later point the awaitable has again the value A. Furthermore, by the time an await for value other than A returns, the awaitable might already again have the value A.

⚠️ Atomic operations that change the value of an awaitable do not implicitly wake up awaiters.

module Awaiter : sig ... end

Low level interface for more flexible waiting.