MySQL 8.0.40
Source Code Documentation
aligned_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/aligned_alloc.h
29 Implementation bits and pieces for aligned allocations. */
30
31#ifndef detail_ut_aligned_alloc_h
32#define detail_ut_aligned_alloc_h
33
34#include <cassert>
35#include <cstddef>
36#include <cstdint>
37#include <cstdlib>
38#include <cstring>
39#include <limits>
40#include <memory>
41#include <utility>
42
43#include "my_compiler.h"
47
48namespace ut {
49namespace detail {
50
52 /** Block of memory returned by this functor will have an additional
53 (metadata) segment (at no additional cost of higher memory consumption)
54 which is guaranteed to be this big, and which can be used to store
55 whatever arbitrary data. See Aligned_alloc and Aligned_alloc_arr for
56 exemplary usages of it.
57 */
58 static constexpr uint32_t metadata_size = alignof(max_align_t);
59
60 /** Alias that we will be using to denote ptr to DATA segment. */
61 using data_segment_ptr = void *;
62
63 /** Dynamically allocates storage of given size and at the address aligned to
64 the requested alignment.
65
66 It is guaranteed that storage allocated by this functor is always
67 (size + alignment) big _and_ that there is always
68 alignof(std::max_align_t) spare bytes within that segment which can be
69 freely used. This means that pointer which is returned by this function
70 can always be safely reversed by alignof(std::max_align_t) bytes and hence
71 make this sub-segment accessible to any subsequent implementation.
72
73 Reversing the pointer for a value which is bigger than
74 alignof(std::max_align_t) bytes is in certain cases possible and can be
75 checked by inspecting the returned offset value. Some more specialized
76 implementations can take advantage of that fact too.
77
78 This property is very important because it can be taken advantage of by
79 other implementations so they are able to store whatever metadata they
80 would like into this segment of alignof(std::max_align_t) bytes, or FWIW
81 (pointer - offset) bytes in more specialized cases.
82
83 For example, let's say that size=10, alignment=32, and underlying
84 allocation function used by this function implementation returns a pointer
85 at address 128. This address must be a multiple of
86 alignof(std::max_align_t) which in this example is 16.
87
88 ------------------------------
89 | VARLEN | META | DATA |
90 ------------------------------
91 128 144 160 170
92
93 DATA is an actual data which has been requested with given size (10) and
94 alignment (32).
95 META is the alignof(std::max_align_t) segment that can always be freely
96 used by other implementations.
97 VARLEN is the leftover variable-length segment that specialized
98 implementations can further make use of by deducing its size from returned
99 offset.
100
101 See Aligned_alloc and Aligned_alloc_arr implementations to see an example
102 of how META segment can be used.
103
104 @param[in] size Size of storage (in bytes) requested to be allocated.
105 @param[in] alignment Alignment requirement for storage to be allocated.
106 Must be power of two and larger than alignof(std::max_align_t).
107 @return {pointer, offset} where pointer is a pointer to dynamically
108 allocated storage aligned to requested alignment, and offset is distance
109 in bytes from pointer which has been originally returned by underlying
110 dynamic allocation function (e.g. std::malloc). Otherwise {nullptr, 0} if
111 dynamic storage allocation failed.
112 */
113 template <bool Zero_initialized>
114 static inline std::pair<data_segment_ptr, std::size_t> alloc(
115 std::size_t size, std::size_t alignment) noexcept {
116 // This API is only about the extended alignments. Non-extended are
117 // already handled with std::malloc.
118 assert(alignment > alignof(std::max_align_t));
119
120 // Here we can take advantage of the fact that std::malloc is always
121 // guaranteed to return suitably aligned pointer P. In other words, this
122 // means that pointer P will be aligned to the value of
123 // alignof(std::max_align_t). That value on Linux implementations is
124 // typically 8 bytes (x86) or 16 bytes (x86-64) but this is completely
125 // implementation-defined. Windows implementation, for example, defines this
126 // value to be 8 bytes for both x86 _and_ x86-64.
127 //
128 // Question which arises is how many bytes do we need to ask std::malloc in
129 // order to satisfy the requested alignment, A, and requested size, S.
130 //
131 // Short answer is (size + alignment) and you can skip the following section
132 // if you're not interested in how we got to that value.
133 //
134 // Long answer is that we can model the problem mathematically as follows:
135 // A = alignment
136 // S = size
137 // P = std::malloc(N);
138 // P % alignof(std::max_align_t) = 0;
139 // where
140 // N is our unknown and for which we can only say that:
141 // N = S + K
142 // is true where K is also some unknown value.
143 //
144 // (A) In cases when P % A = 0, we know that we are already at suitable
145 // address so we can deduce that:
146 // K = 0
147 // from which it follows that:
148 // N = S + K = S
149 //
150 // (B) In cases when P % A != 0, our K must be sufficiently large so that
151 // <P, P + K]
152 // range contains value, P', so that
153 // P' % A = 0
154 // If K = A, then
155 // <P, P + A]
156 // which can be expanded as
157 // <P, P + 1, P + 2, P + 3, ..., P + A]
158 //
159 // If we know that
160 // P % A != 0
161 // then we also know that
162 // (P + A) % A = P % A + A % A = P % A + 0 = P % A != 0
163 // which implies that our P' value is in
164 // <P, P + A>
165 // range.
166 //
167 // Given that
168 // P % alignof(std::max_align_t) = 0
169 // P' % A = 0
170 // A % alignof(std::max_align_t) = 0
171 // P' % alignof(std::max_align_t) = 0
172 // it follows that our P' value can only be found at
173 // P' = P + L*alignof(std::max_align_t)
174 // increments where L is some constant value in range of
175 // L = [1, x>
176 // Expanding <P, P + A> into what we know so far:
177 // <P, ..., P + L*alignof(std::max_align_t), ..., P + A>
178 // it follows that
179 // P + L*alignof(std::max_align_t) < P + A
180 // which equals to
181 // L*alignof(std::max_align_t) < A
182 //
183 // We now just need to prove that
184 // L*alignof(std::max_align_t) < A
185 // is always true for some L value in which case this would mean that our
186 // <P, P + A> range contains at least one value for which P' % = 0.
187 //
188 // Picking the first available L value from L = [1, x> range,
189 // we will get
190 // 1*alignof(std::max_align_t) < A
191 // Given that
192 // A > 2*alignof(std::max_align_t) - 1
193 // must be true, it follows that
194 // 1*alignof(std::max_align_t) < 2*alignof(std::max_align_t) - 1 < A
195 // and
196 // 1*alignof(std::max_align_t) < 2*alignof(std::max_align_t) - 1
197 // alignof(std::max_align_t) > 1
198 // is always true.
199 //
200 // To conclude:
201 // (1) N = S for P % A = 0
202 // (2) N = S + A for P % A != 0
203 //
204 // Given that P is a runtime value which we cannot know upfront we must opt
205 // for N = S + A.
206 const std::size_t data_len = size + alignment;
207 void *mem = Alloc_fn::alloc<Zero_initialized>(data_len);
208 if (unlikely(!mem)) return {nullptr, 0};
209
210 // To guarantee that storage allocated by this function is as advertised
211 // (exactly (size + alignment) big with at least alignof(std::max_align_t)
212 // spare bytes to be used from within that segment) we must handle the N = S
213 // case (see above) and offset the memory by 1 byte. Given the above proof
214 // we know that the next suitable address aligned to requested alignment
215 // must be at least alignof(std::max_align_t) bytes far away.
216 void *buf =
217 reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(mem) + 1);
218 std::size_t buf_size = data_len - 1;
219
220 // TODO(jbakamovic): wrap following std::align expression with ut_a(...)
221 // after we clean up circular-dependencies from our headers. Until then, it
222 // is not possible as including ut0dbg.h header makes the build fail in
223 // mysterious ways.
224 [[maybe_unused]] auto ret = std::align(alignment, size, buf, buf_size);
225 assert(ret != nullptr);
226
227 return {buf, reinterpret_cast<std::uintptr_t>(buf) -
228 reinterpret_cast<std::uintptr_t>(mem)};
229 }
230
231 /** Releases storage allocated through alloc().
232
233 @param[in] ptr data_segment_pointer decreased by offset bytes. Both are
234 obtained through alloc().
235 */
236 static inline void free(void *ptr) noexcept { Alloc_fn::free(ptr); }
237};
238
239/** Memory layout representation of metadata segment guaranteed by
240 the inner workings of Aligned_alloc_impl.
241
242 ----------------------------------------------------
243 | VARLEN | ALIGNED-ALLOC-META | ... DATA ... |
244 ----------------------------------------------------
245 ^ ^
246 | |
247 | |
248 | ptr returned by Aligned_alloc_impl
249 |
250 --------------------------------
251 | META_2 | META_1 |
252 --------------------------------
253 \ \
254 0 \
255 \
256 alignof(max_align_t) - 1
257
258 VARLEN and ALIGNED-ALLOC-META are direct byproduct of Aligned_alloc_impl
259 layout and guarantees.
260
261 VARLEN is the leftover variable-length segment that specialized
262 implementations can further make use of by deducing its size from returned
263 offset. Not used by this implementation.
264
265 ALIGNED-ALLOC-META is the segment which this abstraction is about. It
266 can hold up to sizeof(META_1) + sizeof(META_2) bytes which is, due to
267 Aligned_alloc_impl guarantees, at most alignof(max_align_t) bytes large.
268 Providing larger data than supported is not possible and it is guarded
269 through the means of static_assert. META_1 and META_2 fields can be
270 arbitrarily sized meaning that they can even be of different sizes each.
271
272 DATA is an actual segment which will keep the user data.
273 */
274template <typename Meta_1_type, typename Meta_2_type>
276 /** Convenience types that we will be using to serialize necessary details
277 into the metadata segment.
278 */
279 using meta_1_t = Meta_1_type;
280 using meta_2_t = Meta_2_type;
282
283 /** Metadata size */
284 static constexpr auto allocator_metadata_size =
285 sizeof(meta_1_t) + sizeof(meta_2_t);
286 /** Max metadata size */
288 /** Bail out if we cannot fit the requested data size. */
290 "Aligned_alloc_impl provides a strong guarantee "
291 "of only up to Aligned_alloc_impl::metadata_size bytes.");
292
293 /** Helper function which stores user-provided detail into the META_1 field.
294 */
296 std::size_t meta_1_v) noexcept {
297 assert(meta_1_v <= std::numeric_limits<meta_1_t>::max());
298 *ptr_to_meta_1(data) = meta_1_v;
299 }
300 /** Helper function which stores user-provided detail into the META_2 field.
301 */
303 std::size_t meta_2_v) noexcept {
304 assert(meta_2_v <= std::numeric_limits<meta_2_t>::max());
305 meta_2_t meta_2_v_typed = meta_2_v;
306 memcpy(ptr_to_meta_2(data), &meta_2_v_typed, sizeof(meta_2_t));
307 }
308 /** Helper function which recovers the information user previously stored in
309 META_1 field.
310 */
311 static inline meta_1_t meta_1(
313 return *ptr_to_meta_1(data);
314 }
315 /** Helper function which recovers the information user previously stored in
316 META_2 field.
317 */
318 static inline meta_2_t meta_2(
320 meta_2_t meta_2_v;
321 memcpy(&meta_2_v, ptr_to_meta_2(data), sizeof(meta_2_t));
322 return meta_2_v;
323 }
324
325 private:
326 /** Helper accessor function to metadata (offset). */
327 static inline meta_1_t *ptr_to_meta_1(
329 return reinterpret_cast<meta_1_t *>(data) - 1;
330 }
331 /** Helper accessor function to metadata (data length). */
334 return reinterpret_cast<meta_2_t *>(ptr_to_meta_1(data)) - 1;
335 }
336};
337
338/** Aligned allocation routines.
339
340 They're implemented in terms of Aligned_alloc_impl (and
341 Aligned_alloc_metadata), and given the guarantees it provides,
342 Aligned_alloc::alloc() is able to encode offset and requested
343 allocation datalen into the metadata section without sacrificing memory or
344 making the implementation or end usage more complex.
345
346 Serializing offset into the metadata is what will enable
347 Aligned_alloc::free() to later on recover original pointer returned by
348 the underlying Aligned_alloc_impl allocation mechanism (std::malloc,
349 std::calloc) and consequently be able to appropriately release it
350 (std::free).
351
352 Serializing requested allocation datalen into the metadata is what will
353 enable higher-kinded functions, implemented on top of Aligned_alloc, to
354 take necessary actions such as cleaning up the resources by invoking
355 appropriate number of destructors of non-trivially-destructible types.
356 Otherwise, this would create a burden on end users by having to remember and
357 carry the array size all around the code. This is equivalent to what we find
358 in other standard implementations. For example, new int x[10] is always
359 released without passing the array size: delete[] x; The same holds with
360 this design.
361
362 Memory layout representation looks like the following:
363
364 ----------------------------------------------------
365 | VARLEN | ALIGNED-ALLOC-META | ... DATA ... |
366 ----------------------------------------------------
367 ^ ^
368 | |
369 | |
370 | ptr returned by Aligned_alloc_impl
371 |
372 -----------------------------------
373 | DATALEN | VARLEN-OFFSET |
374 -----------------------------------
375 \ \
376 0 \
377 \
378 alignof(max_align_t) - 1
379
380 VARLEN and ALIGNED-ALLOC-META are direct byproduct of Aligned_alloc_impl
381 layout and guarantees.
382
383 VARLEN is the leftover variable segment of bytes that specialized
384 implementations can further make use of by deducing its size from returned
385 offset. Not used by this implementation.
386
387 DATALEN field in ALIGNED-ALLOC-META segment encodes the total length of DATA
388 segment, which is the actual allocation size that client code has requested.
389
390 VARLEN-OFFSET in ALIGNED-ALLOC-META segment encodes the offset to VARLEN
391 segment which represents the original pointer obtained by underlying
392 allocation Aligned_alloc_impl mechanism.
393 */
394struct Aligned_alloc : public allocator_traits<false> {
396
397 /** Dynamically allocates storage of given size and at the address aligned
398 to the requested alignment.
399
400 @param[in] size Size of storage (in bytes) requested to be allocated.
401 @param[in] alignment Alignment requirement for storage to be allocated.
402 @return Pointer to the allocated storage. nullptr if dynamic storage
403 allocation failed.
404 */
405 template <bool Zero_initialized>
406 static inline void *alloc(std::size_t size, std::size_t alignment) {
407 auto ret = Aligned_alloc_impl::alloc<Zero_initialized>(size, alignment);
408 if (likely(ret.first)) {
409 // We are here taking advantage of Aligned_alloc_impl(S, A) for which
410 // we know that it will always return pointer P and offset O such that:
411 // 1. (P - O) expression is always well-defined.
412 // 2. And O is never less than alignof(std::max_align_t), that is
413 // Aligned_alloc_impl::metadata_size.
414 //
415 // Practically, this means that we can encode whatever metadata we want
416 // into [P - O, P> segment of memory whose length corresponds to
417 // the value of alignof(std::max_align_t). Commonly, this value is 8 bytes
418 // on 32-bit platforms and 16 bytes on 64-bit platforms but not always
419 // (e.g. Windows) and which is why we have to handle it in more generic
420 // way to be fully portable.
421 //
422 // Here we encode the offset so we can later on recover the
423 // original pointer, P' = (P - O), from within Aligned_alloc::free(P)
424 // context. Similarly, we encode the requested allocation datalen.
425 allocator_metadata::meta_1(ret.first, ret.second);
426 allocator_metadata::meta_2(ret.first, size);
427 }
428 return ret.first;
429 }
430
431 /** Releases storage dynamically allocated through
432 Aligned_alloc::alloc().
433
434 @param[in] data Pointer to storage allocated through
435 Aligned_alloc::alloc()
436 */
437 static inline void free(Aligned_alloc_impl::data_segment_ptr data) noexcept {
438 if (unlikely(!data)) return;
439 // Here we make use of the offset which has been encoded by
440 // Aligned_alloc::alloc() to be able to deduce the original pointer and
441 // simply forward it to std::free.
443 }
444
445 /** Returns the number of bytes requested to be allocated.
446
447 @param[in] data Pointer to storage allocated through
448 Aligned_alloc::alloc()
449 @return Number of bytes.
450 */
453 // Deducing the datalen field is straightforward, provided that
454 // data points to the DATA segment, which it is expected to do so.
455 return allocator_metadata::meta_2(data);
456 }
457
458 private:
459 /** Helper function which deduces the original pointer returned by
460 Aligned_alloc_impl from a pointer which is passed to us by the call-site.
461 */
462 static inline void *deduce(
464 // To recover the original pointer we just need to read the offset
465 // we have serialized into the first (allocator) metadata field.
466 auto offset = allocator_metadata::meta_1(data);
467 return reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(data) -
468 offset);
469 }
470};
471
472/** Aligned allocation routines which are instrumented through PFS
473 (performance-schema).
474
475 They're implemented in terms of Aligned_alloc_impl (and
476 Aligned_alloc_metadata), and given the guarantees it provides,
477 Aligned_alloc::alloc() is able to encode offset and requested
478 allocation datalen into the metadata section without sacrificing memory or
479 making the implementation or end usage more complex.
480
481 Serializing offset into the metadata is what will enable
482 Aligned_alloc_pfs::free() to later on recover original pointer returned
483 by the underlying Aligned_alloc_impl allocation mechanism (std::malloc,
484 std::calloc) and consequently be able to appropriately release it
485 (std::free).
486
487 Serializing requested allocation datalen into the metadata is what will
488 enable higher-kinded functions, implemented on top of
489 Aligned_alloc_pfs, to take necessary actions such as cleaning up the
490 resources by invoking appropriate number of destructors of
491 non-trivially-destructible types. Otherwise, this would create a burden on
492 end users by having to remember and carry the array size all around the
493 code. This is equivalent to what we find in other standard implementations.
494 For example, new int x[10] is always released without passing the array
495 size: delete[] x; The same holds with this design.
496
497 PFS-wise this allocation routine will be storing the information that PFS
498 needs to do its own work:
499 - Owning thread
500 - Total length of bytes allocated
501 - Key
502
503 Memory layout representation looks like the following:
504
505 ------------------------------------------------------------------------------
506 | VARLEN1 | ALIGNED-ALLOC-META | PFS-META | VARLEN2 | PFS-META-OFFSET | DATA |
507 ------------------------------------------------------------------------------
508 ^ ^ ^ ^
509 | | | |
510 | | --------------------------- |
511 | | | OWNER | DATALEN' | KEY | |
512 | | --------------------------- |
513 | | |
514 | ptr returned by |
515 | Aligned_alloc_impl |
516 | |
517 | ptr to be returned to call-site
518 | will be pointing here
519 |
520 ------------------------------
521 | DATALEN | VARLEN1-OFFSET |
522 ------------------------------
523 \ \
524 0 \
525 \
526 alignof(max_align_t) - 1
527
528 VARLEN1 and ALIGNED-ALLOC-META are direct byproduct of Aligned_alloc_impl
529 (and Aligned_alloc_metadata) layout and guarantees.
530
531 VARLEN1 is the leftover variable-length segment that specialized
532 implementations can further make use of by deducing its size from returned
533 offset. Not used by this implementation.
534
535 DATALEN field in ALIGNED-ALLOC-META segment encodes the total length of DATA
536 segment, which is the actual allocation size that client code has requested.
537
538 VARLEN1-OFFSET in ALIGNED-ALLOC-META segment encodes the offset to VARLEN1
539 segment which represents the original pointer obtained by underlying
540 allocation Aligned_alloc_impl mechanism.
541
542 PFS-META, VARLEN2 and PFS-META-OFFSET are memory layout representation of
543 PFS_metadata.
544
545 OWNER field encode the owning thread. DATALEN' field encodes total size
546 of memory consumed and not only the size of the DATA segment. KEY field
547 encodes the PFS/PSI key.
548
549 VARLEN2 is the leftover variable-length segment that specialized
550 implementations can further make use of by deducing its size from the
551 following formulae: requested_alignment - sizeof(PFS-META-OFFSET) -
552 sizeof(PFS-META). In code that would be alignment -
553 PFS_metadata::size. Not used by this implementation.
554
555 PFS-META-OFFSET is a field which allows us to recover the pointer to
556 PFS-META segment from a pointer to DATA segment. Having a pointer to
557 PFS-META segment allows us to deduce the VARLEN1-OFFSET field from
558 ALIGNED-ALLOC-META segment which finally gives us a pointer obtained by the
559 underlying allocation Aligned_alloc_impl mechanism.
560 */
561struct Aligned_alloc_pfs : public allocator_traits<true> {
564
565 /** Dynamically allocates storage of given size at the address aligned to the
566 requested alignment.
567
568 @param[in] size Size of storage (in bytes) requested to be allocated.
569 @param[in] alignment Alignment requirement for storage to be allocated.
570 @param[in] key PSI memory key to be used for PFS memory instrumentation.
571 @return Pointer to the allocated storage. nullptr if dynamic storage
572 allocation failed.
573 */
574 template <bool Zero_initialized>
575 static inline void *alloc(std::size_t size, std::size_t alignment,
577 // We must take special care to allocate enough extra space to hold the
578 // PFS metadata (PFS-META + PFS-META-OFFSET) but we also need to take
579 // special care that the pointer which will be returned to the callee by
580 // this function will still be suitably over-aligned as requested. Both of
581 // these requirements can be fulfilled by finding the smallest multiple of
582 // requested alignment that is not smaller than actual PFS metadata size.
583 const auto metadata_len = calc_align(pfs_metadata::size, alignment);
584 const auto total_len = size + metadata_len;
585 auto ret =
586 Aligned_alloc_impl::alloc<Zero_initialized>(total_len, alignment);
587 if (unlikely(!ret.first)) return nullptr;
588
589 // Same as we do with non-PFS variant of Aligned_alloc::alloc(), here we
590 // encode the offset so we can later on recover the original pointer, P' =
591 // (P - O), from within Aligned_alloc_pfs::free(P) context. Similarly, we
592 // encode the requested allocation datalen.
593 allocator_metadata::meta_1(ret.first, ret.second);
594 allocator_metadata::meta_2(ret.first, size);
595
596#ifdef HAVE_PSI_MEMORY_INTERFACE
597 // When computing total number of bytes allocated. we must not only account
598 // for the size that we have requested (total_len) but we also need
599 // to account for extra memory Aligned_alloc_impl may have allocated in
600 // order to be able to accommodate the request. Amount of extra memory
601 // allocated corresponds to the offset value returned by Aligned_alloc_impl.
602 const auto datalen = total_len + ret.second;
603 // The point of this allocator variant is to trace the memory allocations
604 // through PFS (PSI) so do it.
607 // To be able to do the opposite action of tracing when we are releasing the
608 // memory, we need right about the same data we passed to the tracing
609 // memory_alloc function. Let's encode this it into our allocator so we
610 // don't have to carry and keep this data around.
611 pfs_metadata::pfs_owning_thread(ret.first, owner);
613 pfs_metadata::pfs_key(ret.first, key);
614 pfs_metadata::pfs_metaoffset(ret.first, metadata_len);
615#endif
616
617 return static_cast<uint8_t *>(ret.first) + metadata_len;
618 }
619
620 /** Releases storage dynamically allocated through
621 Aligned_alloc_pfs::alloc().
622
623 @param[in] data Pointer to storage allocated through
624 Aligned_alloc_pfs::alloc()
625 */
626 static inline void free(PFS_metadata::data_segment_ptr data) noexcept {
627 if (unlikely(!data)) return;
628
629#ifdef HAVE_PSI_MEMORY_INTERFACE
630 // Deduce the PFS data we encoded in Aligned_alloc_pfs::alloc()
631 auto key = pfs_metadata::pfs_key(data);
632 auto owner = pfs_metadata::pfs_owning_thread(data);
634 // With the deduced PFS data, now trace the memory release action.
636 (key, datalen, owner);
637#endif
638
639 // Here we make use of the offset which has been encoded by
640 // Aligned_alloc_pfs::alloc() to be able to deduce the original pointer and
641 // simply forward it to std::free.
643 }
644
645 /** Returns the number of bytes requested to be allocated.
646
647 @param[in] data Pointer to storage allocated through
648 Aligned_alloc_pfs::alloc()
649 @return Number of bytes.
650 */
653 // In order to be able to deduce the datalen field, we have to deduce the
654 // beginning of PFS metadata segment first.
656 }
657
658 private:
659 /** Helper function which deduces the original pointer returned by
660 Aligned_alloc_impl from a pointer which is passed to us by the call-site.
661 */
662 static inline void *deduce(PFS_metadata::data_segment_ptr data) noexcept {
663 // To recover the original pointer we need to read the offset
664 // we have serialized into the first (allocator) metadata field. But to read
665 // that offset we have to jump over the PFS metadata first. We use PFS meta
666 // offset for that.
667 auto pfs_meta = pfs_metadata::deduce_pfs_meta(data);
668 auto offset = allocator_metadata::meta_1(pfs_meta);
669 return reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(pfs_meta) -
670 offset);
671 }
672};
673
674/** Simple utility metafunction which selects appropriate allocator variant
675 (implementation) depending on the input parameter(s).
676 */
677template <bool Pfs_memory_instrumentation_on>
679 using type =
680 Aligned_alloc; // When PFS is OFF, pick ordinary, non-PFS, variant
681};
682
683template <>
684struct select_alloc_impl<true> {
685 using type = Aligned_alloc_pfs; // Otherwise, pick PFS variant
686};
687
688/** Just a small helper type which saves us some keystrokes. */
689template <bool Pfs_memory_instrumentation_on>
692
693/** Small wrapper which utilizes SFINAE to dispatch the call to appropriate
694 aligned allocator implementation.
695 */
696template <typename Impl>
698 template <bool Zero_initialized, typename T = Impl>
700 alloc(size_t size, size_t alignment, PSI_memory_key key) {
701 return Impl::template alloc<Zero_initialized>(size, alignment, key);
702 }
703 template <bool Zero_initialized, typename T = Impl>
705 alloc(size_t size, size_t alignment, PSI_memory_key /*key*/) {
706 return Impl::template alloc<Zero_initialized>(size, alignment);
707 }
708 static inline void free(void *ptr) { Impl::free(ptr); }
709 static inline size_t datalen(void *ptr) { return Impl::datalen(ptr); }
710};
711
712} // namespace detail
713} // namespace ut
714
715#endif
Simple allocator traits.
#define PSI_MEMORY_CALL(M)
Definition: psi_memory.h:36
constexpr DWORD buf_size
Definition: create_def.cc:228
unsigned int PSI_memory_key
Instrumented memory key.
Definition: psi_memory_bits.h:49
Small helper functions.
#define free(A)
Definition: lexyy.cc:915
Header for compiler-dependent features.
constexpr bool likely(bool expr)
Definition: my_compiler.h:55
constexpr bool unlikely(bool expr)
Definition: my_compiler.h:56
Definition: buf0block_hint.cc:30
Definition: ut0tuple.h:57
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_alloc_impl< Pfs_memory_instrumentation_on >::type select_alloc_impl_t
Just a small helper type which saves us some keystrokes.
Definition: aligned_alloc.h:691
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:99
Implementation bits and pieces for PFS metadata handling.
Small wrapper which utilizes SFINAE to dispatch the call to appropriate aligned allocator implementat...
Definition: aligned_alloc.h:697
static void free(void *ptr)
Definition: aligned_alloc.h:708
static size_t datalen(void *ptr)
Definition: aligned_alloc.h:709
static std::enable_if< T::is_pfs_instrumented_v, void * >::type alloc(size_t size, size_t alignment, PSI_memory_key key)
Definition: aligned_alloc.h:700
static std::enable_if<!T::is_pfs_instrumented_v, void * >::type alloc(size_t size, size_t alignment, PSI_memory_key)
Definition: aligned_alloc.h:705
Definition: aligned_alloc.h:51
static constexpr uint32_t metadata_size
Block of memory returned by this functor will have an additional (metadata) segment (at no additional...
Definition: aligned_alloc.h:58
static void free(void *ptr) noexcept
Releases storage allocated through alloc().
Definition: aligned_alloc.h:236
static std::pair< data_segment_ptr, std::size_t > alloc(std::size_t size, std::size_t alignment) noexcept
Dynamically allocates storage of given size and at the address aligned to the requested alignment.
Definition: aligned_alloc.h:114
void * data_segment_ptr
Alias that we will be using to denote ptr to DATA segment.
Definition: aligned_alloc.h:61
Memory layout representation of metadata segment guaranteed by the inner workings of Aligned_alloc_im...
Definition: aligned_alloc.h:275
static constexpr auto allocator_metadata_size
Metadata size.
Definition: aligned_alloc.h:284
static void meta_2(Aligned_alloc_impl::data_segment_ptr data, std::size_t meta_2_v) noexcept
Helper function which stores user-provided detail into the META_2 field.
Definition: aligned_alloc.h:302
static void meta_1(Aligned_alloc_impl::data_segment_ptr data, std::size_t meta_1_v) noexcept
Bail out if we cannot fit the requested data size.
Definition: aligned_alloc.h:295
static meta_1_t meta_1(Aligned_alloc_impl::data_segment_ptr data) noexcept
Helper function which recovers the information user previously stored in META_1 field.
Definition: aligned_alloc.h:311
static unaligned_meta_2_t * ptr_to_meta_2(Aligned_alloc_impl::data_segment_ptr data) noexcept
Helper accessor function to metadata (data length).
Definition: aligned_alloc.h:332
static constexpr auto max_metadata_size
Max metadata size.
Definition: aligned_alloc.h:287
Meta_1_type meta_1_t
Convenience types that we will be using to serialize necessary details into the metadata segment.
Definition: aligned_alloc.h:279
static meta_2_t meta_2(Aligned_alloc_impl::data_segment_ptr data) noexcept
Helper function which recovers the information user previously stored in META_2 field.
Definition: aligned_alloc.h:318
meta_2_t unaligned_meta_2_t
Definition: aligned_alloc.h:281
Meta_2_type meta_2_t
Definition: aligned_alloc.h:280
static meta_1_t * ptr_to_meta_1(Aligned_alloc_impl::data_segment_ptr data) noexcept
Helper accessor function to metadata (offset).
Definition: aligned_alloc.h:327
Aligned allocation routines which are instrumented through PFS (performance-schema).
Definition: aligned_alloc.h:561
static void * alloc(std::size_t size, std::size_t alignment, pfs_metadata::pfs_memory_key_t key)
Dynamically allocates storage of given size at the address aligned to the requested alignment.
Definition: aligned_alloc.h:575
static void free(PFS_metadata::data_segment_ptr data) noexcept
Releases storage dynamically allocated through Aligned_alloc_pfs::alloc().
Definition: aligned_alloc.h:626
static void * deduce(PFS_metadata::data_segment_ptr data) noexcept
Helper function which deduces the original pointer returned by Aligned_alloc_impl from a pointer whic...
Definition: aligned_alloc.h:662
static allocator_metadata::meta_2_t datalen(PFS_metadata::data_segment_ptr data)
Returns the number of bytes requested to be allocated.
Definition: aligned_alloc.h:651
Aligned allocation routines.
Definition: aligned_alloc.h:394
static void * alloc(std::size_t size, std::size_t alignment)
Dynamically allocates storage of given size and at the address aligned to the requested alignment.
Definition: aligned_alloc.h:406
static void * deduce(Aligned_alloc_impl::data_segment_ptr data) noexcept
Helper function which deduces the original pointer returned by Aligned_alloc_impl from a pointer whic...
Definition: aligned_alloc.h:462
static allocator_metadata::meta_2_t datalen(Aligned_alloc_impl::data_segment_ptr data)
Returns the number of bytes requested to be allocated.
Definition: aligned_alloc.h:451
static void free(Aligned_alloc_impl::data_segment_ptr data) noexcept
Releases storage dynamically allocated through Aligned_alloc::alloc().
Definition: aligned_alloc.h:437
static void free(void *ptr)
Definition: allocator_traits.h:105
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: aligned_alloc.h:678