Skip to content

Commit 90dde80

Browse files
dangelogTomasz Kamiński
andcommitted
libstdc++: Handle cv-qualified types in atomic and atomic_ref [PR115402]
Implements P3233R1 (DR for C++20/C++11, fixes LWG 4069 and 3508). This commit implements std::atomic_ref<cv T> support (LWG3508) as DR for C++20, by extractingparts of the __atomic_ref class (that atomic_ref inherits from) into a further base class (__atomic_ref_base): * __atomic_ref_base<const T> implements non-mutating (const) atomic API. Single base class is used, and the difference in is_always_lock_free and required_aligment values between types are handled by _S_is_always_lock_free, _S_required_aligment helper functions. * __atomic_ref_base<T> implements the common atomic APIs. The non-mutating operations are handled by inherting from __atomic_ref_base<const T> partial partial specialization. Tu support that __atomic_ref_base<const T> stores mutable pointer to T, and performs const_cast in constructor. * __atomic_ref<T, ....> inherits from __atomic_ref_base<T>, and implement type-specific mutable APIs (fetch_add, -=, ...) and difference_type member type. * __atomic_ref<const T, ...> inherits from __atomic_ref_base<const T> and adds different_type member, whose presence and denoted type depends on T. The __atomic_ref specialization selection is adjusted to handle cv-qualified bool (add remove_cv_t) and pointer types. To handle the later, additional constant template parameter is introduced. The atomic wait and notify operations are currently not supported for volatile types, to signal that static assert is added to corresponding methods of atomic_ref. At the same time, disable support for cv-qualified types in std::atomic (for instance, std::atomic<volatile T> isn't meaningful; one should use volatile std::atomic<T>), again as per the paper, resolving LWG4069 as DR for C++11. This only affects atomic<volatile T>, as specialization atomic with const-qualifed types was already producing an compile-time error. PR libstdc++/115402 libstdc++-v3/ChangeLog: * include/bits/atomic_base.h (__atomic_ref_base<const _Tp>) (__atomic_ref_base<_Tp>): Define by extracting common methods from atomic_ref specializations. (__atomic_ref<_Tp, In, Fp, Pt>): Inherit from __atomic_ref_base and remove extracted method. (__atomic_ref<const _Tp, In, Fp, Pt>): Define. * include/std/atomic (std::atomic): Added an * testsuite/29_atomics/atomic/requirements/types_neg.cc: Add test for volatile qualified types. * testsuite/29_atomics/atomic_ref/bool.cc: Move the content to op_support.cc, add test for bool. * testsuite/29_atomics/atomic_ref/op_support.cc: New test expanded from atomic_ref/bool.cc. * testsuite/29_atomics/atomic_ref/cv_qual.cc: New test. * testsuite/29_atomics/atomic_ref/requirements_neg.cc: New test. * testsuite/29_atomics/atomic_ref/deduction.cc: Add tests for cv-qualified types. * testsuite/29_atomics/atomic_ref/float.cc: Likewise. * testsuite/29_atomics/atomic_ref/generic.cc: Likewise. * testsuite/29_atomics/atomic_ref/integral.cc: Likewise. * testsuite/29_atomics/atomic_ref/pointer.cc: Likewise. * testsuite/29_atomics/atomic_ref/requirements.cc: Likewise. * testsuite/29_atomics/atomic_ref/wait_notify.cc: Add tests for const qualified types. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com> Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
1 parent b9a2dfb commit 90dde80

File tree

14 files changed

+621
-439
lines changed

14 files changed

+621
-439
lines changed

libstdc++-v3/include/bits/atomic_base.h

Lines changed: 177 additions & 389 deletions
Large diffs are not rendered by default.

libstdc++-v3/include/std/atomic

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
217217
static_assert(sizeof(_Tp) > 0,
218218
"Incomplete or zero-sized types are not supported");
219219

220+
// _GLIBCXX_RESOLVE_LIB_DEFECTS
221+
// 4069. std::atomic<volatile T> should be ill-formed
222+
static_assert(is_same<_Tp, typename remove_cv<_Tp>::type>::value,
223+
"template argument for std::atomic must not be const or volatile");
224+
220225
#if __cplusplus > 201703L
221226
static_assert(is_copy_constructible_v<_Tp>);
222227
static_assert(is_move_constructible_v<_Tp>);

libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
#include <atomic>
2121

22-
std::atomic<const int> a; // { dg-error "here" }
22+
std::atomic<const int> ca; // { dg-error "here" }
23+
std::atomic<volatile int> va; // { dg-error "here" }
24+
std::atomic<const volatile int> cva; // { dg-error "here" }
2325
// { dg-error "assignment to read-only type" "" { target *-*-* } 0 }
2426

2527
struct MoveOnly
Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,85 @@
1-
// { dg-do compile { target c++20 } }
1+
// Copyright (C) 2019-2025 Free Software Foundation, Inc.
2+
//
3+
// This file is part of the GNU ISO C++ Library. This library is free
4+
// software; you can redistribute it and/or modify it under the
5+
// terms of the GNU General Public License as published by the
6+
// Free Software Foundation; either version 3, or (at your option)
7+
// any later version.
8+
9+
// This library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License along
15+
// with this library; see the file COPYING3. If not see
16+
// <http://www.gnu.org/licenses/>.
17+
18+
// { dg-do run { target c++20 } }
19+
// { dg-require-atomic-cmpxchg-word "" }
20+
// { dg-add-options libatomic }
221

322
#include <atomic>
23+
#include <testsuite_hooks.h>
24+
25+
void
26+
test01()
27+
{
28+
bool value;
29+
30+
{
31+
const auto mo = std::memory_order_relaxed;
32+
std::atomic_ref<bool> a(value);
33+
bool ok = a.is_lock_free();
34+
if constexpr (std::atomic_ref<bool>::is_always_lock_free)
35+
VERIFY( ok );
36+
a = false;
37+
VERIFY( !a.load() );
38+
VERIFY( !a.load(mo) );
39+
a.store(true);
40+
VERIFY( a.load() );
41+
auto v = a.exchange(false);
42+
VERIFY( !a.load() );
43+
VERIFY( v );
44+
v = a.exchange(true, mo);
45+
VERIFY( a.load() );
46+
VERIFY( !v );
47+
48+
auto expected = a.load();
49+
while (!a.compare_exchange_weak(expected, false, mo, mo))
50+
{ /* weak form can fail spuriously */ }
51+
VERIFY( !a.load() );
52+
VERIFY( expected );
53+
54+
ok = a.compare_exchange_strong(expected, true);
55+
VERIFY( !ok && !a.load() && !expected );
56+
57+
ok = a.compare_exchange_strong(expected, true);
58+
VERIFY( ok && a.load() && !expected );
59+
}
60+
}
61+
62+
void
63+
test02()
64+
{
65+
bool b = false;
66+
std::atomic_ref<bool> a0(b);
67+
std::atomic_ref<bool> a1(b);
68+
std::atomic_ref<const bool> a1c(b);
69+
std::atomic_ref<volatile bool> a1v(b);
70+
std::atomic_ref<const volatile bool> a1cv(b);
71+
std::atomic_ref<bool> a2(a0);
72+
b = true;
73+
VERIFY( a1.load() );
74+
VERIFY( a1c.load() );
75+
VERIFY( a1v.load() );
76+
VERIFY( a1cv.load() );
77+
VERIFY( a2.load() );
78+
}
479

5-
template<class T> concept has_and = requires (T& a) { a &= false; };
6-
template<class T> concept has_or = requires (T& a) { a |= false; };
7-
template<class T> concept has_xor = requires (T& a) { a ^= false; };
8-
template<class T> concept has_fetch_add = requires (T& a) { a.fetch_add(true); };
9-
template<class T> concept has_fetch_sub = requires (T& a) { a.fetch_sub(true); };
10-
11-
static_assert( not has_and<std::atomic_ref<bool>> );
12-
static_assert( not has_or<std::atomic_ref<bool>> );
13-
static_assert( not has_xor<std::atomic_ref<bool>> );
14-
static_assert( not has_fetch_add<std::atomic_ref<bool>> );
15-
static_assert( not has_fetch_sub<std::atomic_ref<bool>> );
80+
int
81+
main()
82+
{
83+
test01();
84+
test02();
85+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (C) 2019-2025 Free Software Foundation, Inc.
2+
//
3+
// This file is part of the GNU ISO C++ Library. This library is free
4+
// software; you can redistribute it and/or modify it under the
5+
// terms of the GNU General Public License as published by the
6+
// Free Software Foundation; either version 3, or (at your option)
7+
// any later version.
8+
9+
// This library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License along
15+
// with this library; see the file COPYING3. If not see
16+
// <http://www.gnu.org/licenses/>.
17+
18+
// { dg-do run { target c++20 } }
19+
// { dg-require-atomic-cmpxchg-word "" }
20+
// { dg-add-options libatomic }
21+
22+
#include <atomic>
23+
#include <testsuite_hooks.h>
24+
25+
struct X
26+
{
27+
X() = default;
28+
X(int i) : i(i) { }
29+
int i;
30+
31+
friend bool
32+
operator==(X, X) = default;
33+
};
34+
35+
template<typename V>
36+
void
37+
test01(V v0, V v1)
38+
{
39+
V value;
40+
41+
if constexpr (std::atomic_ref<V>::is_always_lock_free)
42+
{
43+
std::atomic_ref<volatile V> a(value);
44+
VERIFY( a.is_lock_free() );
45+
46+
a = v0;
47+
VERIFY( V(a) == v0 );
48+
VERIFY( a.load() == v0 );
49+
50+
a.store(v1);
51+
VERIFY( a.load() == v1 );
52+
53+
V last = a.exchange(v0);
54+
VERIFY( a.load() == v0 );
55+
VERIFY( last == v1 );
56+
57+
V expected = a.load();
58+
while (!a.compare_exchange_weak(expected, v1))
59+
{ /* weak form can fail spuriously */ }
60+
VERIFY( a.load() == v1 );
61+
VERIFY( expected == v0 );
62+
63+
bool ok;
64+
ok = a.compare_exchange_strong(expected, v0);
65+
VERIFY( !ok && a.load() == v1 && expected == v1 );
66+
67+
ok = a.compare_exchange_strong(expected, v0);
68+
VERIFY( ok && a.load() == v0 && expected == v1 );
69+
70+
std::atomic_ref<const volatile V> cva(value);
71+
VERIFY( cva.is_lock_free() );
72+
VERIFY( V(cva) == v0 );
73+
VERIFY( cva.load() == v0 );
74+
}
75+
76+
value = v0;
77+
std::atomic_ref<const V> ca(value);
78+
bool lf = ca.is_lock_free();
79+
if constexpr (std::atomic_ref<V>::is_always_lock_free)
80+
VERIFY( lf );
81+
VERIFY( V(ca) == v0 );
82+
VERIFY( ca.load() == v0 );
83+
}
84+
85+
int
86+
main()
87+
{
88+
int x;
89+
test01<bool>(false, true);
90+
test01<int>(1, 2);
91+
test01<float>(1.2, 3.4);
92+
test01<int*>(&x, &x+1);
93+
test01<X>(12, 13);
94+
}

libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,29 @@
1919

2020
#include <atomic>
2121

22+
template <typename T>
2223
void
23-
test01()
24+
test_impl(T v)
2425
{
25-
int i = 0;
26-
std::atomic_ref a0(i);
27-
static_assert(std::is_same_v<decltype(a0), std::atomic_ref<int>>);
28-
29-
float f = 1.0f;
30-
std::atomic_ref a1(f);
31-
static_assert(std::is_same_v<decltype(a1), std::atomic_ref<float>>);
26+
std::atomic_ref a(v);
27+
static_assert(std::is_same_v<decltype(a), std::atomic_ref<T>>);
28+
}
3229

33-
int* p = &i;
34-
std::atomic_ref a2(p);
35-
static_assert(std::is_same_v<decltype(a2), std::atomic_ref<int*>>);
30+
template <typename T>
31+
void
32+
test(T v)
33+
{
34+
test_impl<T>(v);
35+
test_impl<const T>(v);
36+
test_impl<volatile T>(v);
37+
test_impl<const volatile T>(v);
38+
}
3639

40+
int main()
41+
{
42+
test<int>(0);
43+
test<float>(1.0f);
44+
test<int*>(nullptr);
3745
struct X { } x;
38-
std::atomic_ref a3(x);
39-
static_assert(std::is_same_v<decltype(a3), std::atomic_ref<X>>);
46+
test<X>(x);
4047
}

libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,19 @@ test04()
299299
{
300300
if constexpr (std::atomic_ref<float>::is_always_lock_free)
301301
{
302-
float i = 0;
303-
float* ptr = 0;
304-
std::atomic_ref<float*> a0(ptr);
305-
std::atomic_ref<float*> a1(ptr);
306-
std::atomic_ref<float*> a2(a0);
307-
a0 = &i;
308-
VERIFY( a1 == &i );
309-
VERIFY( a2 == &i );
302+
float i = 0.0f;
303+
std::atomic_ref<float> a0(i);
304+
std::atomic_ref<float> a1(i);
305+
std::atomic_ref<const float> a1c(i);
306+
std::atomic_ref<volatile float> a1v(i);
307+
std::atomic_ref<const volatile float> a1cv(i);
308+
std::atomic_ref<float> a2(a0);
309+
a0 = 1.0f;
310+
VERIFY( a1 == 1.0f );
311+
VERIFY( a1c == 1.0f );
312+
VERIFY( a1v == 1.0f );
313+
VERIFY( a1cv == 1.0f );
314+
VERIFY( a2 == 1.0f );
310315
}
311316
}
312317

libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,15 @@ test02()
108108
X i;
109109
std::atomic_ref<X> a0(i);
110110
std::atomic_ref<X> a1(i);
111+
std::atomic_ref<const X> a1c(i);
112+
std::atomic_ref<volatile X> a1v(i);
113+
std::atomic_ref<const volatile X> a1cv(i);
111114
std::atomic_ref<X> a2(a0);
112115
a0 = 42;
113116
VERIFY( a1.load() == 42 );
117+
VERIFY( a1c.load() == 42 );
118+
VERIFY( a1v.load() == 42 );
119+
VERIFY( a1cv.load() == 42 );
114120
VERIFY( a2.load() == 42 );
115121
}
116122

libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,15 @@ test03()
302302
int i = 0;
303303
std::atomic_ref<int> a0(i);
304304
std::atomic_ref<int> a1(i);
305+
std::atomic_ref<const int> a1c(i);
306+
std::atomic_ref<volatile int> a1v(i);
307+
std::atomic_ref<const volatile int> a1cv(i);
305308
std::atomic_ref<int> a2(a0);
306309
a0 = 42;
307310
VERIFY( a1 == 42 );
311+
VERIFY( a1c == 42 );
312+
VERIFY( a1v == 42 );
313+
VERIFY( a1cv == 42 );
308314
VERIFY( a2 == 42 );
309315
}
310316

0 commit comments

Comments
 (0)