Skip to content

[libc++] Implement C++20 atomic_ref #76647

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

Merged
merged 99 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
f173470
[libc++][atomic_ref] Refactor atomic_base_impl class and friends
dalg24 Dec 31, 2023
3d8d9db
[libc++][atomic_ref] Enable atomic load/exchange for non-default cons…
dalg24 Dec 31, 2023
f05a599
[libc++][atomic_ref] Add _LIBCPP_CHECK_WAIT_MEMORY_ORDER macro
dalg24 Dec 31, 2023
6e01477
[libc++][atomic_ref] Implement C++20 atomic_ref
dalg24 Dec 31, 2023
1575d41
[libc++][atomic_ref] Add tests for atomic_ref
dalg24 Dec 31, 2023
2767cf0
[libc++][atomic_ref] Fix shadow warning
dalg24 Dec 31, 2023
d79c427
[libc++][atomic_ref] Revert all changes to __atomic/cxx_atomic_impl.h
dalg24 Jan 1, 2024
722217b
[libc++][atomic_ref] move __to_gcc_[failure_]order to its own header …
dalg24 Jan 1, 2024
1a0463f
[libc++][atomic_ref] Reimplement atomic_ref in terms of the GCC __ato…
dalg24 Jan 1, 2024
5e5a5cd
[libc++][atomic_ref] add header to module map
dalg24 Jan 1, 2024
d5525ad
[libc++][atomic_ref] fixup atomic_{add,sub} for floating points
dalg24 Jan 1, 2024
a974df0
[libc++][atomic_ref] cleanup header includes
dalg24 Jan 1, 2024
4172781
[libc++][atomic_ref] annotate wait, notify_one, and notify_all as exp…
dalg24 Jan 1, 2024
02220ba
[libc++][atomic_ref] fix generic-modules build
dalg24 Jan 5, 2024
8cc91fb
[libc++][atomic_ref] uncomment `using std::atomic_ref;` in atomic module
dalg24 Jan 5, 2024
28e4f55
[libc++][atomic_ref] add missing header include for uintptr_t caught …
dalg24 Jan 5, 2024
0391ec0
[libc++][atomic_ref] Attempt to use __cxx_atomic_wait
dalg24 Jan 7, 2024
82fcdad
[libc++][atomic_ref] Fiddle with hardening-mode annotations in precon…
dalg24 Jan 8, 2024
4fec87c
[libc++][atomic_ref] Fix cxx03 CI build
dalg24 Jan 8, 2024
f763a3a
Revert "[libc++][atomic_ref] Fiddle with hardening-mode annotations i…
dalg24 Jan 15, 2024
259afbf
[libc++][atomic_ref] Robust against ADL lookup
dalg24 Jan 15, 2024
f0cbfec
[libc++][atomic_ref] Qualify all __to_gcc_order calls
dalg24 Feb 5, 2024
46ab572
[libc++][atomic_ref] Simplify inheritance from __atomic_ref_base
dalg24 Feb 5, 2024
f9edc9f
[libc++][atomic_ref] Prefer snake_case
dalg24 Feb 5, 2024
2438001
[libc++][atomic_ref] Use proper _LIBCPP_ASSERT_* macro
dalg24 Feb 5, 2024
255559f
[libc++][atomic_ref] Prefix assertion messages with "atomic_ref:"
dalg24 Feb 5, 2024
8ebfe2d
[libc++][atomic_ref] Fix XFAIL annotations
dalg24 Feb 5, 2024
a745599
[libc++][atomic_ref] Fixup conversion test
dalg24 Feb 5, 2024
07ecf37
[libc++][atomic_ref] Drop test that the ctor is explicit since there …
dalg24 Feb 5, 2024
ba4ec71
[libc++][atomic_ref] Fixup do not use -Wno-ctad-maybe-unsupported in …
dalg24 Feb 5, 2024
b476c80
[libc++][atomic_ref] Rename {type -> requires-trivially-copyable}.ver…
dalg24 Feb 5, 2024
21bf066
[libc++][atomic_ref] Const the atomic_refs in the tests
dalg24 Feb 5, 2024
6df2fa8
[libc++][atomic_ref] Let required_alignement test check both at runti…
dalg24 Feb 5, 2024
05dad2c
[libc++][atomic_ref] Add a release note and update the status page
dalg24 Feb 5, 2024
991ccb6
[libc++][atomic_ref] Update generated files
dalg24 Feb 5, 2024
bd870b0
[libc++][atomic_ref] Fix clang-tidy warnings modernize-use-nullptr
dalg24 Feb 6, 2024
c10501c
[libc++][atomic_ref] Use std::same_as<T> trick
dalg24 Feb 6, 2024
6d310d8
[libc++][atomic_ref] Forgot to update assertion msg in tests
dalg24 Feb 6, 2024
7d8a1c8
[libc++][atomic_ref] Fix test compare_exchange_{strong,weak} assertio…
dalg24 Feb 6, 2024
89424db
[libc++][atomic_ref] Forgot to use addressof in __atomic_ref_base con…
dalg24 Feb 6, 2024
956f4b3
[libc++][atomic_ref] Prepare the code for clearing padding bits
dalg24 Feb 7, 2024
6d5c9de
[libc++][atomic_ref] Include missing <cstring> header for memcmp and …
dalg24 Feb 7, 2024
8ad10b6
[libc++][atomic_ref] Avoid sneaky casting `T x(T(1))``
dalg24 Feb 7, 2024
ee7cb80
[libc++][atomic_ref] Check more types for is_always_lock_free
dalg24 Feb 7, 2024
6ef3362
[libc++][atomic_ref] Use fake minimally aligned pointer in is_lock_fr…
dalg24 Feb 7, 2024
ed91c8c
[libc++][atomic_ref] Fixup unsupported hardening mode fast
dalg24 Feb 9, 2024
dee15ac
[libc++][atomic_ref] revert hack to be able to use atomic wait implem…
dalg24 Mar 4, 2024
79876f0
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 Mar 4, 2024
5c0c3c9
[libc++][atomic_ref] Use __atomic_wait following Hui's refactor
dalg24 Mar 4, 2024
a795159
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 Mar 6, 2024
54220e5
[libc++][atomic_ref] Suppress clang-tidy diagnostic when __builtin_cl…
dalg24 Mar 6, 2024
c185cfd
[libc++][atomic_ref] Remove stray semicolon
dalg24 Mar 6, 2024
55ee31a
[libc++][atomic_ref] Mark P0019 as "Complete"
dalg24 Mar 6, 2024
bc5a5df
[libc++][atomic_ref] Per review put typedefs together
dalg24 Mar 6, 2024
4616b46
[libc++][atomic_ref] Fixup release note
dalg24 Mar 8, 2024
fe1027c
[libc++][atomic_ref] Propose new is_[always_]lock_free implementation…
dalg24 Mar 8, 2024
e2b1529
[libc++][atomic_ref] GCC does not provide __builtin_align_up
dalg24 Mar 8, 2024
d935cb4
[libc++][atomic_ref] Prefer reinterpret_cast to C-style cast and qual…
dalg24 Mar 11, 2024
dabe768
[libc++][atomic_ref] Add comment suggested by Louis to describe how t…
dalg24 Mar 11, 2024
3ec8d68
[libc++][atomic_ref] Use protected access specifier in __atomic_ref_base
dalg24 Mar 11, 2024
54b6230
[libc++][atomic_ref] Specify release in cxx20 status table
dalg24 Mar 11, 2024
8a29a08
[libc++][atomic_ref] __clear_padding and __compare_exchange can be pr…
dalg24 Mar 11, 2024
e2e4c6c
[libc++][atomic_ref] Prefer std::byte for raw memory buffers
dalg24 Mar 11, 2024
becd644
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 Mar 11, 2024
4d7c3a8
[libc++][atomic_ref] Instantiate tests for more types
dalg24 Mar 13, 2024
e4da1e0
[libc++][atomic_ref] Instantiate libcxx tests for more types
dalg24 Mar 13, 2024
e09ac7a
[libc++][atomic_ref] assert integral type
dalg24 Mar 14, 2024
cee65ac
[libc++][atomic_ref] casually disable long double tests that failed
dalg24 Mar 14, 2024
7e68d6c
[libc++][atomic_ref] assert integral type in xor
dalg24 Mar 14, 2024
3cc65a5
[libc++][atomic_ref] forgot fetch_sub
dalg24 Mar 14, 2024
f64b44f
[libc++][atomic_ref] Add tests that actually check the aromicity
dalg24 Mar 30, 2024
64941f1
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 Mar 30, 2024
8101aff
[libc++][atomic_ref] delete trailing empty line in test helper that c…
dalg24 Mar 30, 2024
ef5fb5a
[libc++][atomic_ref] fix redeclaration of x shadow warning
dalg24 Apr 1, 2024
e656ca2
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 Apr 12, 2024
9c20e50
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 May 3, 2024
919e8c9
[libc++][atomic_ref] Mark notify_{all,one}/wait test as unsupported w…
dalg24 May 3, 2024
cce18df
Merge branch 'main' of https://github.com/llvm/llvm-project into atom…
dalg24 May 8, 2024
6815d2b
[libc++][atomic_ref] Fixup AtomicRef status entry
dalg24 May 8, 2024
049b3d7
[libc++][atomic_ref] Also mark P1643R1 (add wait/notify to atomic_ref…
dalg24 May 8, 2024
505ddb5
[libc++][atomic_ref] Add empty line before the first REQUIRES
dalg24 May 8, 2024
a593fa0
[libc++][atomic_ref] Add comment where no assertion is expected to fail
dalg24 May 8, 2024
0a03362
[libc++][atomic_ref] Prefer TestEachAtomicType
dalg24 May 8, 2024
41b1eff
[libc++][atomic_ref] Improve misaligned objects test
dalg24 May 8, 2024
4279d70
[libc++][atomic_ref] Add a comment for the definition of is_always_lo…
dalg24 May 8, 2024
12ecd1d
[libc++][atomic_ref] Fix typo memory_or[e]der
dalg24 May 8, 2024
d7ff8c9
[libc++][atomic_ref] Inline test() into main and use TestEachAtomicType
dalg24 May 8, 2024
7492f36
[libc++][atomic_ref] check return type with same_as decltype(auto) idiom
dalg24 May 8, 2024
3d6645b
[libc++][atomic_ref] improve operator&= test
dalg24 May 8, 2024
75d3a66
[libc++][atomic_ref] Fix typo in noexecpt check for operator|=
dalg24 May 8, 2024
9b81d05
[libc++][atomic_ref] Add missing <concepts> header include
dalg24 May 8, 2024
6ae8d85
[libc++][atomic_ref] Fix shadowed declaration warning
dalg24 May 8, 2024
8fef446
[libc++][atomic_ref] Fix silly redefinition of a buffer of raw unitia…
dalg24 May 8, 2024
856d2dd
[libc++][atomic_ref] Add XFAIL: !has-64-bit-atomics
dalg24 May 8, 2024
e39d261
[libc++][atomic_ref] Add XFAIL: !has-64-bit-atomics and XFAIL: availa…
dalg24 May 8, 2024
ee8a50e
[libc++][atomic_ref] Fixup fetch_{add,sub} do not use TestEachFloatin…
dalg24 May 9, 2024
47d3cde
[libc++][atomic_ref] Add XFAIL: !has-1024-bit-atomics to "fix" the wi…
dalg24 May 9, 2024
9c85f18
[libc++][atomic_ref] Attempt to drop XFAIL: !has-64-bit-atomics in a …
dalg24 May 10, 2024
233f568
Merge branch 'main' into atomic_ref
dalg24 May 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Implemented Papers
- P3029R1 - Better ``mdspan``'s CTAD
- P2387R3 - Pipe support for user-defined range adaptors
- P2713R1 - Escaping improvements in ``std::format``
- P0019R8 - ``std::atomic_ref``

Improvements and New Features
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"`P0905R1 <https://wg21.link/P0905R1>`__","CWG","Symmetry for spaceship","Jacksonville","|Complete|","7.0","|spaceship|"
"`P0966R1 <https://wg21.link/P0966R1>`__","LWG","``string::reserve``\ Should Not Shrink","Jacksonville","|Complete| [#note-P0966]_","12.0"
"","","","","","",""
"`P0019R8 <https://wg21.link/P0019R8>`__","LWG","Atomic Ref","Rapperswil","",""
"`P0019R8 <https://wg21.link/P0019R8>`__","LWG","Atomic Ref","Rapperswil","[Complete]","19.0"
"`P0458R2 <https://wg21.link/P0458R2>`__","LWG","Checking for Existence of an Element in Associative Containers","Rapperswil","|Complete|","13.0"
"`P0475R1 <https://wg21.link/P0475R1>`__","LWG","LWG 2511: guaranteed copy elision for piecewise construction","Rapperswil","|Complete|",""
"`P0476R2 <https://wg21.link/P0476R2>`__","LWG","Bit-casting object representations","Rapperswil","|Complete|","14.0"
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ set(files
__atomic/atomic_flag.h
__atomic/atomic_init.h
__atomic/atomic_lock_free.h
__atomic/atomic_ref.h
__atomic/atomic_sync.h
__atomic/check_memory_order.h
__atomic/contention_t.h
Expand All @@ -232,6 +233,7 @@ set(files
__atomic/is_always_lock_free.h
__atomic/kill_dependency.h
__atomic/memory_order.h
__atomic/to_gcc_order.h
__availability
__bit/bit_cast.h
__bit/bit_ceil.h
Expand Down
356 changes: 356 additions & 0 deletions libcxx/include/__atomic/atomic_ref.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
//===---------------------------------------------------------------------===//

#ifndef _LIBCPP___ATOMIC_ATOMIC_REF_H
#define _LIBCPP___ATOMIC_ATOMIC_REF_H

#include <__assert>
#include <__atomic/atomic_sync.h>
#include <__atomic/check_memory_order.h>
#include <__atomic/to_gcc_order.h>
#include <__concepts/arithmetic.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__memory/addressof.h>
#include <__type_traits/has_unique_object_representation.h>
#include <__type_traits/is_trivially_copyable.h>
#include <cstddef>
#include <cstdint>
#include <cstring>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 20

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);
# if __has_builtin(__builtin_clear_padding)
__builtin_clear_padding(__ptr);
# endif
return __ptr;
}

_LIBCPP_HIDE_FROM_ABI static bool __compare_exchange(
_Tp* __ptr, _Tp* __expected, _Tp* __desired, bool __is_weak, int __success, int __failure) noexcept {
if constexpr (
# if __has_builtin(__builtin_clear_padding)
has_unique_object_representations_v<_Tp> || same_as<_Tp, float> || same_as<_Tp, double>
# else
true // NOLINT(readability-simplify-boolean-expr)
# endif
) {
return __atomic_compare_exchange(__ptr, __expected, __desired, __is_weak, __success, __failure);
} else { // _Tp has padding bits and __builtin_clear_padding is available
__clear_padding(*__desired);
_Tp __copy = *__expected;
__clear_padding(__copy);
// The algorithm we use here is basically to perform `__atomic_compare_exchange` on the
// values until it has either succeeded, or failed because the value representation of the
// objects involved was different. This is why we loop around __atomic_compare_exchange:
// we basically loop until its failure is caused by the value representation of the objects
// being different, not only their object representation.
while (true) {
_Tp __prev = __copy;
if (__atomic_compare_exchange(__ptr, std::addressof(__copy), __desired, __is_weak, __success, __failure)) {
return true;
}
_Tp __curr = __copy;
if (std::memcmp(__clear_padding(__prev), __clear_padding(__curr), sizeof(_Tp)) != 0) {
// Value representation without padding bits do not compare equal ->
// write the current content of *ptr into *expected
std::memcpy(__expected, std::addressof(__copy), sizeof(_Tp));
return false;
}
}
}
}

friend struct __atomic_waitable_traits<__atomic_ref_base<_Tp>>;

public:
using value_type = _Tp;

static constexpr size_t required_alignment = alignof(_Tp);

static constexpr bool is_always_lock_free =
__atomic_always_lock_free(sizeof(_Tp), reinterpret_cast<void*>(-required_alignment));

_LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return __atomic_is_lock_free(sizeof(_Tp), __ptr_); }

_LIBCPP_HIDE_FROM_ABI void store(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept
_LIBCPP_CHECK_STORE_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::release || __order == memory_order::seq_cst,
"atomic_ref: memory order argument to atomic store operation is invalid");
__atomic_store(__ptr_, __clear_padding(__desired), std::__to_gcc_order(__order));
}

_LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept {
store(__desired);
return __desired;
}

_LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __order = memory_order::seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
__order == memory_order::seq_cst,
"atomic_ref: memory order argument to atomic load operation is invalid");
alignas(_Tp) byte __mem[sizeof(_Tp)];
auto* __ret = reinterpret_cast<_Tp*>(__mem);
__atomic_load(__ptr_, __ret, std::__to_gcc_order(__order));
return *__ret;
}

_LIBCPP_HIDE_FROM_ABI operator _Tp() const noexcept { return load(); }

_LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
alignas(_Tp) byte __mem[sizeof(_Tp)];
auto* __ret = reinterpret_cast<_Tp*>(__mem);
__atomic_exchange(__ptr_, __clear_padding(__desired), __ret, std::__to_gcc_order(__order));
return *__ret;
}

_LIBCPP_HIDE_FROM_ABI bool
compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__failure == memory_order::relaxed || __failure == memory_order::consume ||
__failure == memory_order::acquire || __failure == memory_order::seq_cst,
"atomic_ref: failure memory order argument to weak atomic compare-and-exchange operation is invalid");
return __compare_exchange(
__ptr_,
std::addressof(__expected),
std::addressof(__desired),
true,
std::__to_gcc_order(__success),
std::__to_gcc_order(__failure));
}
_LIBCPP_HIDE_FROM_ABI bool
compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__failure == memory_order::relaxed || __failure == memory_order::consume ||
__failure == memory_order::acquire || __failure == memory_order::seq_cst,
"atomic_ref: failure memory order argument to strong atomic compare-and-exchange operation is invalid");
return __compare_exchange(
__ptr_,
std::addressof(__expected),
std::addressof(__desired),
false,
std::__to_gcc_order(__success),
std::__to_gcc_order(__failure));
}

_LIBCPP_HIDE_FROM_ABI bool
compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
return __compare_exchange(
__ptr_,
std::addressof(__expected),
std::addressof(__desired),
true,
std::__to_gcc_order(__order),
std::__to_gcc_failure_order(__order));
}
_LIBCPP_HIDE_FROM_ABI bool
compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
return __compare_exchange(
__ptr_,
std::addressof(__expected),
std::addressof(__desired),
false,
std::__to_gcc_order(__order),
std::__to_gcc_failure_order(__order));
}

_LIBCPP_HIDE_FROM_ABI void wait(_Tp __old, memory_order __order = memory_order::seq_cst) const noexcept
_LIBCPP_CHECK_WAIT_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
__order == memory_order::seq_cst,
"atomic_ref: memory order argument to atomic wait operation is invalid");
std::__atomic_wait(*this, __old, __order);
}
_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); }
};

template <class _Tp>
struct __atomic_waitable_traits<__atomic_ref_base<_Tp>> {
static _LIBCPP_HIDE_FROM_ABI _Tp __atomic_load(const __atomic_ref_base<_Tp>& __a, memory_order __order) {
return __a.load(__order);
}
static _LIBCPP_HIDE_FROM_ABI const _Tp* __atomic_contention_address(const __atomic_ref_base<_Tp>& __a) {
return __a.__ptr_;
}
};

template <class _Tp>
struct atomic_ref : public __atomic_ref_base<_Tp> {
static_assert(is_trivially_copyable_v<_Tp>, "std::atomic_ref<T> requires that 'T' be a trivially copyable type");

using __base = __atomic_ref_base<_Tp>;

_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
reinterpret_cast<uintptr_t>(std::addressof(__obj)) % __base::required_alignment == 0,
"atomic_ref ctor: referenced object must be aligned to required_alignment");
}

_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;

_LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }

atomic_ref& operator=(const atomic_ref&) = delete;
};

template <class _Tp>
requires(std::integral<_Tp> && !std::same_as<bool, _Tp>)
struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
using __base = __atomic_ref_base<_Tp>;

using difference_type = __base::value_type;

_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
reinterpret_cast<uintptr_t>(std::addressof(__obj)) % __base::required_alignment == 0,
"atomic_ref ctor: referenced object must be aligned to required_alignment");
}

_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;

_LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }

atomic_ref& operator=(const atomic_ref&) = delete;

_LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_add(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
_LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_sub(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
_LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_and(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
_LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_or(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
_LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
}

_LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
_LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
_LIBCPP_HIDE_FROM_ABI _Tp operator++() const noexcept { return fetch_add(_Tp(1)) + _Tp(1); }
_LIBCPP_HIDE_FROM_ABI _Tp operator--() const noexcept { return fetch_sub(_Tp(1)) - _Tp(1); }
_LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __arg) const noexcept { return fetch_and(__arg) & __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __arg) const noexcept { return fetch_or(__arg) | __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __arg) const noexcept { return fetch_xor(__arg) ^ __arg; }
};

template <class _Tp>
requires std::floating_point<_Tp>
struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
using __base = __atomic_ref_base<_Tp>;

using difference_type = __base::value_type;

_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
reinterpret_cast<uintptr_t>(std::addressof(__obj)) % __base::required_alignment == 0,
"atomic_ref ctor: referenced object must be aligned to required_alignment");
}

_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;

_LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }

atomic_ref& operator=(const atomic_ref&) = delete;

_LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

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

not an issue. we do have builtins for RMW operations for floating points in clang. but i guess gcc does not have it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Honestly I don't remember. IIRC I did this it because I had seen failures in some builds in the CI. I am not sure how we would guard against versions of the builtins that support atomic operations on fp.
I can undo and check more in details what happens if you like. Let me know what you think.

_Tp __old = this->load(memory_order_relaxed);
_Tp __new = __old + __arg;
while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
__new = __old + __arg;
}
return __old;
}
_LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
_Tp __old = this->load(memory_order_relaxed);
_Tp __new = __old - __arg;
while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
__new = __old - __arg;
}
return __old;
}

_LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
};

template <class _Tp>
struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
using __base = __atomic_ref_base<_Tp*>;

using difference_type = ptrdiff_t;

_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp*& __ptr) : __base(__ptr) {}

_LIBCPP_HIDE_FROM_ABI _Tp* operator=(_Tp* __desired) const noexcept { return __base::operator=(__desired); }

atomic_ref& operator=(const atomic_ref&) = delete;

_LIBCPP_HIDE_FROM_ABI _Tp* fetch_add(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_add(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
}
_LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
}

_LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
_LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
_LIBCPP_HIDE_FROM_ABI _Tp* operator++() const noexcept { return fetch_add(1) + 1; }
_LIBCPP_HIDE_FROM_ABI _Tp* operator--() const noexcept { return fetch_sub(1) - 1; }
_LIBCPP_HIDE_FROM_ABI _Tp* operator+=(ptrdiff_t __arg) const noexcept { return fetch_add(__arg) + __arg; }
_LIBCPP_HIDE_FROM_ABI _Tp* operator-=(ptrdiff_t __arg) const noexcept { return fetch_sub(__arg) - __arg; }
};

_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(atomic_ref);

#endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP__ATOMIC_ATOMIC_REF_H
1 change: 1 addition & 0 deletions libcxx/include/__atomic/atomic_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <__atomic/contention_t.h>
#include <__atomic/cxx_atomic_impl.h>
#include <__atomic/memory_order.h>
#include <__atomic/to_gcc_order.h>
#include <__availability>
#include <__chrono/duration.h>
#include <__config>
Expand Down
Loading