MySQL 8.1.0
Source Code Documentation
lock_free_type.h
Go to the documentation of this file.
1/* Copyright (c) 2020, 2023, Oracle and/or its affiliates.
2
3This program is free software; you can redistribute it and/or modify it under
4the terms of the GNU General Public License, version 2.0, as published by the
5Free Software Foundation.
6
7This program is also distributed with certain software (including but not
8limited to OpenSSL) that is licensed under separate terms, as designated in a
9particular file or component or in included license documentation. The authors
10of MySQL hereby grant you an additional permission to link the program and
11your derivative works with the separately licensed software that they have
12included with MySQL.
13
14This program is distributed in the hope that it will be useful, but WITHOUT
15ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
17for more details.
18
19You should have received a copy of the GNU General Public License along with
20this program; if not, write to the Free Software Foundation, Inc.,
2151 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23/** @file storage/temptable/include/temptable/lock_free_type.h
24Lock-free type (selection) implementation. */
25
26#ifndef TEMPTABLE_LOCK_FREE_TYPE_H
27#define TEMPTABLE_LOCK_FREE_TYPE_H
28
29#include <atomic>
30#include <type_traits>
31
32#include "my_config.h"
34
35namespace temptable {
36
37/** Clang has a bug which causes ATOMIC_LLONG_LOCK_FREE to be defined as 1
38 * (or "sometimes lock-free") in 32-bit builds even though
39 * __atomic_always_lock_free returns true for the same type on the same
40 * platform. This is an inconsistency which can be easily verified by:
41 *
42 * % clang -dM -E -x c /dev/null | grep LLONG_LOCK
43 * #define __CLANG_ATOMIC_LLONG_LOCK_FREE 2
44 * #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
45 *
46 * % clang -m32 -dM -E -x c /dev/null | grep LLONG_LOCK
47 * #define __CLANG_ATOMIC_LLONG_LOCK_FREE 1
48 * #define __GCC_ATOMIC_LLONG_LOCK_FREE 1
49 *
50 * % gcc -dM -E -x c /dev/null | grep LLONG_LOCK
51 * #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
52 *
53 * % gcc -m32 -dM -E -x c /dev/null | grep LLONG_LOCK
54 * #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
55 *
56 * There has been some work towards fixing this issue:
57 * * https://bugs.llvm.org/show_bug.cgi?id=30581
58 * * Introduces the fix for the aforementioned problem but ...
59 * * https://bugs.llvm.org/show_bug.cgi?id=31864
60 * * ... reverts the fix because it breaks some other targets, e.g.
61 * 32-bit FreeBSD
62 *
63 * Some more links:
64 * * https://reviews.llvm.org/D28213
65 * * https://reviews.llvm.org/D29542
66 */
67#if defined(__clang__) && (SIZEOF_VOIDP == 4) && (ATOMIC_LLONG_LOCK_FREE == 1)
68#define WORKAROUND_PR31864_CLANG_BUG (1)
69#else
70#define WORKAROUND_PR31864_CLANG_BUG (0)
71#endif
72
73/** Enum class describing alignment-requirements. */
75
76/** Lock-free type selector, a helper utility which evaluates during
77 * the compile-time whether the given type T has a property of being
78 * always-lock-free for given platform. If true, Lock_free_type_selector::Type
79 * will hold T, otherwise Lock_free_type_selector::Type will be inexisting in
80 * which case static-assert will be triggered with a hopefully descriptive
81 * error-message. In the event of static-assert, one can either try to select
82 * another type T or, if one does not care about the actual underlying
83 * type representation, simply utilize the `Largest_lock_free_type_selector`
84 * utility instead. This utility will work out those details automagically. For
85 * more information, see documentation on `Largest_lock_free_type_selector`.
86 *
87 * In short, reasoning behind this machinery lies in the fact that the standard
88 * cannot guarantee that underlying implementation of std::atomic<T> is going
89 * to be able to use lock-free atomic CPU instructions. That obviously depends
90 * on the given type T but also on the properties of concrete platform.
91 * Therefore, actual implementation is mostly platform-dependent and is
92 * free to choose any other locking operation (e.g. mutex) as long as it is
93 * able to fulfill the atomicity. Lock-freedom is not a pre-requisite. Only
94 * exception is std::atomic_flag.
95 *
96 * For certain types, however, lock-freedom can be claimed upfront during the
97 * compile-time phase, and this is where this utiliy kicks in. It is essentially
98 * a C++14 rewrite of std::atomic<T>::is_always_lock_free which is only
99 * available from C++17 onwards. Once moved to C++17 this utility will become
100 * obsolete and shall be replaced with standard-compliant implementation.
101 *
102 * More details and motivation can be found at:
103 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0152r0.html
104 * */
105template <typename T, typename V = void>
107 static_assert(
108 !std::is_same<T, T>::value,
109 "No always-lock-free property could be found for given type. "
110 "Type provided is probably not a built-in (fundamental) type or a "
111 "pointer which makes it impossible for this particular check to be "
112 "excercised at compile-time.");
113};
114
115/** Template-specialization for trivially-copyable classes/structs.
116 *
117 * Subset of trivially-copyable classes/structs might have always-lock-free
118 * property but for this feature to be implemented we would have to go at great
119 * lengths to implement cross-platform support. Therefore, for simplicity
120 * reasons let's just detect the overload and fail gracefully.
121 * */
122template <typename T>
124 T, typename std::enable_if<std::is_class<T>::value and
125 std::is_trivially_copyable<T>::value>::type> {
126 static_assert(!std::is_same<T, T>::value,
127 "Querying always-lock-free property of trivially-copyable "
128 "classes or structs is not yet implemented!");
129};
130
131/** Template-specialization for pointer types. */
132template <typename T>
134 T, typename std::enable_if<std::is_pointer<T>::value>::type> {
135#if (ATOMIC_POINTER_LOCK_FREE == 2)
136 using Type = T;
137#else
138 static_assert(false,
139 "Pointer type on this platform does not have an "
140 "always-lock-free property. Bailing out ...");
141#endif
142};
143
144/** Template-specialization for long long types. */
145template <typename T>
147 T,
148 typename std::enable_if<std::is_same<T, long long>::value or
149 std::is_same<T, unsigned long long>::value>::type> {
150#if (ATOMIC_LLONG_LOCK_FREE == 2) || (WORKAROUND_PR31864_CLANG_BUG == 1)
151 using Type = T;
152#else
153 static_assert(false,
154 "(unsigned) long long type on this platform does not have an "
155 "always-lock-free property. Bailing out ...");
156#endif
157};
158
159/** Template-specialization for long types. */
160template <typename T>
162 T, typename std::enable_if<std::is_same<T, long>::value or
163 std::is_same<T, unsigned long>::value>::type> {
164#if (ATOMIC_LONG_LOCK_FREE == 2)
165 using Type = T;
166#else
167 static_assert(false,
168 "(unsigned) long type on this platform does not have an "
169 "always-lock-free property. Bailing out ...");
170#endif
171};
172
173/** Template-specialization for int types. */
174template <typename T>
176 T, typename std::enable_if<std::is_same<T, int>::value or
177 std::is_same<T, unsigned int>::value>::type> {
178#if (ATOMIC_INT_LOCK_FREE == 2)
179 using Type = T;
180#else
181 static_assert(false,
182 "(unsigned) int type on this platform does not have an "
183 "always-lock-free property. Bailing out ...");
184#endif
185};
186
187/** Template-specialization for short types. */
188template <typename T>
190 T, typename std::enable_if<std::is_same<T, short>::value or
191 std::is_same<T, unsigned short>::value>::type> {
192#if (ATOMIC_SHORT_LOCK_FREE == 2)
193 using Type = T;
194#else
195 static_assert(false,
196 "(unsigned) short type on this platform does not have an "
197 "always-lock-free property. Bailing out ...");
198#endif
199};
200
201/** Template-specialization for char types. */
202template <typename T>
204 T, typename std::enable_if<std::is_same<T, char>::value or
205 std::is_same<T, unsigned char>::value>::type> {
206#if (ATOMIC_CHAR_LOCK_FREE == 2)
207 using Type = T;
208#else
209 static_assert(false,
210 "(unsigned) char type on this platform does not have an "
211 "always-lock-free property. Bailing out ...");
212#endif
213};
214
215/** Template-specialization for boolean types. */
216template <typename T>
218 T, typename std::enable_if<std::is_same<T, bool>::value>::type> {
219#if (ATOMIC_BOOL_LOCK_FREE == 2)
220 using Type = T;
221#else
222 static_assert(false,
223 "bool type on this platform does not have an "
224 "always-lock-free property. Bailing out ...");
225#endif
226};
227
228/** Largest lock-free type selector, a helper utility very much similar
229 * to Lock_free_type_selector with the difference being that it tries hard
230 * not to fail. E.g. it will try to find the largest available T for given
231 * platform which has a property of being always-lock-free. T which has been
232 * selected is then found in Largest_lock_free_type_selector::Type.
233 * Signedness of T is respected.
234 * */
235template <typename T, typename V = void>
237 static_assert(
238 !std::is_same<T, T>::value,
239 "No always-lock-free property could be found for given type. "
240 "Type provided is probably not a built-in (fundamental) type or a "
241 "pointer which makes it impossible for this particular check to be "
242 "excercised at compile-time.");
243};
244
245/** Template-specialization for pointer types. */
246template <typename T>
248 T, typename std::enable_if<std::is_pointer<T>::value>::type> {
249#if (ATOMIC_POINTER_LOCK_FREE == 2)
250 using Type = T;
251#else
252 static_assert(false,
253 "Pointer type on this platform does not have an "
254 "always-lock-free property. Bailing out ...");
255#endif
256};
257
258/** Template-specialization for integral types. */
259template <typename T>
261 T, typename std::enable_if<std::is_integral<T>::value>::type> {
262#if (ATOMIC_LLONG_LOCK_FREE == 2) || (WORKAROUND_PR31864_CLANG_BUG == 1)
263 using Type = std::conditional_t<std::is_unsigned<T>::value,
264 unsigned long long, long long>;
265#elif (ATOMIC_LONG_LOCK_FREE == 2)
266 using Type =
267 std::conditional_t<std::is_unsigned<T>::value, unsigned long, long>;
268#elif (ATOMIC_INT_LOCK_FREE == 2)
269 using Type =
270 std::conditional_t<std::is_unsigned<T>::value, unsigned int, int>;
271#elif (ATOMIC_SHORT_LOCK_FREE == 2)
272 using Type =
273 std::conditional_t<std::is_unsigned<T>::value, unsigned short, short>;
274#elif (ATOMIC_CHAR_LOCK_FREE == 2)
275 using Type =
276 std::conditional_t<std::is_unsigned<T>::value, unsigned char, char>;
277#elif (ATOMIC_BOOL_LOCK_FREE == 2)
278 using Type = bool;
279#else
280 static_assert(
281 false,
282 "No suitable always-lock-free type was found for this platform. "
283 "Bailing out ...");
284#endif
285};
286
287/** Representation of an atomic type which is guaranteed to be always-lock-free.
288 * In case always-lock-free property cannot be satisfied for given T,
289 * Lock_free_type instantiation will fail with the compile-time error.
290 *
291 * Always-lock-free guarantee is implemented through the means of
292 * Lock_free_type_selector or Largest_lock_free_type_selector. User code can
293 * opt-in for any of those. By default, Lock_free_type_selector is used.
294 *
295 * In addition, this type provides an ability to redefine the
296 * alignment-requirement of the underlying always-lock-free type, basically
297 * making it possible to over-align T to the size of the L1-data cache-line
298 * size. By default, T has a natural alignment.
299 */
300template <typename T, Alignment ALIGN = Alignment::NATURAL,
301 template <typename, typename = void> class TypeSelector =
304 using Type = typename TypeSelector<T>::Type;
305 std::atomic<Type> m_value;
306};
307
308/*
309 * Template-specialization for Lock_free_type with alignment-requirement set to
310 * L1-data cache size.
311 * */
312template <typename T, template <typename, typename = void> class TypeSelector>
313struct Lock_free_type<T, Alignment::L1_DCACHE_SIZE, TypeSelector> {
314 using Type = typename TypeSelector<T>::Type;
315 alignas(L1_DCACHE_SIZE) std::atomic<Type> m_value;
316};
317
318} // namespace temptable
319
320#endif /* TEMPTABLE_LOCK_FREE_TYPE_H */
Type
Definition: resource_group_basic_types.h:32
Definition: varlen_sort.h:183
Definition: allocator.h:44
Alignment
Enum class describing alignment-requirements.
Definition: lock_free_type.h:74
constexpr size_t L1_DCACHE_SIZE
Store L1-dcache size information into the constexpr expression.
Definition: constants.h:81
required string type
Definition: replication_group_member_actions.proto:33
TempTable constants.
Largest lock-free type selector, a helper utility very much similar to Lock_free_type_selector with t...
Definition: lock_free_type.h:236
typename TypeSelector< T >::Type Type
Definition: lock_free_type.h:314
Lock-free type selector, a helper utility which evaluates during the compile-time whether the given t...
Definition: lock_free_type.h:106
Representation of an atomic type which is guaranteed to be always-lock-free.
Definition: lock_free_type.h:303
typename TypeSelector< T >::Type Type
Definition: lock_free_type.h:304
std::atomic< Type > m_value
Definition: lock_free_type.h:305