atomic(9)
NAME
- atomic_add, atomic_clear, atomic_cmpset, atomic_fetchadd,
- atomic_load,
atomic_readandclear, atomic_set, atomic_subtract,
- atomic_store - atomic
operations
SYNOPSIS
#include <sys/types.h>
#include <machine/atomic.h>
void
atomic_add_[acq_|rel_]<type>(volatile <type> *p, <type> v);
void
atomic_clear_[acq_|rel_]<type>(volatile <type> *p, <type>
v);
int
atomic_cmpset_[acq_|rel_]<type>(volatile <type> *dst, <type>
old,
<type> new);
<type>
atomic_fetchadd_<type>(volatile <type> *p, <type> v);
<type>
atomic_load_acq_<type>(volatile <type> *p);
<type>
atomic_readandclear_<type>(volatile <type> *p);
void
atomic_set_[acq_|rel_]<type>(volatile <type> *p, <type> v);
void
atomic_subtract_[acq_|rel_]<type>(volatile <type> *p, <type>
v);
void
atomic_store_rel_<type>(volatile <type> *p, <type> v);
DESCRIPTION
- Each of the atomic operations is guaranteed to be atomic in
- the presence
of interrupts. They can be used to implement reference
- counts or as
building blocks for more advanced synchronization primitives
- such as
mutexes.
- Types
- Each atomic operation operates on a specific type. The type
- to use is
indicated in the function name. The available types that
- can be used
are:
int unsigned integer
long unsigned long integer
ptr unsigned integer the size of a pointer
32 unsigned 32-bit integer
64 unsigned 64-bit integer
- For example, the function to atomically add two integers is
- called
atomic_add_int().
- Certain architectures also provide operations for types
- smaller than
``int''.
char unsigned character
short unsigned short integer
8 unsigned 8-bit integer
16 unsigned 16-bit integer
- These must not be used in MI code because the instructions
- to implement
them efficiently may not be available.
- Memory Barriers
- Memory barriers are used to guarantee the order of data ac
- cesses in two
ways. First, they specify hints to the compiler to not re
- order or optimize the operations. Second, on architectures that do not
- guarantee
ordered data accesses, special instructions or special vari
- ants of
instructions are used to indicate to the processor that data
- accesses
need to occur in a certain order. As a result, most of the
- atomic operations have three variants in order to include optional memo
- ry barriers.
The first form just performs the operation without any ex
- plicit barriers.
The second form uses a read memory barrier, and the third
- variant uses a
write memory barrier.
- The second variant of each operation includes a read memory
- barrier.
This barrier ensures that the effects of this operation are
- completed
before the effects of any later data accesses. As a result,
- the operation is said to have acquire semantics as it acquires a
- pseudo-lock
requiring further operations to wait until it has completed.
- To denote
this, the suffix ``_acq'' is inserted into the function name
- immediately
prior to the ``_<type>'' suffix. For example, to subtract
- two integers
ensuring that any later writes will happen after the sub
- traction is performed, use atomic_subtract_acq_int().
- The third variant of each operation includes a write memory
- barrier.
This ensures that all effects of all previous data accesses
- are completed
before this operation takes place. As a result, the opera
- tion is said to
have release semantics as it releases any pending data ac
- cesses to be
completed before its operation is performed. To denote
- this, the suffix
``_rel'' is inserted into the function name immediately pri
- or to the
``_<type>'' suffix. For example, to add two long integers
- ensuring that
all previous writes will happen first, use
- atomic_add_rel_long().
- A practical example of using memory barriers is to ensure
- that data
accesses that are protected by a lock are all performed
- while the lock is
held. To achieve this, one would use a read barrier when
- acquiring the
lock to guarantee that the lock is held before any protected
- operations
are performed. Finally, one would use a write barrier when
- releasing the
lock to ensure that all of the protected operations are com
- pleted before
the lock is released.
- Multiple Processors
- The current set of atomic operations do not necessarily
- guarantee atomicity across multiple processors. To guarantee atomicity
- across processors, not only does the individual operation need to be
- atomic on the
processor performing the operation, but the result of the
- operation needs
to be pushed out to stable storage and the caches of all
- other processors
on the system need to invalidate any cache lines that in
- clude the
affected memory region. On the i386 architecture, the cache
- coherency
model requires that the hardware perform this task, thus the
- atomic operations are atomic across multiple processors. On the ia64
- architecture,
coherency is only guaranteed for pages that are configured
- to using a
caching policy of either uncached or write back.
- Semantics
- This section describes the semantics of each operation using
- a C like
notation.
- atomic_add(p, v)
*p += v;
- atomic_clear(p, v)
- *p &= ~v;
- atomic_cmpset(dst, old, new)
- if (*dst == old) {
*dst = new;
return 1;
- } else
return 0;
- The atomic_cmpset() functions are not implemented for the
- types ``char'',
``short'', ``8'', and ``16''.
- atomic_fetchadd(p, v)
- tmp = *p;
*p += v;
return tmp;
- The atomic_fetchadd() functions are only implemented for the
- types
``int'' and ``32'' and do not have any variants with memory
- barriers at
this time.
- atomic_load(addr)
- return (*addr)
- The atomic_load() functions always have acquire semantics.
- atomic_readandclear(addr)
- temp = *addr;
*addr = 0;
return (temp);
- The atomic_readandclear() functions are not implemented for
- the types
``char'', ``short'', ``ptr'', ``8'', and ``16'' and do not
- have any variants with memory barriers at this time.
- atomic_set(p, v)
- *p |= v;
- atomic_subtract(p, v)
- *p -= v;
- atomic_store(p, v)
- *p = v;
- The atomic_store() functions always have release semantics.
- The type ``64'' is currently not implemented for any of the
- atomic operations on the arm, i386, and powerpc architectures.
RETURN VALUES
- The atomic_cmpset() function returns the result of the com
- pare operation.
The atomic_fetchadd(), atomic_load(), and
- atomic_readandclear() functions
return the value at the specified address.
EXAMPLES
- This example uses the atomic_cmpset_acq_ptr() and
- atomic_set_ptr() functions to obtain a sleep mutex and handle recursion. Since
- the mtx_lock
member of a struct mtx is a pointer, the ``ptr'' type is
- used.
- /* Try to obtain mtx_lock once. */
#define _obtain_lock(mp, tid)
- atomic_cmpset_acq_ptr(&(mp)->mtx_lock, MTX_UNOWNED, (tid))
- /* Get a sleep lock, deal with recursion inline. */
#define _get_sleep_lock(mp, tid, opts, file, line) do {
- uintptr_t _tid = (uintptr_t)(tid);
- if (!_obtain_lock(mp, tid)) {
- if (((mp)->mtx_lock & MTX_FLAGMASK) != _tid)
- _mtx_lock_sleep((mp), _tid, (opts), (file), (line));
- else {
- atomic_set_ptr(&(mp)->mtx_lock, MTX_RECURSE);
- (mp)->mtx_recurse++; } } } while (0)
HISTORY
- The atomic_add(), atomic_clear(), atomic_set(), and
- atomic_subtract()
operations were first introduced in FreeBSD 3.0. This first
- set only
supported the types ``char'', ``short'', ``int'', and
- ``long''. The
atomic_cmpset(), atomic_load(), atomic_readandclear(), and
- atomic_store()
operations were added in FreeBSD 5.0. The types ``8'',
- ``16'', ``32'',
``64'', and ``ptr'' and all of the acquire and release vari
- ants were
added in FreeBSD 5.0 as well. The atomic_fetchadd() opera
- tions were
added in FreeBSD 6.0.
- BSD October 27, 2000