...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
While Boost.Atomic strives to implement the atomic operations from C++11 and later as faithfully as possible, there are a few limitations that cannot be lifted without compiler support:
atomic<>
template needs an initialization
constructor that performs the necessary conversion. This makes atomic<>
a non-aggregate type and prohibits aggregate initialization syntax (atomic<int> a = {10}
).
Boost.Atomic does support direct and unified
initialization syntax though. Advice:
Always use direct initialization (atomic<int> a(10)
)
or unified initialization (atomic<int> a{10}
)
syntax.
constexpr
for some types: For value types other than integral types,
bool
, enums, floating point
types and classes without padding, atomic<>
initializing constructor needs
to perform runtime conversion to the storage type and potentially clear
padding bits. This limitation may be lifted for more categories of types
in the future.
atomic<>
,
the default constructor must also be defined. In C++03 the constructor
cannot be defined as defaulted and therefore it is not trivial. In C++11
the constructor is defaulted (and trivial, if the default constructor of
the value type is). In any case, the default constructor of atomic<>
performs default initialization of the atomic value, as required in C++11.
Advice: In C++03, do not use Boost.Atomic in contexts where trivial default constructor
is important (e.g. as a global variable which is required to be statically
initialized).
memory_order_consume
only affects computationally-dependent operations, but in general there
is nothing preventing a compiler from transforming a computation dependency
into a control dependency. A fully compliant C++11 compiler would be forbidden
from such a transformation, but in practice most if not all compilers have
chosen to promote memory_order_consume
to memory_order_acquire
instead (see this
gcc bug for example). In the current implementation Boost.Atomic
follows that trend, but this may change in the future. Advice:
In general, avoid memory_order_consume
and use memory_order_acquire
instead. Use memory_order_consume
only in conjunction with pointer values, and only if you can ensure that
the compiler cannot speculate and transform these into control dependencies.
memory_order_acquire
/memory_order_consume
and memory_order_release
need to restrain
reordering of memory operations only in one direction. Since in C++03 there
is no way to express this constraint to the compiler, these act as "full
compiler barriers" in C++03 implementation. In corner cases this may
result in a slightly less efficient code than a C++11 compiler could generate.
Boost.Atomic will use compiler intrinsics,
if possible, to express the proper ordering constraints.
memory_order_seq_cst
)
to generate code. Not only this reduces performance, this may hide bugs
in the user's code (e.g. if the user used a wrong memory order constraint,
which caused a data race). Advice: Always
test your code with optimizations enabled.
atomic<T>
in shared memory only works correctly, if atomic<T>::is_lock_free() == true
.
Same with atomic_ref<T>
.
Advice: Use IPC
atomic types for inter-process communication.
const
-qualification.
Boost.Atomic may implement load operations
using read-modify-write instructions on some targets, such as CMPXCHG16B
on x86. The load operation
does not change the object value, but the instruction issues a write to
the memory location nonetheless, so the memory must be writable. There
may be other hardware-specific restrictions on the memory types that can
be used with atomic instructions. Also, the operating system may have additional
restrictions on the memory type and the set of allowed operations on it
to implement waiting and notifying operations correctly. Such requirements
are system-specific. For example, on Mac OS IPC atomics cannot be placed
in stack memory, as notifying operations may spuriously fail to wake up
blocked threads. Boost.Atomic aims to
support more atomic types and operations natively, even if it means not
supporting some rarely useful corner cases. Advice:
Non-IPC atomics can be safely used in regular read-write process-local
memory (e.g. stack or obtained via malloc
or new
), and IPC atomics can
be used in read-write process-shared memory (e.g. obtained via shm_open
+ mmap
). Any special memory types,
such as mapped device memory or memory mapped with special caching strategies,
are not guaranteed to work and are subject to system-specific restrictions.
compare_exchange_strong
/compare_exchange_weak
are not able to
function as intended, as they will fail spuriously because of mismatching
contents in the padding. Note that other operations may be implemented
in terms of compare_exchange_*
internally. If the compiler does not offer
a way to clear padding bits, Boost.Atomic
does support padding bits for floating point types on platforms where the
location of the padding bits is known at compile time, but otherwise types
with padding cannot be supported. Note that, as discussed in atomic
description, unions with
padding bits cannot be reliably supported even on compilers that do offer
a way to clear the padding.