TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/capy
9 : //
10 :
11 : #ifndef BOOST_CAPY_DETAIL_FRAME_MEMORY_RESOURCE_HPP
12 : #define BOOST_CAPY_DETAIL_FRAME_MEMORY_RESOURCE_HPP
13 :
14 : #include <boost/capy/detail/config.hpp>
15 :
16 : #include <cstddef>
17 : #include <memory>
18 : #include <memory_resource>
19 : #include <new>
20 : #include <type_traits>
21 :
22 : namespace boost {
23 : namespace capy {
24 : namespace detail {
25 :
26 : /** The alignment capy requests for every coroutine frame.
27 :
28 : The compiler assumes a frame's `operator new` returns memory
29 : aligned to `__STDCPP_DEFAULT_NEW_ALIGNMENT__`. On some targets,
30 : such as MSVC and Apple arm64, that is 16 while
31 : `alignof(std::max_align_t)` is only 8.
32 : */
33 : inline constexpr std::size_t frame_alignment =
34 : static_cast<std::size_t>(__STDCPP_DEFAULT_NEW_ALIGNMENT__) >
35 : alignof(std::max_align_t)
36 : ? static_cast<std::size_t>(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
37 : : alignof(std::max_align_t);
38 :
39 : /** One unit of frame storage.
40 :
41 : A standard allocator takes no alignment argument, so frame memory
42 : is allocated through it as an array of units: every block is then
43 : aligned for any frame. A unit is exactly as wide as its alignment,
44 : which can be less than `sizeof(std::max_align_t)`, so a block
45 : wastes less than one alignment of padding.
46 : */
47 : struct alignas(frame_alignment) frame_unit
48 : {
49 : unsigned char bytes[frame_alignment];
50 : };
51 :
52 : /** Return the number of frame units that hold `bytes`.
53 :
54 : Rounds up without overflowing for any `bytes`.
55 : */
56 : inline constexpr std::size_t
57 HIT 14503 : frame_units(std::size_t bytes) noexcept
58 : {
59 14503 : return bytes / sizeof(frame_unit) +
60 14503 : (bytes % sizeof(frame_unit) != 0);
61 : }
62 :
63 : /** Wrapper that adapts a standard Allocator to memory_resource.
64 :
65 : This wrapper is used to store value-type allocators in the
66 : execution context or trampoline frame. It rebinds the allocator
67 : to `frame_unit` and allocates whole units of it, so every
68 : block is aligned for any frame. A standard allocator takes no
69 : alignment argument, and one rebound to `std::byte` may pack blocks
70 : at byte alignment.
71 :
72 : A request for alignment above `frame_alignment` throws
73 : `std::bad_alloc`.
74 :
75 : @tparam Alloc The standard allocator type.
76 : */
77 : template<class Alloc>
78 : class frame_memory_resource : public std::pmr::memory_resource
79 : {
80 : using traits = std::allocator_traits<Alloc>;
81 : using unit_alloc =
82 : typename traits::template rebind_alloc<frame_unit>;
83 : using unit_traits = std::allocator_traits<unit_alloc>;
84 :
85 : static_assert(
86 : requires { typename traits::value_type; },
87 : "Alloc must satisfy allocator requirements");
88 :
89 : static_assert(
90 : std::is_copy_constructible_v<Alloc>,
91 : "Alloc must be copy constructible");
92 :
93 : unit_alloc alloc_;
94 :
95 : public:
96 : /** Construct from an allocator.
97 :
98 : The allocator is rebind-copied to `frame_unit`.
99 :
100 : @param a The allocator to adapt.
101 : */
102 : explicit
103 3771 : frame_memory_resource(Alloc const& a)
104 3771 : : alloc_(a)
105 : {
106 3771 : }
107 :
108 : /** Get the memory resource pointer.
109 :
110 : @return Pointer to this memory resource.
111 : */
112 11 : std::pmr::memory_resource* get() noexcept { return this; }
113 :
114 : protected:
115 : void*
116 4926 : do_allocate(std::size_t bytes, std::size_t alignment) override
117 : {
118 : // A block is only as aligned as a unit. The memory_resource
119 : // contract allows a throw, never a misaligned block.
120 4926 : if(alignment > alignof(frame_unit))
121 1 : throw std::bad_alloc();
122 9709 : return unit_traits::allocate(alloc_, frame_units(bytes));
123 : }
124 :
125 : void
126 4924 : do_deallocate(void* p, std::size_t bytes, std::size_t) override
127 : {
128 4924 : unit_traits::deallocate(
129 4924 : alloc_, static_cast<frame_unit*>(p), frame_units(bytes));
130 4924 : }
131 :
132 : bool
133 2 : do_is_equal(const memory_resource& other) const noexcept override
134 : {
135 2 : return this == &other;
136 : }
137 : };
138 :
139 : } // namespace detail
140 : } // namespace capy
141 : } // namespace boost
142 :
143 : #endif
|