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