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