Skip to content

[libc++] Increase atomic_ref's required alignment for small types #99654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

17 changes: 11 additions & 6 deletions libcxx/include/__atomic/atomic_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ struct __get_aligner_instance {

template <class _Tp>
struct __atomic_ref_base {
protected:
_Tp* __ptr_;

_LIBCPP_HIDE_FROM_ABI __atomic_ref_base(_Tp& __obj) : __ptr_(std::addressof(__obj)) {}

private:
_LIBCPP_HIDE_FROM_ABI static _Tp* __clear_padding(_Tp& __val) noexcept {
_Tp* __ptr = std::addressof(__val);
Expand Down Expand Up @@ -108,10 +103,14 @@ struct __atomic_ref_base {

friend struct __atomic_waitable_traits<__atomic_ref_base<_Tp>>;

// require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to at least their size to be potentially
// used lock-free
static constexpr size_t __min_alignment = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || (sizeof(_Tp) > 16) ? 0 : sizeof(_Tp);

public:
using value_type = _Tp;

static constexpr size_t required_alignment = alignof(_Tp);
static constexpr size_t required_alignment = alignof(_Tp) > __min_alignment ? alignof(_Tp) : __min_alignment;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should calculate the alignment using something like this?

template <class T>
constexpr size_t calc_alignment() {
    size_t align = alignof(T);
    while (align <= alignof(max_align_t)) {
        if (__atomic_always_lock_free(sizeof(T), align))
            return align;
        align *= 2;
    }
    return alignof(T);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this, I think this directly translates to what we are actually trying to achieve (which is to calculate the smallest alignment that allows us to be lockfree).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not a valid implementation, because it causes the ABI of the type to change depending on the microarchitecture selection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL


// The __atomic_always_lock_free builtin takes into account the alignment of the pointer if provided,
// so we create a fake pointer with a suitable alignment when querying it. Note that we are guaranteed
Expand Down Expand Up @@ -218,6 +217,12 @@ struct __atomic_ref_base {
}
_LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept { std::__atomic_notify_one(*this); }
_LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept { std::__atomic_notify_all(*this); }

protected:
typedef _Tp _Aligned_Tp __attribute__((aligned(required_alignment)));
_Aligned_Tp* __ptr_;

_LIBCPP_HIDE_FROM_ABI __atomic_ref_base(_Tp& __obj) : __ptr_(std::addressof(__obj)) {}
};

template <class _Tp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void check_always_lock_free(std::atomic_ref<T> const& a) {
#define CHECK_ALWAYS_LOCK_FREE(T) \
do { \
typedef T type; \
type obj{}; \
alignas(std::atomic_ref<type>::required_alignment) type obj{}; \
std::atomic_ref<type> a(obj); \
check_always_lock_free(a); \
} while (0)
Expand Down
Loading