Skip to content

Commit 39e8e77

Browse files
dalg24ldionne
authored andcommitted
[libc++] Increase atomic_ref's required alignment for small types (#99654)
This patch increases the alignment requirement for std::atomic_ref such that we can guarantee lockfree operations more often. Specifically, we require types that are 1, 2, 4, 8, or 16 bytes in size to be aligned to at least their size to be used with std::atomic_ref. This is the case for most types, however a notable exception is `long long` on x86, which is 8 bytes in length but has an alignment of 4. As a result of this patch, one has to be more careful about the alignment of objects used with std::atomic_ref. Failure to provide a properly-aligned object to std::atomic_ref is a precondition violation and is technically UB. On the flipside, this allows us to provide an atomic_ref that is actually lockfree more often, which is an important QOI property. More information in the discussion at #99570 (comment). Co-authored-by: Louis Dionne <[email protected]> (cherry picked from commit 59ca618)
1 parent 23f3b64 commit 39e8e77

File tree

2 files changed

+12
-7
lines changed

2 files changed

+12
-7
lines changed

libcxx/include/__atomic/atomic_ref.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ struct __get_aligner_instance {
5757

5858
template <class _Tp>
5959
struct __atomic_ref_base {
60-
protected:
61-
_Tp* __ptr_;
62-
63-
_LIBCPP_HIDE_FROM_ABI __atomic_ref_base(_Tp& __obj) : __ptr_(std::addressof(__obj)) {}
64-
6560
private:
6661
_LIBCPP_HIDE_FROM_ABI static _Tp* __clear_padding(_Tp& __val) noexcept {
6762
_Tp* __ptr = std::addressof(__val);
@@ -108,10 +103,14 @@ struct __atomic_ref_base {
108103

109104
friend struct __atomic_waitable_traits<__atomic_ref_base<_Tp>>;
110105

106+
// require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to at least their size to be potentially
107+
// used lock-free
108+
static constexpr size_t __min_alignment = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || (sizeof(_Tp) > 16) ? 0 : sizeof(_Tp);
109+
111110
public:
112111
using value_type = _Tp;
113112

114-
static constexpr size_t required_alignment = alignof(_Tp);
113+
static constexpr size_t required_alignment = alignof(_Tp) > __min_alignment ? alignof(_Tp) : __min_alignment;
115114

116115
// The __atomic_always_lock_free builtin takes into account the alignment of the pointer if provided,
117116
// so we create a fake pointer with a suitable alignment when querying it. Note that we are guaranteed
@@ -218,6 +217,12 @@ struct __atomic_ref_base {
218217
}
219218
_LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept { std::__atomic_notify_one(*this); }
220219
_LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept { std::__atomic_notify_all(*this); }
220+
221+
protected:
222+
typedef _Tp _Aligned_Tp __attribute__((aligned(required_alignment)));
223+
_Aligned_Tp* __ptr_;
224+
225+
_LIBCPP_HIDE_FROM_ABI __atomic_ref_base(_Tp& __obj) : __ptr_(std::addressof(__obj)) {}
221226
};
222227

223228
template <class _Tp>

libcxx/test/std/atomics/atomics.ref/is_always_lock_free.pass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void check_always_lock_free(std::atomic_ref<T> const& a) {
5454
#define CHECK_ALWAYS_LOCK_FREE(T) \
5555
do { \
5656
typedef T type; \
57-
type obj{}; \
57+
alignas(std::atomic_ref<type>::required_alignment) type obj{}; \
5858
std::atomic_ref<type> a(obj); \
5959
check_always_lock_free(a); \
6060
} while (0)

0 commit comments

Comments
 (0)