MySQL 8.3.0
Source Code Documentation
atomics_array.h
Go to the documentation of this file.
1/* Copyright (c) 2020, 2023, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23#ifndef CONTAINER_ATOMIC_INTEGRALS_ARRAY
24#define CONTAINER_ATOMIC_INTEGRALS_ARRAY
25
26#include <algorithm>
27#include <atomic>
28#include <cmath>
29#include <map>
30#include <memory>
31#include <sstream>
32#include <thread>
33#include <tuple>
34
35#include "sql/containers/atomics_array_index_padding.h" // container::Padded_Indexing
36#include "sql/memory/unique_ptr.h" // memory::Unique_ptr
37
38namespace container {
39/**
40 Array of `std::atomic` elements of type `T`.
41
42 An array of `std::atomic` elements implies, almost for sure, a
43 multi-threaded environment and concurrent access to the array may lead to
44 false sharing when consecutive elements are pulled into the same CPU
45 cache line. This class accepts, as a template parameter, a helper class
46 that is both an element storage index translator and element storage size
47 provider. Different strategies to prevent false sharing and the
48 subsequent cache invalidation and misses, may be applied. Amongst others,
49 padding each element of the array to the size of the cache line or use
50 index translation to interleave sequential indexes to leave enough space
51 for them not to be pulled into the same cache line. The two described
52 strategies are provided already with the `container::Padded_indexing` and
53 `container::Interleaved_indexing` classes.
54
55 Template parameters are as follows:
56 - `T`: the integral type for the array elements.
57 - `I`: type of indexing to be used by this array in the form of a
58 class. Available classes are `container::Padded_indexing` and
59 `container::Interleaved_indexing`. The parameter defaults to
60 `container::Padded_indexing`.
61 - `A`: type of memory allocator to be used, in the form of a class
62 (defaults to no allocator).
63
64 When and if deciding between interleaved or padded indexing, one could
65 take into consideration, amongst possibly others, the following
66 arguments:
67
68 - For arrays with random concurrent access patterns, interleaved indexing
69 doesn't ensure false-sharing prevention.
70 - For arrays with sequential concurrent access patterns, if it's needed
71 that interleaved indexing prevents false-sharing, consecutive array
72 indexes will need to be apart, physically, the size of a
73 cache-line. So, in a system with expectation of T threads accessing
74 concurrently an array of elements of size `E` and with a cache-line of
75 size `CL`, the array capacity should be at least `T * (CL / E)` for
76 interleaved indexing to prevent false-sharing.
77 - Padded indexing will always prevent false-sharing but it will consume
78 more memory in order to have the same array capacity as the interleaved
79 indexing.
80 */
81template <typename T, typename I = container::Padded_indexing<T>,
82 typename A = std::nullptr_t>
84 public:
85 using pointer_type = T *;
86 using const_pointer_type = T const *;
87 using reference_type = T &;
88 using const_reference_type = T const &;
89 using value_type = T;
90 using const_value_type = T const;
91 using element_type = std::atomic<T>;
92
93 /**
94 Iterator helper class to iterate over the array, from 0 to the array
95 size.
96
97 Check C++ documentation on `Iterator named requirements` for more
98 information on the implementation.
99 */
100 class Iterator {
101 public:
102 using difference_type = std::ptrdiff_t;
103 using value_type = T;
104 using pointer = T *;
105 using reference = T;
106 using iterator_category = std::forward_iterator_tag;
107
108 /**
109 */
110 explicit Iterator(Atomics_array<T, I, A> const &parent, size_t position);
111 Iterator(const Iterator &rhs);
113 virtual ~Iterator() = default;
114
115 // BASIC ITERATOR METHODS //
120 // END / BASIC ITERATOR METHODS //
121
122 // INPUT ITERATOR METHODS //
125 bool operator==(Iterator const &rhs) const;
126 bool operator!=(Iterator const &rhs) const;
127 // END / INPUT ITERATOR METHODS //
128
129 // OUTPUT ITERATOR METHODS //
130 // reference operator*(); <- already defined
131 // iterator operator++(int); <- already defined
132 // END / OUTPUT ITERATOR METHODS //
133
134 // FORWARD ITERATOR METHODS //
135 // Enable support for both input and output iterator <- already enabled
136 // END / FORWARD ITERATOR METHODS //
137
138 private:
139 /** The position of the element this iterator is pointing to. */
140 size_t m_current{0};
141 /** The reference to the queue holding the elements. */
143 };
144
145 /**
146 Constructor allowing a specific memory allocator, a specific queue size
147 and the instance of `T` to initialize the array with.
148
149 @param alloc The memory allocator instance
150 @param size The desired size for the queue
151 @param init_value The instance of `T` to initialize the array with
152 */
153 template <
154 typename D = T, typename J = I, typename B = A,
155 std::enable_if_t<!std::is_same<B, std::nullptr_t>::value> * = nullptr>
156 Atomics_array(A &alloc, size_t size, T init_value);
157 /**
158 Constructor allowing a specific queue size and the instance of `T` to
159 initialize the array with.
160
161 @param size The desired size for the queue
162 @param init_value The instance of `T` to initialize the array with
163 */
164 template <
165 typename D = T, typename J = I, typename B = A,
166 std::enable_if_t<std::is_same<B, std::nullptr_t>::value> * = nullptr>
167 Atomics_array(size_t size, T init_value);
168 // Deleted copy and move constructors.
171 //
172 /**
173 Destructor for the class.
174 */
175 virtual ~Atomics_array() = default;
176 // Deleted copy and move operators.
179 //
180 /**
181 Retrieves the value stored in a specific index of the array.
182
183 @param index The index of the element to retrieve the value for
184
185 @return the value of the element at index `index`
186 */
187 element_type &operator[](size_t index) const;
188 /**
189 Retrieves an iterator instance that points to the beginning of the
190 array.
191
192 @return An instance of an iterator pointing to the beginning of the
193 array.
194 */
195 Iterator begin() const;
196 /**
197 Retrieves an iterator instance that points to the end of the underlying
198 array.
199
200 @return An instance of an iterator pointing to the end of the underlying
201 array.
202 */
203 Iterator end() const;
204 /**
205 Finds a value in the queue according to the evaluation of `predicate`
206 by traversing the array from `start_from` to the array size.
207
208 The find condition is given according to the predicate `predicate` which
209 should be any predicate which translatable to `[](value_type value,
210 size_t index) -> bool`. If the predicate evaluates to `true`, the value
211 and respective index are returned as an `std::tuple`.
212
213 Check C++ documentation for the definition of `Predicate` named requirement
214 for more information.
215
216 @param predicate The predicate to be evaluated
217 @param start_from The index to start the search at
218
219 @return A tuple holding the value and the index of the first element
220 for which the predicate evaluated to true. If the predicate
221 doesn't evaluate to true for any of the elements, it returns a
222 pair holding the default value for `T` and the size of the
223 array.
224
225 */
226 template <typename Pred>
227 std::tuple<T, size_t> find_if(Pred predicate, size_t start_from = 0) const;
228 /**
229 First the first occurrence of `to_find`, starting at `start_from` index.
230
231 @param to_find the value to find
232 @param start_from the index to start the search from
233
234 @return the index of the the first element that matches the `to_find`
235 value or the array size if no element matches.
236 */
237 size_t find(T to_find, size_t start_from = 0) const;
238 /**
239 Returns the size of the array.
240
241 @return The size of the array
242 */
243 size_t size() const;
244 /**
245 Returns the number of bytes used to allocate the array.
246
247 @return The number of bytes used to allocate the array
248 */
249 size_t allocated_size() const;
250 /**
251 Return `this` queue textual representation.
252
253 @return The textual representation for `this` queue.
254 */
255 std::string to_string() const;
256
257 friend std::ostream &operator<<(std::ostream &out,
259 out << in.to_string(true) << std::flush;
260 return out;
261 }
262
263 private:
264 /** The index translation object to be used */
266 /** The byte array in which the elements will be stored */
268
269 /**
270 Initializes the array.
271
272 @return The reference to `this` object, for chaining purposes.
273 */
275};
276} // namespace container
277
278template <typename T, typename I, typename A>
280 Atomics_array<T, I, A> const &parent, size_t position)
281 : m_current{position}, m_parent{&parent} {}
282
283template <typename T, typename I, typename A>
285 : m_current{rhs.m_current}, m_parent{rhs.m_parent} {}
286
287template <typename T, typename I, typename A>
289 : m_current{rhs.m_current}, m_parent{rhs.m_parent} {
290 rhs.m_current = 0;
291 rhs.m_parent = nullptr;
292}
293
294template <typename T, typename I, typename A>
297 this->m_current = rhs.m_current;
298 this->m_parent = rhs.m_parent;
299 return (*this);
300}
301
302template <typename T, typename I, typename A>
305 this->m_current = rhs.m_current;
306 this->m_parent = rhs.m_parent;
307 rhs.m_current = 0;
308 rhs.m_parent = nullptr;
309 return (*this);
310}
311
312template <typename T, typename I, typename A>
315 if (this->m_current < this->m_parent->m_index.size()) {
316 ++this->m_current;
317 }
318 return (*this);
319}
320
321template <typename T, typename I, typename A>
324 return (*this->m_parent)[this->m_current];
325}
326
327template <typename T, typename I, typename A>
330 auto to_return = (*this);
331 ++(*this);
332 return to_return;
333}
334
335template <typename T, typename I, typename A>
338 return &((*this->m_parent)[this->m_current]);
339}
340
341template <typename T, typename I, typename A>
343 Iterator const &rhs) const {
344 return this->m_current == rhs.m_current && this->m_parent == rhs.m_parent;
345}
346
347template <typename T, typename I, typename A>
349 Iterator const &rhs) const {
350 return !((*this) == rhs);
351}
352
353template <typename T, typename I, typename A>
354template <typename D, typename J, typename B,
355 std::enable_if_t<!std::is_same<B, std::nullptr_t>::value> *>
357 T init_value)
358 : m_index{size},
359 m_array{alloc, static_cast<size_t>(m_index.size()) * I::element_size()} {
360 this->init(init_value);
361}
362
363template <typename T, typename I, typename A>
364template <typename D, typename J, typename B,
365 std::enable_if_t<std::is_same<B, std::nullptr_t>::value> *>
367 : m_index{size},
368 m_array{memory::make_unique<unsigned char[]>(
369 static_cast<size_t>(m_index.size()) * I::element_size())} {
370 this->init(init_value);
371}
372
373template <typename T, typename I, typename A>
376 return reinterpret_cast<Atomics_array::element_type &>(
377 this->m_array[this->m_index.translate(index)]);
378}
379
380template <typename T, typename I, typename A>
383 return Iterator{*this, 0};
384}
385
386template <typename T, typename I, typename A>
389 return Iterator{*this, this->m_index.size()};
390}
391
392template <typename T, typename I, typename A>
393template <typename Pred>
395 Pred predicate, size_t start_from) const {
396 for (size_t idx = start_from; idx != this->m_index.size(); ++idx) {
397 auto &current = (*this)[idx];
398 T value = current.load(std::memory_order_relaxed);
399 if (predicate(value, idx)) {
400 return std::make_tuple(value, idx);
401 }
402 }
403 return std::make_tuple(std::numeric_limits<T>::max(), this->m_index.size());
404}
405
406template <typename T, typename I, typename A>
408 size_t start_from) const {
409 for (size_t idx = start_from; idx != this->m_index.size(); ++idx) {
410 auto &current = (*this)[idx];
411 T value = current.load(std::memory_order_relaxed);
412 if (value == to_find) {
413 return idx;
414 }
415 }
416 return this->m_index.size();
417}
418
419template <typename T, typename I, typename A>
421 return this->m_index.size();
422}
423
424template <typename T, typename I, typename A>
426 return this->m_index.size() * I::element_size();
427}
428
429template <typename T, typename I, typename A>
432 for (auto value : (*this)) {
433 out << std::to_string(value) << ", ";
434 }
435 out << "EOF" << std::flush;
436 return out.str();
437}
438
439template <typename T, typename I, typename A>
441 T init_value) {
442 for (size_t idx = 0; idx != this->m_index.size(); ++idx) {
443 ::new (&this->m_array[this->m_index.translate(idx)])
444 element_type(init_value);
445 }
446 return (*this);
447}
448
449#endif // CONTAINER_ATOMIC_INTEGRALS_ARRAY
Iterator helper class to iterate over the array, from 0 to the array size.
Definition: atomics_array.h:100
bool operator!=(Iterator const &rhs) const
Definition: atomics_array.h:348
std::forward_iterator_tag iterator_category
Definition: atomics_array.h:106
size_t m_current
The position of the element this iterator is pointing to.
Definition: atomics_array.h:140
Iterator & operator=(const Iterator &rhs)
Definition: atomics_array.h:296
T reference
Definition: atomics_array.h:105
reference operator*() const
Definition: atomics_array.h:323
Iterator(Atomics_array< T, I, A > const &parent, size_t position)
Definition: atomics_array.h:279
T * pointer
Definition: atomics_array.h:104
T value_type
Definition: atomics_array.h:103
pointer operator->() const
Definition: atomics_array.h:337
Iterator & operator++()
Definition: atomics_array.h:314
std::ptrdiff_t difference_type
Definition: atomics_array.h:102
Atomics_array< T, I, A > const * m_parent
The reference to the queue holding the elements.
Definition: atomics_array.h:142
bool operator==(Iterator const &rhs) const
Definition: atomics_array.h:342
Array of std::atomic elements of type T.
Definition: atomics_array.h:83
T const const_value_type
Definition: atomics_array.h:90
T const & const_reference_type
Definition: atomics_array.h:88
size_t find(T to_find, size_t start_from=0) const
First the first occurrence of to_find, starting at start_from index.
Definition: atomics_array.h:407
T value_type
Definition: atomics_array.h:89
T * pointer_type
Definition: atomics_array.h:85
size_t size() const
Returns the size of the array.
Definition: atomics_array.h:420
container::Atomics_array< T, I, A > & init(T init_value)
Initializes the array.
Definition: atomics_array.h:440
Atomics_array< T, I, A > & operator=(Atomics_array< T, I, A > &&rhs)=delete
std::string to_string() const
Return this queue textual representation.
Definition: atomics_array.h:430
Atomics_array(Atomics_array< T, I, A > const &rhs)=delete
I m_index
The index translation object to be used.
Definition: atomics_array.h:265
std::tuple< T, size_t > find_if(Pred predicate, size_t start_from=0) const
Finds a value in the queue according to the evaluation of predicate by traversing the array from star...
Definition: atomics_array.h:394
T const * const_pointer_type
Definition: atomics_array.h:86
Atomics_array< T, I, A > & operator=(Atomics_array< T, I, A > const &rhs)=delete
Iterator end() const
Retrieves an iterator instance that points to the end of the underlying array.
Definition: atomics_array.h:388
Atomics_array(A &alloc, size_t size, T init_value)
Constructor allowing a specific memory allocator, a specific queue size and the instance of T to init...
Definition: atomics_array.h:356
std::atomic< T > element_type
Definition: atomics_array.h:91
memory::Unique_ptr< unsigned char[], A > m_array
The byte array in which the elements will be stored.
Definition: atomics_array.h:267
size_t allocated_size() const
Returns the number of bytes used to allocate the array.
Definition: atomics_array.h:425
Atomics_array(size_t size, T init_value)
Constructor allowing a specific queue size and the instance of T to initialize the array with.
Definition: atomics_array.h:366
T & reference_type
Definition: atomics_array.h:87
Iterator begin() const
Retrieves an iterator instance that points to the beginning of the array.
Definition: atomics_array.h:382
virtual ~Atomics_array()=default
Destructor for the class.
Atomics_array(Atomics_array< T, I, A > &&rhs)=delete
friend std::ostream & operator<<(std::ostream &out, Atomics_array< T, I, A > &in)
Definition: atomics_array.h:257
element_type & operator[](size_t index) const
Retrieves the value stored in a specific index of the array.
Definition: atomics_array.h:375
Smart pointer to hold a unique pointer to a heap allocated memory of type T, constructed using a spec...
Definition: unique_ptr.h:151
static std::string to_string(const LEX_STRING &str)
Definition: lex_string.h:49
Definition: atomics_array.h:38
Definition: aligned_atomic.h:43
Unique_ptr< T, std::nullptr_t > make_unique(size_t size)
In-place constructs a new unique pointer with no specific allocator and with array type T.
static mysql_service_status_t flush(reference_caching_cache cache) noexcept
Definition: component.cc:113
std::basic_ostringstream< char, std::char_traits< char >, ut::allocator< char > > ostringstream
Specialization of basic_ostringstream which uses ut::allocator.
Definition: ut0new.h:2869