Most platforms that support futexes or similar mechanisms support it
for 32-bit integers, which makes it more preferred to implement
atomic_flag efficiently. Most architectures also support 32-bit atomic
operations natively as well.
Also, reduced code duplication in instantiating operation backends.
The generic implementation is based on the lock pool. A list of condition
variables (or waiting futexes) is added per lock. Basically, the lock
pool serves as a global hash table, where each lock represents
a bucket and each wait state is an element. Every wait operation
allocates a wait state keyed on the pointer to the atomic object. Notify
operations look up the wait state by the atomic pointer and notify
the condition variable/futex. The corresponding lock needs to be acquired
to protect the wait state list during all wait/notify operations.
Backends not involving the lock pool are going to be added later.
The implementation of wait operation extends the C++20 definition in that
it returns the newly loaded value instead of void. This allows the caller
to avoid loading the value himself.
The waiting/notifying operations are not address-free. Address-free variants
will be added later.
Added tests for the new operations and refactored existing tests for atomic
operations. Added docs for the new operations.
bitwise_cast is more lightweight in terms of compile times and is equivalent
to integral_truncate in case of atomic_ref as its storage type is always
of the same size as the value type.
Due to BOOST_ATOMIC_DETAIL_ALIGNED_VAR_TPL macro expansion, the aligner
data member was made an array, which increased the size of the resulting
buffer_storage. This caused memory corruption with atomic_ref, which
requires the storage type to be of the same size as the value.
To protect against such mistakes in the future, changed
BOOST_ATOMIC_DETAIL_ALIGNED_VAR_TPL and BOOST_ATOMIC_DETAIL_ALIGNED_VAR
definitions to prohibit their direct use with arrays.
Increased lock pool size to 64 entries and improve pool efficiency:
- Shift off lower pointer bits that are zero due to object alignment.
- Mix higher pointer bits to account for alignment typically imposed by
malloc/new implementations.
- Use bit masking to select a lock from pool, given that the pool size
is a power of 2 now.
Also, extracted (u)intptr_t definition to a common header to avoid code
duplication.
This simplifies the code slightly without changing semantics. static_cast was
already used in atomic constructor in order to make it constexpr, and this
commit makes the rest of the code consistent.
The compiler allows to apply alignas but later fails to pass arguments
of the aligned types to functions with error C2719. At the same time,
std::max_align_t has alignment of 8 and the error doesn't show up when
the type is aligned using the union trick. Thus we disable alignas
for MSVC 14.0 in 32-bit mode.
Also, use std::max_align_t on MSVC, when possible.
gcc 4.7 does not support constexpr constructors that initialize one member
of an anonymous union data member of the class. atomic and atomic_flag
no longer have constexpr constructors on this compiler.
gcc older than 8.1 and clang older than 8.0 produce incorrect results of
std::alignment_of for 64-bit types on 32-bit x86. Use boost::alignment_of,
which contains workarounds for these compilers.
gcc 4.8 requires that the argument of alignas is a literal constant and does not
accept a constant expression.
This workaround is temporary, until Boost.Config updates BOOST_NO_CXX11_ALIGNAS:
https://github.com/boostorg/config/pull/324
On 32-bit x86, 64-bit integers have 4-byte alignment, which resulted in
a non-integral type being selected for storage type in case of lock-based
atomic_ref. This broke arithmetic and bitwise operations on atomic_ref.
We now select an integral type based on its native alignment, not the storage
alignment we require for atomic operations. This is fine in case of lock-based
backend.
Also, extended buffer_storage with support for specifying alignment and removed
aligned_buffer_storage and storage128_t.
Lock-based operations have no reason to require object alignment higher
than alignof(T). This commit implements a special storage type for
lock-based operations, which has the same alignment as value_type.
Also, added tests to verify required_alignment correctness both
in lock-free and lock-based cases.
The macro was used to highlight the (op)_and_test methods of atomic<>
that changed the returned value to the opposite in Boost 1.67. The old
behavior was only released in 1.66 and the macro was a means to help
1.66 users to transition to the new releases.
1.67 will have been released 2 years before the upcoming 1.73 release,
in which this macro will be removed.
This commit changes how storage alignment is enforced and ensures
that the storage is sufficiently aligned for both atomic operations
and direct access to the stored value. This allowed to implement
the new value() accessor, which returns a reference to the stored
value. This is unlike the previously available storage() accessor,
which returns a reference to storage_type and requires users to cast
the reference to value_type, which is potentially unsafe.
The public storage() accessor is now deprecated in favor of value()
and storage_type - in favor of value_type. The deprecation warnings
can be disabled by defining BOOST_ATOMIC_SILENCE_STORAGE_DEPRECATION.
When emulated operations backend is used, the storage type still has
high alignment requirement, possibly higher than that of the user's type.
This means that code generated for operations may be incorrect for the
underaligned user's objects, which can cause alignment violation crashes.
This was causing Cygwin and MinGW test failures.
For now, use the alignment requirement from the operations, even in
the lock-based mode. This increases alignment requirement unnecessarily,
because lock-based implementation could work with reduced alignment
storage. A better fix requires a different approach to storage type
generation. It will be done in a later commit.
We currently don't support structs with padding bits, so the checks
are useless. Also, updated docs so that users are not given the idea
that structs with padding bits are supported.
The implementation used to generate 32-bit bts/btr/btc for 8 and 16-bit
atomics, which could result in alignment and access violation and possibly
data corruption. 32 and 64-bit atomics are unaffected.
This commit fixes operaend width for 16-bit atomics. For 8-bit atomics the
generic implementation is used based on or/and/xor instructions since there
are no 8-bit bts/btr/btc.
We currently don't support clearing internal padding in structures,
and we cannot detect tail padding in structures either. So for now there
is no point in the CAS loop during atomic_ref construction.