MySQL 8.1.0
Source Code Documentation
alloc.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2021, 2023, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is also distributed with certain software (including but not
10limited to OpenSSL) that is licensed under separate terms, as designated in a
11particular file or component or in included license documentation. The authors
12of MySQL hereby grant you an additional permission to link the program and
13your derivative works with the separately licensed software that they have
14included with MySQL.
15
16This program is distributed in the hope that it will be useful, but WITHOUT
17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25*****************************************************************************/
26
27/** @file include/detail/ut/alloc.h
28 Implementation bits and pieces for PFS and non-PFS variants for normal
29 allocations and deallocations through new, delete, malloc, zalloc, free etc.
30 */
31
32#ifndef detail_ut_alloc_h
33#define detail_ut_alloc_h
34
35#include <cassert>
36#include <cstddef>
37#include <cstdint>
38#include <cstdlib>
39#include <memory>
40#include <utility>
41
42#include "my_compiler.h"
47
48namespace ut {
49namespace detail {
50
51/** Allocation routines for non-extended alignment types, as opposed to
52 Aligned_alloc for example.
53
54 These are only a mere wrappers around standard allocation routines so
55 memory layout representation doesn't look any other than the following:
56
57 --------------------------------
58 | ... DATA ... |
59 --------------------------------
60 ^
61 |
62 |
63 ptr to be returned to call-site
64
65 DATA segment is a segment that will be returned to the call-site.
66 */
67struct Alloc : public allocator_traits<false> {
68 /** Dynamically allocates storage of given size.
69
70 @param[in] size Size of storage (in bytes) requested to be allocated.
71 @return Pointer to the allocated storage. nullptr if dynamic storage
72 allocation failed.
73 */
74 template <bool Zero_initialized>
75 static inline void *alloc(std::size_t size) noexcept {
76 return Alloc_fn::alloc<Zero_initialized>(size);
77 }
78
79 /** Reallocates the given area of memory. Behaves as std::realloc()
80 implementation on given platform.
81
82 @param[in] ptr Pointer to the memory to be reallocated.
83 @param[in] size New size of storage (in bytes) requested to be allocated.
84 @return Pointer to the reallocated storage. Or nullptr if realloc
85 operation failed.
86 */
87 static inline void *realloc(void *ptr, std::size_t size) noexcept {
88 return Alloc_fn::realloc(ptr, size);
89 }
90
91 /** Releases storage dynamically allocated through
92 Alloc::alloc() or Alloc::realloc().
93
94 @param[in] ptr Pointer to storage allocated through
95 Alloc::alloc() or Alloc::realloc()
96 */
97 static inline void free(void *ptr) noexcept { Alloc_fn::free(ptr); }
98};
99
100/** Specialization of allocation routines for non-extended alignment types
101 but which in comparison to Alloc are providing support for arrays.
102
103 To provide support for arrays, these allocation routines will allocate extra
104 (metadata) space so that they can serialize the requested size of an array
105 (in bytes) into the memory. That will enable higher-kinded functions,
106 implemented on top of Alloc, to take necessary actions such as cleaning up
107 the resources by invoking appropriate number of destructors of
108 non-trivially-destructible types. Otherwise, this would create a burden on
109 end users by having to remember and carry the array size all around the
110 code. This is equivalent to what we find in other standard implementations.
111 For example, new int x[10] is always released without passing the array
112 size: delete[] x; The same holds with this design.
113
114 Memory layout representation looks like the following:
115
116 ---------------------------------------
117 | ALLOC-ARR-META | ... DATA ... |
118 ---------------------------------------
119 ^ ^
120 | |
121 | |
122 | ptr to be returned to call-site
123 |
124 -----------------
125 | DATALEN |
126 -----------------
127 \ \
128 0 \
129 alignof(max_align_t) - 1
130
131 DATALEN segment encodes the total length of DATA segment, which is the
132 actual allocation size that client code has requested.
133
134 DATA segment is a segment that will be returned to the call-site.
135 */
136struct Alloc_arr : public allocator_traits<false> {
137 /** This is how much the metadata (ALLOC-ARR-META) segment will be big. */
138 static constexpr auto metadata_len = alignof(max_align_t);
139
140 /** This is the type we will be using to store the size of an array. */
141 using datalen_t = size_t;
142
143 /** Sanity check so that we can be sure that our metadata segment can fit
144 the datalen_t.
145 */
146 static_assert(sizeof(datalen_t) <= metadata_len, "Metadata does not fit!");
147
148 /** Sanity check so that we can be sure that the size of our metadata segment
149 is such so that the pointer to DATA segment is always suitably aligned
150 (multiple of alignof(max_align_t).
151 */
152 static_assert(metadata_len % alignof(max_align_t) == 0,
153 "metadata_len must be divisible by alignof(max_align_t)");
154
155 /** Dynamically allocates storage of given size.
156
157 @param[in] size Size of storage (in bytes) requested to be allocated.
158 @return Pointer to the allocated storage. nullptr if dynamic storage
159 allocation failed.
160 */
161 template <bool Zero_initialized>
162 static inline void *alloc(std::size_t size) noexcept {
163 const auto total_len = size + Alloc_arr::metadata_len;
164 auto mem = Alloc_fn::alloc<Zero_initialized>(total_len);
165 *(static_cast<datalen_t *>(mem)) = size;
166 return static_cast<uint8_t *>(mem) + Alloc_arr::metadata_len;
167 }
168
169 /** Releases storage dynamically allocated through Alloc_arr::alloc().
170
171 @param[in] ptr Pointer to storage allocated through Alloc_arr::alloc().
172 */
173 static inline void free(void *ptr) noexcept {
174 if (unlikely(!ptr)) return;
176 }
177
178 /** Returns the size of an array in bytes.
179
180 @param[in] ptr Pointer to storage allocated through
181 Alloc_arr::alloc().
182 @return Size of an array in bytes (or number of bytes allocated).
183 */
184 static inline datalen_t datalen(void *ptr) {
185 return *reinterpret_cast<datalen_t *>(deduce(ptr));
186 }
187
188 private:
189 /** Helper function which deduces the original pointer returned by
190 Alloc_arr::alloc() from a pointer which is passed to us by the call-site.
191 */
192 static inline void *deduce(void *ptr) noexcept {
193 return static_cast<uint8_t *>(ptr) - Alloc_arr::metadata_len;
194 }
195};
196
197/** Allocation routines for non-extended alignment types, as opposed to
198 Aligned_alloc_pfs for example, but which are instrumented through PFS
199 (performance-schema).
200
201 Implemented in terms of PFS_metadata.
202
203 Memory layout representation looks like the following:
204
205 --------------------------------------------------
206 | PFS-META | VARLEN | PFS-META-OFFSET | DATA |
207 --------------------------------------------------
208 ^ ^ ^
209 | | |
210 | --------------------------- |
211 | | OWNER | DATALEN | KEY | |
212 | --------------------------- |
213 | |
214 ptr returned by |
215 Alloc_fn |
216 |
217 ptr to be returned to call-site
218 will be pointing here
219
220 OWNER field encodes the owning thread.
221 DATALEN field encodes total size of memory consumed and not only the size of
222 the DATA segment.
223 KEY field encodes the PFS/PSI key.
224
225 VARLEN is the leftover variable-length segment that specialized
226 implementations can further make use of by deducing its size from the
227 following formulae: abs(alignof(max_align_t) - sizeof(PFS-META-OFFSET) -
228 sizeof(PFS-META)). In code that would be std::abs(alignof(max_align_t) -
229 PFS_metadata::size). Not used by this implementation.
230
231 PFS-META-OFFSET, strictly speaking, isn't necessary in this case of
232 non-extended alignments, where alignment is always known in compile-time and
233 thus the offset we will be storing into the PFS-META-OFFSET field is always
234 going to be the same for the given platform. So, rather than serializing
235 this piece of information into the memory as we do right now, we could very
236 well be storing it into the compile-time evaluated constexpr constant. The
237 reason why we don't do it is that there is no advantage (*) of doing so
238 while we would be introducing a disadvantage of having to maintain separate
239 specialization of PFS_metadata and code would be somewhat more fragmented.
240
241 (*) Extra space that we need to allocate in order to be able to fit the
242 PFS_metadata is going to be the same regardless if there is
243 PFS-META-OFFSET field or not. This is due to the fact that PFS-META
244 segment alone is larger than alignof(max_align_t) so in order to
245 keep the DATA segment suitably aligned (% alignof(max_align_t) == 0)
246 we must choose the size for the whole PFS segment that is a multiple
247 of alignof(max_align_t).
248
249 PFS-META-OFFSET is a field which allows us to recover the pointer to
250 PFS-META segment from a pointer to DATA segment.
251
252 DATA is an actual segment which will keep the user data.
253*/
254struct Alloc_pfs : public allocator_traits<true> {
256
257 /** This is how much the metadata (PFS-META | VARLEN | PFS-META-OFFSET)
258 segment will be big.
259 */
260 static constexpr auto metadata_len =
261 calc_align(pfs_metadata::size, alignof(max_align_t));
262
263 /** Dynamically allocates storage of given size at the address aligned to the
264 requested alignment.
265
266 @param[in] size Size of storage (in bytes) requested to be allocated.
267 @param[in] key PSI memory key to be used for PFS memory instrumentation.
268 @return Pointer to the allocated storage. nullptr if dynamic storage
269 allocation failed.
270 */
271 template <bool Zero_initialized>
272 static inline void *alloc(std::size_t size,
274 const auto total_len = size + Alloc_pfs::metadata_len;
275 auto mem = Alloc_fn::alloc<Zero_initialized>(total_len);
276 if (unlikely(!mem)) return nullptr;
277
278#ifdef HAVE_PSI_MEMORY_INTERFACE
279 // The point of this allocator variant is to trace the memory allocations
280 // through PFS (PSI) so do it.
282 key = PSI_MEMORY_CALL(memory_alloc)(key, total_len, &owner);
283 // To be able to do the opposite action of tracing when we are releasing the
284 // memory, we need right about the same data we passed to the tracing
285 // memory_alloc function. Let's encode this it into our allocator so we
286 // don't have to carry and keep this data around.
288 pfs_metadata::pfs_datalen(mem, total_len);
291#endif
292
293 return static_cast<uint8_t *>(mem) + Alloc_pfs::metadata_len;
294 }
295
296 /** Reallocates the given area of memory, which if not nullptr, must be
297 previously allocated by Alloc_pfs::alloc() or Alloc_pfs::realloc().
298
299 Mimics unfortunate realloc() design so that:
300 * If pointer passed is nullptr, then behavior is as if
301 Alloc_pfs::alloc() had been called.
302 * If new size of storage requested is 0, then behavior is as if
303 Alloc_pfs::free() had been called.
304
305 @param[in] data Pointer to the memory to be reallocated.
306 @param[in] size New size of storage (in bytes) requested to be allocated.
307 @param[in] key PSI memory key to be used for PFS memory instrumentation.
308 @return Pointer to the reallocated storage. nullptr if dynamic storage
309 allocation or reallocation failed or if new size requested was 0.
310 */
311 static inline void *realloc(PFS_metadata::data_segment_ptr data,
312 std::size_t size,
314 // Allocate memory if pointer passed in is nullptr
315 if (!data) {
316 return Alloc_pfs::alloc<false>(size, key);
317 }
318
319 // Free the memory if passed in size is zero
320 if (size == 0) {
321 Alloc_pfs::free(data);
322 return nullptr;
323 }
324
325#ifdef HAVE_PSI_MEMORY_INTERFACE
326 // Deduce the PFS data we encoded in Alloc_pfs::alloc()
327 auto key_curr = pfs_metadata::pfs_key(data);
328 auto owner_curr = pfs_metadata::pfs_owning_thread(data);
329 auto datalen_curr = pfs_metadata::pfs_datalen(data);
330 // With the deduced PFS data, now trace the memory release action.
332 (key_curr, datalen_curr, owner_curr);
333#endif
334
335 // Otherwise, continue with the plain realloc
336 const auto total_len = size + Alloc_pfs::metadata_len;
337 auto mem = Alloc_fn::realloc(deduce(data), total_len);
338 if (unlikely(!mem)) return nullptr;
339
340#ifdef HAVE_PSI_MEMORY_INTERFACE
341 // The point of this allocator variant is to trace the memory allocations
342 // through PFS (PSI) so do it.
344 key = PSI_MEMORY_CALL(memory_alloc)(key, total_len, &owner);
345 // To be able to do the opposite action of tracing when we are releasing the
346 // memory, we need right about the same data we passed to the tracing
347 // memory_alloc function. Let's encode this it into our allocator so we
348 // don't have to carry and keep this data around.
350 pfs_metadata::pfs_datalen(mem, total_len);
353#endif
354
355 return static_cast<uint8_t *>(mem) + Alloc_pfs::metadata_len;
356 }
357
358 /** Releases storage dynamically allocated through
359 Alloc_pfs::alloc().
360
361 @param[in] data Pointer to storage allocated through
362 Alloc_pfs::alloc()
363 */
364 static inline void free(PFS_metadata::data_segment_ptr data) noexcept {
365 if (unlikely(!data)) return;
366
367#ifdef HAVE_PSI_MEMORY_INTERFACE
368 // Deduce the PFS data we encoded in Alloc_pfs::alloc()
369 auto key = pfs_metadata::pfs_key(data);
370 auto owner = pfs_metadata::pfs_owning_thread(data);
372 // With the deduced PFS data, now trace the memory release action.
374 (key, datalen, owner);
375#endif
376
377 // Here we make use of the offset which has been encoded by
378 // Alloc_pfs::alloc() to be able to deduce the original pointer and
379 // simply forward it to std::free.
380 Alloc_fn::free(deduce(data));
381 }
382
383 /** Returns the number of bytes requested to be allocated.
384
385 @param[in] data Pointer to storage allocated through
386 Alloc_pfs::alloc()
387 @return Number of bytes.
388 */
389 static inline size_t datalen(PFS_metadata::data_segment_ptr data) {
391 }
392
393 private:
394 /** Helper function which deduces the original pointer returned by
395 Alloc_pfs::alloc() from a pointer which is passed to us by the call-site.
396 */
397 static inline void *deduce(PFS_metadata::data_segment_ptr data) noexcept {
399 }
400};
401
402/** Simple utility metafunction which selects appropriate allocator variant
403 (implementation) depending on the input parameter(s).
404 */
405template <bool Pfs_memory_instrumentation_on, bool Array_specialization>
407
408template <>
409struct select_malloc_impl<false, false> {
410 using type = Alloc; // When PFS is OFF, pick ordinary, non-PFS, variant
411};
412
413template <>
414struct select_malloc_impl<false, true> {
415 using type = Alloc_arr; // When needed, take special care to pick the variant
416 // which specializes for arrays
417};
418
419template <bool Array_specialization>
420struct select_malloc_impl<true, Array_specialization> {
421 using type = Alloc_pfs; // Otherwise, pick PFS variant
422};
423
424/** Just a small helper type which saves us some keystrokes. */
425template <bool Pfs_memory_instrumentation_on, bool Array_specialization>
427 typename select_malloc_impl<Pfs_memory_instrumentation_on,
428 Array_specialization>::type;
429
430/** Small wrapper which utilizes SFINAE to dispatch the call to appropriate
431 allocator implementation.
432 */
433template <typename Impl>
434struct Alloc_ {
435 template <bool Zero_initialized, typename T = Impl>
437 alloc(size_t size, PSI_memory_key key) {
438 return Impl::template alloc<Zero_initialized>(size, key);
439 }
440 template <bool Zero_initialized, typename T = Impl>
442 alloc(size_t size, PSI_memory_key /*key*/) {
443 return Impl::template alloc<Zero_initialized>(size);
444 }
445 template <typename T = Impl>
447 realloc(void *ptr, size_t size, PSI_memory_key key) {
448 return Impl::realloc(ptr, size, key);
449 }
450 template <typename T = Impl>
452 realloc(void *ptr, size_t size, PSI_memory_key /*key*/) {
453 return Impl::realloc(ptr, size);
454 }
455 static inline void free(void *ptr) { Impl::free(ptr); }
456 static inline size_t datalen(void *ptr) { return Impl::datalen(ptr); }
457 template <typename T = Impl>
461 }
462 template <typename T = Impl>
465 return 0;
466 }
467};
468
469} // namespace detail
470} // namespace ut
471
472#endif
Simple allocator traits.
#define PSI_MEMORY_CALL(M)
Definition: psi_memory.h:35
unsigned int PSI_memory_key
Instrumented memory key.
Definition: psi_memory_bits.h:48
Small helper functions.
#define realloc(P, A)
Definition: lexyy.cc:916
#define free(A)
Definition: lexyy.cc:915
Header for compiler-dependent features.
constexpr bool unlikely(bool expr)
Definition: my_compiler.h:57
Instrumentation helpers for memory allocation.
Definition: ut0tuple.h:56
static PSI_memory_key memory_alloc(PSI_memory_key, size_t, struct PSI_thread **owner)
Definition: psi_memory_v2_empty.cc:34
static void memory_free(PSI_memory_key, size_t, struct PSI_thread *)
Definition: psi_memory_v2_empty.cc:52
constexpr size_t calc_align(size_t n, size_t m)
Calculates the smallest multiple of m that is not smaller than n when m is a power of two.
Definition: helper.h:44
typename select_malloc_impl< Pfs_memory_instrumentation_on, Array_specialization >::type select_malloc_impl_t
Just a small helper type which saves us some keystrokes.
Definition: alloc.h:428
This file contains a set of libraries providing overloads for regular dynamic allocation routines whi...
Definition: aligned_alloc.h:47
required string key
Definition: replication_asynchronous_connection_failover.proto:59
required string type
Definition: replication_group_member_actions.proto:33
static MEM_ROOT mem
Definition: sql_servers.cc:99
Implementation bits and pieces for PFS metadata handling.
Small wrapper which utilizes SFINAE to dispatch the call to appropriate allocator implementation.
Definition: alloc.h:434
static void free(void *ptr)
Definition: alloc.h:455
static std::enable_if<!T::is_pfs_instrumented_v, void * >::type alloc(size_t size, PSI_memory_key)
Definition: alloc.h:442
static std::enable_if<!T::is_pfs_instrumented_v, void * >::type realloc(void *ptr, size_t size, PSI_memory_key)
Definition: alloc.h:452
static std::enable_if< T::is_pfs_instrumented_v, size_t >::type pfs_overhead()
Definition: alloc.h:459
static std::enable_if< T::is_pfs_instrumented_v, void * >::type realloc(void *ptr, size_t size, PSI_memory_key key)
Definition: alloc.h:447
static std::enable_if< T::is_pfs_instrumented_v, void * >::type alloc(size_t size, PSI_memory_key key)
Definition: alloc.h:437
static std::enable_if<!T::is_pfs_instrumented_v, size_t >::type pfs_overhead()
Definition: alloc.h:464
static size_t datalen(void *ptr)
Definition: alloc.h:456
Specialization of allocation routines for non-extended alignment types but which in comparison to All...
Definition: alloc.h:136
static void * deduce(void *ptr) noexcept
Helper function which deduces the original pointer returned by Alloc_arr::alloc() from a pointer whic...
Definition: alloc.h:192
static constexpr auto metadata_len
This is how much the metadata (ALLOC-ARR-META) segment will be big.
Definition: alloc.h:138
static void * alloc(std::size_t size) noexcept
Sanity check so that we can be sure that our metadata segment can fit the datalen_t.
Definition: alloc.h:162
size_t datalen_t
This is the type we will be using to store the size of an array.
Definition: alloc.h:141
static void free(void *ptr) noexcept
Releases storage dynamically allocated through Alloc_arr::alloc().
Definition: alloc.h:173
static datalen_t datalen(void *ptr)
Returns the size of an array in bytes.
Definition: alloc.h:184
static void free(void *ptr)
Definition: allocator_traits.h:104
static void * realloc(void *ptr, size_t nbytes)
Definition: allocator_traits.h:100
Allocation routines for non-extended alignment types, as opposed to Aligned_alloc_pfs for example,...
Definition: alloc.h:254
static void * realloc(PFS_metadata::data_segment_ptr data, std::size_t size, pfs_metadata::pfs_memory_key_t key)
Reallocates the given area of memory, which if not nullptr, must be previously allocated by Alloc_pfs...
Definition: alloc.h:311
static void free(PFS_metadata::data_segment_ptr data) noexcept
Releases storage dynamically allocated through Alloc_pfs::alloc().
Definition: alloc.h:364
static constexpr auto metadata_len
This is how much the metadata (PFS-META | VARLEN | PFS-META-OFFSET) segment will be big.
Definition: alloc.h:260
static size_t datalen(PFS_metadata::data_segment_ptr data)
Returns the number of bytes requested to be allocated.
Definition: alloc.h:389
static void * deduce(PFS_metadata::data_segment_ptr data) noexcept
Helper function which deduces the original pointer returned by Alloc_pfs::alloc() from a pointer whic...
Definition: alloc.h:397
static void * alloc(std::size_t size, pfs_metadata::pfs_memory_key_t key)
Dynamically allocates storage of given size at the address aligned to the requested alignment.
Definition: alloc.h:272
Allocation routines for non-extended alignment types, as opposed to Aligned_alloc for example.
Definition: alloc.h:67
static void * alloc(std::size_t size) noexcept
Dynamically allocates storage of given size.
Definition: alloc.h:75
static void free(void *ptr) noexcept
Releases storage dynamically allocated through Alloc::alloc() or Alloc::realloc().
Definition: alloc.h:97
static void * realloc(void *ptr, std::size_t size) noexcept
Reallocates the given area of memory.
Definition: alloc.h:87
Memory layout representation of PFS metadata segment that is used by the allocator variants which als...
Definition: pfs.h:85
void * data_segment_ptr
Definition: pfs.h:93
static void pfs_datalen(data_segment_ptr data, size_t datalen) noexcept
Helper function which stores the PFS datalen info into the DATALEN field.
Definition: pfs.h:108
static void pfs_key(data_segment_ptr data, pfs_memory_key_t key) noexcept
Helper function which stores the PFS key info into the KEY field.
Definition: pfs.h:114
PSI_thread * pfs_owning_thread_t
Convenience types that we will be using to serialize necessary details into the Aligned_alloc metadat...
Definition: pfs.h:89
static constexpr auto size
Definition: pfs.h:99
static void pfs_owning_thread(data_segment_ptr data, pfs_owning_thread_t thread) noexcept
Helper function which stores the PFS thread info into the OWNER field.
Definition: pfs.h:102
static void pfs_metaoffset(data_segment_ptr data, std::size_t alignment) noexcept
Helper function which stores the offset to PFS metadata segment into the PFS-META-OFFSET field.
Definition: pfs.h:121
PSI_memory_key pfs_memory_key_t
Definition: pfs.h:91
static void * deduce_pfs_meta(data_segment_ptr data) noexcept
Helper function which deduces the pointer to the beginning of PFS metadata segment given the pointer ...
Definition: pfs.h:155
Simple allocator traits.
Definition: allocator_traits.h:81
Simple utility metafunction which selects appropriate allocator variant (implementation) depending on...
Definition: alloc.h:406