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 | |||||