123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295moduleDefinitions=struct(*_ Every operation on subatomics shows up in three places:
1) Untemplated at top-level, non-atomic/uncontended
2) Templated at top-level, atomic/shared
3) Untemplated under [Shared], atomic/shared
We define the module type once, generic over these three, in order to avoid
duplication and keep the interfaces in sync. To make this work, we actually generate
8 module types, and pick out the ones we want to use. These module types vary on:
1) What naming to use (i.e. templated or not)
2) What implementation to use (i.e. atomic or not)
3) An extra variable for whether contents need to cross contention (only necessary
due to a limitation in [ppx_template] which prevents tuples from varying over
multiple axes arbitrarily and not being able to express complex relationships
between modes)
*)moduletype%template[@synchro.explicitname@__=(unsync_uncontended,sync_shared),_impl@maybe_shared=(unsync_uncontended,sync_shared)][@mode.explicitmaybe_contended=(uncontended,contended)]S=sigtype('a:value_or_null)t(*_ Use [name] to determine templating *)[@@@synchro.defaultname](** Read the current value of a subatomic.
- [get] performs a non-atomic read on an uncontended subatomic.
- [get [@synchro atomic]] and [Shared.get] perform an atomic read on a shared
subatomic. *)valget:('a:value_or_null).'at@localmaybe_shared->'a@maybe_shared(** Update the current value of a subatomic.
- [set] performs a non-atomic write on an uncontended subatomic.
- [set [@synchro atomic]] and [Shared.set] perform an atomic write on a shared
subatomic. *)valset:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->'a->unit(** Update the current value of a subatomic, and return the previous value.
- [update] performs a non-atomic read/write on an uncontended subatomic.
- [update [@synchro atomic]] and [Shared.update] perform an atomic read/write on a
shared subatomic. *)valexchange:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->'a->'a(** If the current value of the subatomic is [phys_equal] to [if_phys_equal], then
update it to [replace_with] and return [Set_here]; otherwise, return
[Compare_failed] (with the subatomic left unchanged).
- [compare_and_set] performs a non-atomic read/compare/write on an uncontended
subatomic.
- [compare_and_set [@synchro atomic]] and [Shared.compare_and_set] perform an
atomic read/compare/write on a shared subatomic. *)valcompare_and_set:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->if_phys_equal_to:'a->replace_with:'a->Atomic.Compare_failed_or_set_here.t(** If the current value of the subatomic is [phys_equal] to [if_phys_equal], then
update it to [replace_with] and return the previous value; otherwise, return
current (unchanged) value.
- [compare_exchange] performs a non-atomic read/compare/write on an uncontended
subatomic.
- [compare_exchange [@synchro atomic]] and [Shared.compare_exchange] perform an
atomic read/compare/write on a shared subatomic. *)valcompare_exchange:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->if_phys_equal_to:'a->replace_with:'a->'a(** Update [t] to be the result of [pure_f (get t)]; if [t] is changed before the
whole operation is complete, retry until success. [pure_f] may be called multiple
times, so should be free of side effects.
- [update] performs a non-atomic read/compare/write on an uncontended subatomic.
[pure_f] still may be called multiple times due to nonportable threading or if
[pure_f] is not actually pure.
- [update [@synchro atomic]] and [Shared.update] perform an atomic
read/compare/write on a shared subatomic. *)valupdate:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->pure_f:('a->'a)@local->unit(** Update [t] to be the result of [pure_f (get t)], and return the old value; if [t]
is changed before the whole operation is complete, retry until success. [pure_f]
may be called multiple times, so should be free of side effects.
- [get_and_update] performs a non-atomic read/compare/write on an uncontended
subatomic. [pure_f] still may be called multiple times due to nonportable
threading or if [pure_f] is not actually pure.
- [get_and_update [@synchro atomic]] and [Shared.get_and_update] perform an atomic
read/compare/write on a shared subatomic. *)valget_and_update:('a:value_or_nullmodmaybe_contended).'at@localmaybe_shared->pure_f:('a->'a)@local->'a(** Increments the value of a subatomic by the given value, and return the previous
value (before the increment).
- [fetch_and_add] performs a non-atomic update on an uncontended subatomic.
- [fetch_and_add [@synchro atomic]] and [Shared.fetch_and_add] perform an atomic
update on a shared subatomic. *)valfetch_and_add:intt@localmaybe_shared->int->int(** Add the given value to the subatomic.
- [add] performs a non-atomic update on an uncontended subatomic.
- [add [@synchro atomic]] and [Shared.add] perform an atomic update on a shared
subatomic. *)valadd:intt@localmaybe_shared->int->unit(** Subtract the given value from the subatomic.
- [sub] performs a non-atomic update on an uncontended subatomic.
- [sub [@synchro atomic]] and [Shared.sub] perform an atomic update on a shared
subatomic. *)valsub:intt@localmaybe_shared->int->unit(** Apply bitwise-and of the given value to the subatomic.
- [logand] performs a non-atomic update on an uncontended subatomic.
- [logand [@synchro atomic]] and [Shared.logand] perform an atomic update on a
shared subatomic. *)vallogand:intt@localmaybe_shared->int->unit(** Apply bitwise-or of the given value to the subatomic.
- [logor] performs a non-atomic update on an uncontended subatomic.
- [logor [@synchro atomic]] and [Shared.logor] perform an atomic update on a
shared subatomic. *)vallogor:intt@localmaybe_shared->int->unit(** Apply bitwise-xor of the given value to the subatomic.
- [logxor] performs a non-atomic update on an uncontended subatomic.
- [logxor [@synchro atomic]] and [Shared.logxor] perform an atomic update on a
shared subatomic. *)vallogxor:intt@localmaybe_shared->int->unit(** Increment a subatomic by 1.
- [incr] performs a non-atomic update on an uncontended subatomic.
- [incr [@synchro atomic]] and [Shared.incr] perform an atomic update on a shared
subatomic. *)valincr:intt@localmaybe_shared->unit(** Decrement a subatomic by 1.
- [decr] performs a non-atomic update on an uncontended subatomic.
- [decr [@synchro atomic]] and [Shared.decr] perform an atomic update on a shared
subatomic. *)valdecr:intt@localmaybe_shared->unitendendmoduletypeSubatomic=sig@@portableopenDefinitions(** A [Subatomic.t] is a hybrid between [ref] and [Atomic.t]. When you have
[uncontended] access to a subatomic, you can do non-atomic reads/writes to it, like
a [ref]. When you have [shared] access to a subatomic, you can do atomic
reads/writes to it, like an [Atomic]. Nothing can be done to a [contended]
subatomic.
The safety of this interface depends on two differences from each of its
counterparts:
- Unlike [ref], you cannot non-atomically read a [shared] subatomic
- Unlike [Atomic.t], you cannot do anything with a [contended] subatomic, and
subatomics consequently do not mode cross on the contention axis
Conceptually, ['a Atomic.t] could be implemented as a globally-[shared]
['a Subatomic.t]:
{[
type 'a isolated =
| Isolated : ('a, 'k) Capsule.Data.t * 'k Capsule.Key.t -> 'a isolated
[@@unboxed]
type 'a atomic = 'a subatomic isolated
]} *)type(!'a:value_or_null)t:mutable_datawith'a='aBasement.Subatomic.t[%%rederive:typenonrec(!'a:valuemodcontended)t='at[@@derivingequal~localize,sexp_of]][%%rederive:typenonrec(!'a:valuemodportable)t='at[@@derivingof_sexp]](** Create a subatomic reference; has the same codegen as constructing an [Atomic.t] or
a [ref]. *)externalmake:('a:value_or_null).'a->('at[@local_opt])="%makemutable"(** Create a subatomic reference that is alone on a cache line. See {!Atomic.make_alone}
for details. *)valmake_alone:('a:value_or_null).'a->'at(*_ Untemplated nonatomic uncontended operations, e.g. [Subatomic.get] *)include%templateS[@synchro.explicitunsyncunsync][@mode.explicituncontended]withtype('a:value_or_null)t:='at(** @inline *)(*_ Templated atomic shared operations, e.g. [Subatomic.get [@synchro atomic]] *)include%templateS[@synchro.explicitsyncsync][@mode.explicitcontended]withtype('a:value_or_null)t:='at(** @inline *)module%templateShared:sig(*_ Untemplated atomic shared operations, e.g. [Subatomic.Shared.get] *)includeS[@synchro.explicitunsyncsync][@mode.explicitcontended]withtype('a:value_or_null)t:='at(** @inline *)endmoduleLoc:sigtype(!'a:value_or_null)t:mutable_datawith'a='aBasement.Subatomic.Loc.t[%%rederive:typenonrec(!'a:valuemodcontended)t='at[@@derivingsexp_of]]externalunsafe_of_atomic_loc:('a:value_or_null).('aStdlib.Atomic.Loc.t[@local_opt])->('at[@local_opt])="%identity"[@@ocaml.doc{| Convert an [Atomic.Loc.t] to a [Subatomic.Loc.t]. Currently, there is no
compiler support for subatomic fields, but we can mimic it using atomic fields.
Correct usage of this function is to have a record with an atomic field, and to
only ever access that field like
{[
type t = { mutable field : int [@atomic] }
let get_field t = Subatomic.Loc.unsafe_of_atomic_loc [%atomic.loc t.field]
]}
Accessing or mutating the field in any other way is unsound. |}][%%template:includeS[@synchro.explicitunsyncunsync][@mode.explicituncontended]withtype('a:value_or_null)t:='at[@@ocaml.doc{| @inline |}]][%%template:includeS[@synchro.explicitsyncsync][@mode.explicitcontended]withtype('a:value_or_null)t:='at[@@ocaml.doc{| @inline |}]][%%template:moduleShared:sigexternalunsafe_of_atomic_loc:('a:value_or_null).('aStdlib.Atomic.Loc.t[@local_opt])@shared->('at[@local_opt])@shared="%identity"[@@ocaml.doc{| Like {!Subatomic.Loc.unsafe_of_atomic_loc}, but for [shared] values. |}]includeS[@synchro.explicitunsyncsync][@mode.explicitcontended]withtype('a:value_or_null)t:='at[@@ocaml.doc{| @inline |}]end]end[@@ocaml.doc{| A [Subatomic.Loc.t] is the subatomic analog of [Atomic.Loc.t], i.e. a subatomic
reference to a field of a record. |}](**/**)(*_ See the Jane Street Style Guide for an explanation of [Private] submodules:
https://opensource.janestreet.com/standards/#private-submodules *)modulePrivate:sigincludemoduletypeofstructincludeDefinitionsendendend