TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_EX_FRAME_ALLOC_MIXIN_HPP
11 : #define BOOST_CAPY_EX_FRAME_ALLOC_MIXIN_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/frame_allocator.hpp>
15 : #include <boost/capy/ex/recycling_memory_resource.hpp>
16 :
17 : #include <cstddef>
18 : #include <cstring>
19 : #include <memory_resource>
20 :
21 : namespace boost {
22 : namespace capy {
23 :
24 : /** Mixin that adds frame-allocator-aware allocation to a promise type.
25 :
26 : Inherit from this class in any coroutine promise type to opt into
27 : TLS-based frame allocation with the recycling memory resource
28 : fast path. The mixin provides `operator new` and `operator delete`
29 : that:
30 :
31 : 1. Read the thread-local frame allocator set by `run_async` or `run`.
32 : 2. Bypass virtual dispatch when the allocator is the default
33 : recycling memory resource.
34 : 3. Store the allocator pointer at the end of each frame for
35 : correct deallocation even when TLS changes between allocation
36 : and deallocation.
37 :
38 : This is the same allocation strategy used by @ref
39 : io_awaitable_promise_base. Use this mixin directly when your
40 : promise type does not need the full environment and continuation
41 : support that `io_awaitable_promise_base` provides.
42 :
43 : @par Example
44 : @code
45 : struct my_internal_coroutine
46 : {
47 : struct promise_type : frame_alloc_mixin
48 : {
49 : my_internal_coroutine get_return_object();
50 : std::suspend_always initial_suspend() noexcept;
51 : std::suspend_always final_suspend() noexcept;
52 : void return_void();
53 : void unhandled_exception() noexcept;
54 : };
55 : };
56 : @endcode
57 :
58 : @par Thread Safety
59 : The allocation fast path uses thread-local storage and requires
60 : no synchronization. The global pool fallback is mutex-protected.
61 :
62 : @see io_awaitable_promise_base, frame_allocator, recycling_memory_resource
63 : */
64 : struct frame_alloc_mixin
65 : {
66 : /** Allocate a coroutine frame.
67 :
68 : Uses the thread-local frame allocator set by run_async.
69 : Falls back to default memory resource if not set.
70 : Stores the allocator pointer at the end of each frame for
71 : correct deallocation even when TLS changes. Uses memcpy
72 : to avoid alignment requirements on the trailing pointer.
73 : Bypasses virtual dispatch for the recycling allocator.
74 : */
75 HIT 5293 : static void* operator new(std::size_t size)
76 : {
77 5293 : static auto* const rmr = get_recycling_memory_resource();
78 :
79 5293 : auto* mr = get_current_frame_allocator();
80 5293 : if(!mr)
81 2802 : mr = std::pmr::get_default_resource();
82 :
83 5293 : auto total = size + sizeof(std::pmr::memory_resource*);
84 : void* raw;
85 5293 : if(mr == rmr)
86 : raw = static_cast<recycling_memory_resource*>(mr)
87 2079 : ->allocate_fast(total, alignof(std::max_align_t));
88 : else
89 3214 : raw = mr->allocate(total, alignof(std::max_align_t));
90 5293 : std::memcpy(static_cast<char*>(raw) + size, &mr, sizeof(mr));
91 5293 : return raw;
92 : }
93 :
94 : /** Deallocate a coroutine frame.
95 :
96 : Reads the allocator pointer stored at the end of the frame
97 : to ensure correct deallocation regardless of current TLS.
98 : Bypasses virtual dispatch for the recycling allocator.
99 : */
100 5293 : static void operator delete(void* ptr, std::size_t size) noexcept
101 : {
102 5293 : static auto* const rmr = get_recycling_memory_resource();
103 :
104 : std::pmr::memory_resource* mr;
105 5293 : std::memcpy(&mr, static_cast<char*>(ptr) + size, sizeof(mr));
106 5293 : auto total = size + sizeof(std::pmr::memory_resource*);
107 5293 : if(mr == rmr)
108 : static_cast<recycling_memory_resource*>(mr)
109 2079 : ->deallocate_fast(ptr, total, alignof(std::max_align_t));
110 : else
111 3214 : mr->deallocate(ptr, total, alignof(std::max_align_t));
112 5293 : }
113 : };
114 :
115 : } // namespace capy
116 : } // namespace boost
117 :
118 : #endif
|