Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libc/atomic Update membar_ops(3) man page with examples ...



details:   https://anonhg.NetBSD.org/src/rev/c42addbce71c
branches:  trunk
changeset: 1013665:c42addbce71c
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Thu Sep 03 00:00:06 2020 +0000

description:
Update membar_ops(3) man page with examples and relation to C11.

Add exhortation to always always always document how membars come in
pairs for synchronization between two CPUs when you use them.

diffstat:

 lib/libc/atomic/membar_ops.3 |  263 +++++++++++++++++++++++++++++++++++++++---
 1 files changed, 242 insertions(+), 21 deletions(-)

diffs (truncated from 323 to 300 lines):

diff -r 7c7c37590a9e -r c42addbce71c lib/libc/atomic/membar_ops.3
--- a/lib/libc/atomic/membar_ops.3      Wed Sep 02 23:42:58 2020 +0000
+++ b/lib/libc/atomic/membar_ops.3      Thu Sep 03 00:00:06 2020 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: membar_ops.3,v 1.5 2017/10/24 18:19:17 abhinav Exp $
+.\"    $NetBSD: membar_ops.3,v 1.6 2020/09/03 00:00:06 riastradh Exp $
 .\"
 .\" Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 20, 2014
+.Dd September 2, 2020
 .Dt MEMBAR_OPS 3
 .Os
 .Sh NAME
@@ -38,7 +38,7 @@
 .Nm membar_consumer ,
 .Nm membar_datadep_consumer ,
 .Nm membar_sync
-.Nd memory access barrier operations
+.Nd memory ordering barriers
 .\" .Sh LIBRARY
 .\" .Lb libc
 .Sh SYNOPSIS
@@ -58,33 +58,213 @@
 .Fn membar_sync "void"
 .Sh DESCRIPTION
 The
-.Nm membar_ops
-family of functions provide memory access barrier operations necessary
+.Nm
+family of functions prevent reordering of memory operations, as needed
 for synchronization in multiprocessor execution environments that have
 relaxed load and store order.
-.Bl -tag -width "mem"
+.Pp
+In general, memory barriers must come in pairs \(em a barrier on one
+CPU, such as
+.Fn membar_exit ,
+must pair with a barrier on another CPU, such as
+.Fn membar_enter ,
+in order to synchronize anything between the two CPUs.
+Code using
+.Nm
+should generally be annotated with comments identifying how they are
+paired.
+.Pp
+.Nm
+affect only operations on regular memory, not on device
+memory; see
+.Xr bus_space 9
+and
+.Xr bus_dma 9
+for machine-independent interfaces to handling device memory and DMA
+operations for device drivers.
+.Pp
+Unlike C11,
+.Em all
+memory operations \(em that is, all loads and stores on regular
+memory \(em are affected by
+.Nm ,
+not just C11 atomic operations on
+.Vt _Atomic Ns -qualified
+objects.
+.Bl -tag -width abcd
 .It Fn membar_enter
 Any store preceding
 .Fn membar_enter
-will reach global visibility before all loads and stores following it.
+will happen before all memory operations following it.
+.Pp
+An atomic read/modify/write operation
+.Pq Xr atomic_ops 3
+followed by a
+.Fn membar_enter
+implies a
+.Em load-acquire
+operation in the language of C11.
+.Pp
+.Sy WARNING :
+A load followed by
+.Fn membar_enter
+.Em does not
+imply a
+.Em load-acquire
+operation, even though
+.Fn membar_exit
+followed by a store implies a
+.Em store-release
+operation; the symmetry of these names and asymmetry of the semantics
+is a historical mistake.
+In the
+.Nx
+kernel, you can use
+.Xr atomic_load_acquire 9
+for a
+.Em load-acquire
+operation without any atomic read/modify/write.
 .Pp
 .Fn membar_enter
 is typically used in code that implements locking primitives to ensure
-that a lock protects its data.
+that a lock protects its data, and is typically paired with
+.Fn membar_exit ;
+see below for an example.
 .It Fn membar_exit
-All loads and stores preceding
+All memory operations preceding
+.Fn membar_exit
+will happen before any store that follows it.
+.Pp
+A
 .Fn membar_exit
-will reach global visibility before any store that follows it.
+followed by a store implies a
+.Em store-release
+operation in the language of C11.
+For a regular store, rather than an atomic read/modify/write store, you
+should use
+.Xr atomic_store_release 9
+instead of
+.Fn membar_exit
+followed by the store.
 .Pp
 .Fn membar_exit
 is typically used in code that implements locking primitives to ensure
-that a lock protects its data.
+that a lock protects its data, and is typically paired with
+.Fn membar_enter .
+For example:
+.Bd -literal -offset abcdefgh
+/* thread A */
+obj->state.mumblefrotz = 42;
+KASSERT(valid(&obj->state));
+membar_exit();
+obj->lock = 0;
+
+/* thread B */
+if (atomic_cas_uint(&obj->lock, 0, 1) != 0)
+       return;
+membar_enter();
+KASSERT(valid(&obj->state));
+obj->state.mumblefrotz--;
+.Ed
+.Pp
+In this example,
+.Em if
+the
+.Fn atomic_cas_uint
+operation in thread B witnesses the store
+.Li "obj->lock = 0"
+from thread A,
+.Em then
+everything in thread A before the
+.Fn membar_exit
+is guaranteed to happen before everything in thread B after the
+.Fn membar_enter ,
+as if the machine had sequentially executed:
+.Bd -literal -offset abcdefgh
+obj->state.mumblefrotz = 42;   /* from thread A */
+KASSERT(valid(&obj->state));
+\&...
+KASSERT(valid(&obj->state));   /* from thread B */
+obj->state.mumblefrotz--;
+.Ed
+.Pp
+.Fn membar_exit
+followed by a store, serving as a
+.Em store-release
+operation, may also be paired with a subsequent load followed by
+.Fn membar_sync ,
+serving as the corresponding
+.Em load-acquire
+operation.
+However, you should use
+.Xr atomic_store_release 9
+and
+.Xr atomic_load_acquire 9
+instead in that situation, unless the store is an atomic
+read/modify/write which requires a separate
+.Fn membar_exit .
 .It Fn membar_producer
-All stores preceding the memory barrier will reach global visibility
-before any stores after the memory barrier reach global visibility.
+All stores preceding
+.Fn membar_producer
+will happen before any stores following it.
+.Pp
+.Fn membar_producer
+has no analogue in C11.
+.Pp
+.Fn membar_producer
+is typically used in code that produces data for read-only consumers
+which use
+.Fn membar_consumer ,
+such as
+.Sq seqlocked
+snapshots of statistics; see below for an example.
 .It Fn membar_consumer
-All loads preceding the memory barrier will complete before any loads
-after the memory barrier complete.
+All loads preceding
+.Fn membar_consumer
+will complete before any loads after it.
+.Pp
+.Fn membar_consumer
+has no analogue in C11.
+.Pp
+.Fn membar_consumer
+is typically used in code that reads data from producers which use
+.Fn membar_producer ,
+such as
+.Sq seqlocked
+snapshots of statistics.
+For example:
+.Bd -literal
+struct {
+       /* version number and in-progress bit */
+       unsigned        seq;
+
+       /* read-only statistics, too large for atomic load */
+       unsigned        foo;
+       int             bar;
+       uint64_t        baz;
+} stats;
+
+       /* producer (must be serialized, e.g. with mutex(9)) */
+       stats->seq |= 1;        /* mark update in progress */
+       membar_producer();
+       stats->foo = count_foo();
+       stats->bar = measure_bar();
+       stats->baz = enumerate_baz();
+       membar_producer();
+       stats->seq++;           /* bump version number */
+
+       /* consumer (in parallel w/ producer, other consumers) */
+restart:
+       while ((seq = stats->seq) & 1)  /* wait for update */
+               SPINLOCK_BACKOFF_HOOK;
+       membar_consumer();
+       foo = stats->foo;       /* read out a candidate snapshot */
+       bar = stats->bar;
+       baz = stats->baz;
+       membar_consumer();
+       if (seq != stats->seq)  /* try again if version changed */
+               goto restart;
+.Ed
 .It Fn membar_datadep_consumer
 Same as
 .Fn membar_consumer ,
@@ -100,9 +280,21 @@
 consume(v);
 .Ed
 .Pp
-Does not guarantee ordering of loads in branches, or
+.Fn membar_datadep_consumer
+is typically paired with
+.Fn membar_exit
+by code that initializes an object before publishing it.
+However, you should use
+.Xr atomic_store_release 9
+and
+.Xr atomic_load_consume 9
+instead, to avoid obscure edge cases in case the consumer is not
+read-only.
+.Pp
+.Fn membar_datadep_consumer
+does not guarantee ordering of loads in branches, or
 .Sq control-dependent
-loads -- you must use
+loads \(em you must use
 .Fn membar_consumer
 instead:
 .Bd -literal -offset indent
@@ -120,12 +312,41 @@
 .Fn membar_datadep_consumer
 is a no-op on those CPUs.
 .It Fn membar_sync
-All loads and stores preceding the memory barrier will complete and
-reach global visibility before any loads and stores after the memory
-barrier complete and reach global visibility.
+All memory operations preceding
+.Fn membar_sync
+will happen before any memory operations following it.
+.Pp
+.Fn membar_sync
+is a sequential consistency acquire/release barrier, analogous to
+.Li "atomic_thread_fence(memory_order_seq_cst)"
+in C11.
+.Pp
+.Fn membar_sync
+is typically paired with
+.Fn membar_sync .
+.Pp
+A load followed by
+.Fn membar_sync ,
+serving as a



Home | Main Index | Thread Index | Old Index