Manual Page Result
0
Command: membar_release | Section: 3 | Source: NetBSD | File: membar_release.3
MEMBAR_OPS(3) FreeBSD Library Functions Manual MEMBAR_OPS(3)
NAME
membar_ops, membar_acquire, membar_release, membar_producer,
membar_consumer, membar_datadep_consumer, membar_sync - memory ordering
barriers
SYNOPSIS
#include <sys/atomic.h>
void
membar_acquire(void);
void
membar_release(void);
void
membar_producer(void);
void
membar_consumer(void);
void
membar_datadep_consumer(void);
void
membar_sync(void);
DESCRIPTION
The membar_ops family of functions prevent reordering of memory
operations, as needed for synchronization in multiprocessor execution
environments that have relaxed load and store order.
In general, memory barriers must come in pairs -- a barrier on one CPU,
such as membar_release(), must pair with a barrier on another CPU, such
as membar_acquire(), in order to synchronize anything between the two
CPUs. Code using membar_ops should generally be annotated with comments
identifying how they are paired.
membar_ops affect only operations on regular memory, not on device
memory; see bus_space(9) and bus_dma(9) for machine-independent
interfaces to handling device memory and DMA operations for device
drivers.
Unlike C11, all memory operations -- that is, all loads and stores on
regular memory -- are affected by membar_ops, not just C11 atomic
operations on _Atomic-qualified objects.
membar_acquire()
Any load preceding membar_acquire() will happen before all memory
operations following it.
A load followed by a membar_acquire() implies a load-acquire
operation in the language of C11. membar_acquire() should only be
used after atomic read/modify/write, such as atomic_cas_uint(3).
For regular loads, instead of x = *p; membar_acquire(), you should
use x = atomic_load_acquire(p).
membar_acquire() is typically used in code that implements locking
primitives to ensure that a lock protects its data, and is
typically paired with membar_release(); see below for an example.
membar_release()
All memory operations preceding membar_release() will happen before
any store that follows it.
A membar_release() followed by a store implies a store-release
operation in the language of C11. membar_release() should only be
used before atomic read/modify/write, such as atomic_inc_uint(3).
For regular stores, instead of membar_release(); *p = x, you should
use atomic_store_release(p, x).
membar_release() is typically paired with membar_acquire(), and is
typically used in code that implements locking or reference
counting primitives. Releasing a lock or reference count should
use membar_release(), and acquiring a lock or handling an object
after draining references should use membar_acquire(), so that
whatever happened before releasing will also have happened before
acquiring. For example:
/* thread A -- release a reference */
obj->state.mumblefrotz = 42;
KASSERT(valid(&obj->state));
membar_release();
atomic_dec_uint(&obj->refcnt);
/*
* thread B -- busy-wait until last reference is released,
* then lock it by setting refcnt to UINT_MAX
*/
while (atomic_cas_uint(&obj->refcnt, 0, -1) != 0)
continue;
membar_acquire();
KASSERT(valid(&obj->state));
obj->state.mumblefrotz--;
In this example, if the load in atomic_cas_uint() in thread B
witnesses the store in atomic_dec_uint() in thread A setting the
reference count to zero, then everything in thread A before the
membar_release() is guaranteed to happen before everything in
thread B after the membar_acquire(), as if the machine had
sequentially executed:
obj->state.mumblefrotz = 42; /* from thread A */
KASSERT(valid(&obj->state));
...
KASSERT(valid(&obj->state)); /* from thread B */
obj->state.mumblefrotz--;
membar_release() followed by a store, serving as a store-release
operation, may also be paired with a subsequent load followed by
membar_acquire(), serving as the corresponding load-acquire
operation. However, you should use atomic_store_release(9) and
atomic_load_acquire(9) instead in that situation, unless the store
is an atomic read/modify/write which requires a separate
membar_release().
membar_producer()
All stores preceding membar_producer() will happen before any
stores following it.
membar_producer() has no analogue in C11.
membar_producer() is typically used in code that produces data for
read-only consumers which use membar_consumer(), such as
`seqlocked' snapshots of statistics; see below for an example.
membar_consumer()
All loads preceding membar_consumer() will complete before any
loads after it.
membar_consumer() has no analogue in C11.
membar_consumer() is typically used in code that reads data from
producers which use membar_producer(), such as `seqlocked'
snapshots of statistics. For example:
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;
membar_datadep_consumer()
Same as membar_consumer(), but limited to loads of addresses
dependent on prior loads, or `data-dependent' loads:
int **pp, *p, v;
p = *pp;
membar_datadep_consumer();
v = *p;
consume(v);
membar_datadep_consumer() is typically paired with membar_release()
by code that initializes an object before publishing it. However,
you should use atomic_store_release(9) and atomic_load_consume(9)
instead, to avoid obscure edge cases in case the consumer is not
read-only.
membar_datadep_consumer() does not guarantee ordering of loads in
branches, or `control-dependent' loads -- you must use
membar_consumer() instead:
int *ok, *p, v;
if (*ok) {
membar_consumer();
v = *p;
consume(v);
}
Most CPUs do not reorder data-dependent loads (i.e., most CPUs
guarantee that cached values are not stale in that case), so
membar_datadep_consumer() is a no-op on those CPUs.
membar_sync()
All memory operations preceding membar_sync() will happen before
any memory operations following it.
membar_sync() is a sequential consistency acquire/release barrier,
analogous to atomic_thread_fence(memory_order_seq_cst) in C11.
membar_sync() is typically paired with membar_sync().
membar_sync() is typically not needed except in exotic
synchronization schemes like Dekker's algorithm that require store-
before-load ordering. If you are tempted to reach for it, see if
there is another way to do what you're trying to do first.
DEPRECATED MEMORY BARRIERS
The following memory barriers are deprecated. They were imported from
Solaris, which describes them as providing ordering relative to `lock
acquisition', but the documentation in NetBSD disagreed with the
implementation and use on the semantics.
membar_enter()
Originally documented as store-before-load/store, this was instead
implemented as load-before-load/store on some platforms, which is
what essentially all uses relied on. Now this is implemented as an
alias for membar_sync() everywhere, meaning a full load/store-
before-load/store sequential consistency barrier, in order to
guarantee what the documentation claimed and what the
implementation actually did.
New code should use membar_acquire() for load-before-load/store
ordering, which is what most uses need, or membar_sync() for store-
before-load/store ordering, which typically only appears in exotic
synchronization schemes like Dekker's algorithm.
membar_exit()
Alias for membar_release(). This was originally meant to be paired
with membar_enter().
New code should use membar_release() instead.
SEE ALSO
atomic_ops(3), atomic_loadstore(9), bus_dma(9), bus_space(9)
HISTORY
The membar_ops functions first appeared in NetBSD 5.0.
The data-dependent load barrier, membar_datadep_consumer(), first
appeared in NetBSD 7.0.
The membar_acquire() and membar_release() functions first appeared, and
the membar_enter() and membar_exit() functions were deprecated, in
NetBSD 10.0.
FreeBSD 14.1-RELEASE-p8 March 30, 2022 FreeBSD 14.1-RELEASE-p8