File: atomic_helpers.h

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 1,998,492 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (240 lines) | stat: -rw-r--r-- 8,739 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef ATOMIC_HELPERS_H
#define ATOMIC_HELPERS_H

#include <cassert>
#include <cstdint>
#include <cstddef>
#include <type_traits>

#include "test_macros.h"

#if defined(TEST_COMPILER_CLANG)
#  define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE
#  define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE
#  define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE
#  define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE
#  define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE
#  define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE
#elif defined(TEST_COMPILER_GCC)
#  define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
#  define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
#  define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
#  define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
#  define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
#  define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
#elif TEST_COMPILER_MSVC
// This is lifted from STL/stl/inc/atomic on github for the purposes of
// keeping the tests compiling for MSVC's STL. It's not a perfect solution
// but at least the tests will keep running.
//
// Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free.
template <class T, size_t Size = sizeof(T)>
constexpr bool msvc_is_lock_free_macro_value() {
  return (Size <= 8 && (Size & Size - 1) == 0) ? 2 : 0;
}
#  define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>()
#  define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>()
#  define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>()
#  define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>()
#  define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>()
#  define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>()
#else
#  error "Unknown compiler"
#endif

#ifdef TEST_COMPILER_CLANG
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wc++11-extensions"
#endif

enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 };

// We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required
// for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type
// aligned to at least its size is going to be atomic if there exists atomic operations for that size at all,
// which is true on most platforms. This technically reduces our test coverage in the sense that if a type has
// an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report
// that we don't know whether it is lockfree or not.
#define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T))

template <class T>
struct LockFreeStatusInfo {
  static const LockFreeStatus value = LockFreeStatus(
      COMPARE_TYPES(T, char)
          ? TEST_ATOMIC_CHAR_LOCK_FREE
          : (COMPARE_TYPES(T, short)
                 ? TEST_ATOMIC_SHORT_LOCK_FREE
                 : (COMPARE_TYPES(T, int)
                        ? TEST_ATOMIC_INT_LOCK_FREE
                        : (COMPARE_TYPES(T, long)
                               ? TEST_ATOMIC_LONG_LOCK_FREE
                               : (COMPARE_TYPES(T, long long)
                                      ? TEST_ATOMIC_LLONG_LOCK_FREE
                                      : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1))))));

  static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown;
};

#undef COMPARE_TYPES

// This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test.
#if TEST_STD_VER >= 11
static_assert(LockFreeStatusInfo<char>::status_known, "");
static_assert(LockFreeStatusInfo<short>::status_known, "");
static_assert(LockFreeStatusInfo<int>::status_known, "");
static_assert(LockFreeStatusInfo<long>::status_known, "");
static_assert(LockFreeStatusInfo<void*>::status_known, "");

// long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is
// 8 bytes. In that case, atomics may or may not be lockfree based on their address.
static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, "");

// Those should always be lock free: hardcode some expected values to make sure our tests are actually
// testing something meaningful.
static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, "");
static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, "");
static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, "");
#endif

// These macros are somewhat suprising to use, since they take the values 0, 1, or 2.
// To make the tests clearer, get rid of them in preference of LockFreeStatusInfo.
#undef TEST_ATOMIC_CHAR_LOCK_FREE
#undef TEST_ATOMIC_SHORT_LOCK_FREE
#undef TEST_ATOMIC_INT_LOCK_FREE
#undef TEST_ATOMIC_LONG_LOCK_FREE
#undef TEST_ATOMIC_LLONG_LOCK_FREE
#undef TEST_ATOMIC_POINTER_LOCK_FREE

#ifdef TEST_COMPILER_CLANG
#  pragma clang diagnostic pop
#endif

struct UserAtomicType {
  int i;

  explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

  friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; }
};

/*

Enable these once we have P0528

struct WeirdUserAtomicType
{
    char i, j, k; // the 3 chars of doom

    explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

    friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y)
    { return x.i == y.i; }
};

struct PaddedUserAtomicType
{
    char i; int j; // probably lock-free?

    explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

    friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y)
    { return x.i == y.i; }
};

*/

struct LargeUserAtomicType {
  int a[128]; /* decidedly not lock-free */

  LargeUserAtomicType(int d = 0) TEST_NOEXCEPT {
    for (auto&& e : a)
      e = d++;
  }

  friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT {
    for (int i = 0; i < 128; ++i)
      if (x.a[i] != y.a[i])
        return false;
    return true;
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachIntegralType {
  void operator()() const {
    TestFunctor<char>()();
    TestFunctor<signed char>()();
    TestFunctor<unsigned char>()();
    TestFunctor<short>()();
    TestFunctor<unsigned short>()();
    TestFunctor<int>()();
    TestFunctor<unsigned int>()();
    TestFunctor<long>()();
    TestFunctor<unsigned long>()();
    TestFunctor<long long>()();
    TestFunctor<unsigned long long>()();
    TestFunctor<wchar_t>()();
#if TEST_STD_VER > 17 && defined(__cpp_char8_t)
    TestFunctor<char8_t>()();
#endif
    TestFunctor<char16_t>()();
    TestFunctor<char32_t>()();
    TestFunctor<std::int8_t>()();
    TestFunctor<std::uint8_t>()();
    TestFunctor<std::int16_t>()();
    TestFunctor<std::uint16_t>()();
    TestFunctor<std::int32_t>()();
    TestFunctor<std::uint32_t>()();
    TestFunctor<std::int64_t>()();
    TestFunctor<std::uint64_t>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachFloatingPointType {
  void operator()() const {
    TestFunctor<float>()();
    TestFunctor<double>()();
    TestFunctor<long double>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachPointerType {
  void operator()() const {
    TestFunctor<int*>()();
    TestFunctor<const int*>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachAtomicType {
  void operator()() const {
    TestEachIntegralType<TestFunctor>()();
    TestEachPointerType<TestFunctor>()();
    TestFunctor<UserAtomicType>()();
    /*
            Note: These aren't going to be lock-free,
            so some libatomic.a is necessary.
        */
    TestFunctor<LargeUserAtomicType>()();
    /*
    Enable these once we have P0528

        TestFunctor<PaddedUserAtomicType>()();
        TestFunctor<WeirdUserAtomicType>()();
*/
    TestFunctor<float>()();
    TestFunctor<double>()();
  }
};

#endif // ATOMIC_HELPERS_H