Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Limitations
PrevUpHomeNext

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:

  • Aggregate initialization syntax is not supported: Since Boost.Atomic sometimes uses storage type that is different from the value type, the 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.
  • Initializing constructor is not 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.
  • Default constructor is not trivial in C++03: Because the initializing constructor has to be defined in 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).
  • C++03 compilers may transform computation dependency to control dependency: Crucially, 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.
  • Fence operations may enforce "too strong" compiler ordering: Semantically, 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.
  • Atomic operations may enforce "too strong" memory ordering in debug mode: On some compilers, disabling optimizations makes it impossible to provide memory ordering constraints as compile-time constants to the compiler intrinsics. This causes the compiler to silently ignore the provided constraints and choose the "strongest" memory order (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.
  • No interprocess fallback: using 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.
  • Memory type requirements: Atomic objects cannot be placed in read-only memory, even if they are only read from. Here, read-only means that the memory region is mapped with read-only permissions by the OS, regardless of the 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.
  • Signed integers must use two's complement representation: Boost.Atomic makes this requirement in order to implement conversions between signed and unsigned integers internally. C++11 requires all atomic arithmetic operations on integers to be well defined according to two's complement arithmetics, which means that Boost.Atomic has to operate on unsigned integers internally to avoid undefined behavior that results from signed integer overflows. Platforms with other signed integer representations are not supported. Note that C++20 makes two's complement representation of signed integers mandatory.
  • Limited support for types with padding bits: There is no portable way to clear the padding bits of an object. Doing so requires support from the compiler, which is typically available in compilers supporting C++20. Without clearing the padding, 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.

PrevUpHomeNext