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_FRAME_ALLOCATOR_HPP
12 : #define BOOST_CAPY_FRAME_ALLOCATOR_HPP
13 :
14 : #include <boost/capy/detail/config.hpp>
15 :
16 : #include <coroutine>
17 : #include <memory_resource>
18 :
19 : /* Design rationale (pdimov):
20 :
21 : This accessor is a thin wrapper over a thread-local pointer.
22 : It returns exactly what was stored, including nullptr. No
23 : dynamic initializer on the thread-local; a dynamic TLS
24 : initializer moves you into a costlier implementation bucket
25 : on some platforms - avoid it.
26 :
27 : Null handling is the caller's responsibility (e.g. in
28 : promise_type::operator new). The accessor must not substitute
29 : a default, because there are multiple valid choices
30 : (new_delete_resource, the default pmr resource, etc.). If
31 : the allocator is not set, it reports "not set" and the
32 : caller interprets that however it wants.
33 : */
34 :
35 : namespace boost {
36 : namespace capy {
37 :
38 : namespace detail {
39 :
40 : inline std::pmr::memory_resource*&
41 HIT 115425 : current_frame_allocator_ref() noexcept
42 : {
43 : static thread_local std::pmr::memory_resource* mr = nullptr;
44 115425 : return mr;
45 : }
46 :
47 : } // namespace detail
48 :
49 : /** Return the current frame allocator for this thread.
50 :
51 : These accessors exist to implement the allocator
52 : propagation portion of the @ref IoAwaitable protocol.
53 : Launcher functions (`run_async`, `run`) set the
54 : thread-local value before invoking a child coroutine.
55 : The child's `promise_type::operator new` reads it to
56 : allocate the coroutine frame from the correct resource.
57 :
58 : The value is only valid during a narrow execution
59 : window. Between a coroutine's resumption
60 : and the next suspension point, the protocol guarantees
61 : that TLS contains the allocator associated with the
62 : currently running chain. Outside that window the value
63 : is indeterminate. Only code that implements an
64 : @ref IoAwaitable should call these functions.
65 :
66 : A return value of `nullptr` means "not specified" -
67 : no allocator is established for this chain.
68 : The awaitable is free to use whatever allocation
69 : strategy makes best sense (e.g.
70 : `std::pmr::new_delete_resource()`).
71 :
72 : Use of the frame allocator is optional. An awaitable
73 : that does not consult this value to allocate its
74 : coroutine frame is never wrong. However, a conforming
75 : awaitable must still propagate the allocator faithfully
76 : so that downstream coroutines can use it.
77 :
78 : @return The thread-local memory_resource pointer,
79 : or `nullptr` if none is set.
80 :
81 : @see set_current_frame_allocator, IoAwaitable
82 : */
83 : inline
84 : std::pmr::memory_resource*
85 55536 : get_current_frame_allocator() noexcept
86 : {
87 55536 : return detail::current_frame_allocator_ref();
88 : }
89 :
90 : /** Set the current frame allocator for this thread.
91 :
92 : Installs @p mr as the frame allocator read by the
93 : next coroutine's `promise_type::operator new` on
94 : this thread. Only launcher functions and
95 : @ref IoAwaitable machinery should call this; see
96 : @ref get_current_frame_allocator for the full protocol
97 : description.
98 :
99 : Passing `nullptr` means "not specified" - no
100 : particular allocator is established for the chain.
101 :
102 : @param mr The memory_resource to install, or
103 : `nullptr` to clear.
104 :
105 : @see get_current_frame_allocator, IoAwaitable
106 : */
107 : inline void
108 59889 : set_current_frame_allocator(
109 : std::pmr::memory_resource* mr) noexcept
110 : {
111 59889 : detail::current_frame_allocator_ref() = mr;
112 59889 : }
113 :
114 : /** Resume a coroutine handle with frame-allocator TLS protection.
115 :
116 : Saves the current thread-local frame allocator before
117 : calling `h.resume()`, then restores it after the call
118 : returns. This prevents a resumed coroutine's
119 : `await_resume` from permanently overwriting the caller's
120 : allocator value.
121 :
122 : Between a coroutine's resumption and its next child
123 : invocation, arbitrary user code may run. If that code
124 : resumes a coroutine from a different chain on this
125 : thread, the other coroutine's `await_resume` overwrites
126 : TLS with its own allocator. Without save/restore, the
127 : original coroutine's next child would allocate from
128 : the wrong resource.
129 :
130 : Event loops, strand dispatch loops, and any code that
131 : calls `.resume()` on a coroutine handle should use
132 : this function instead of calling `.resume()` directly.
133 : See the @ref Executor concept documentation for details.
134 :
135 : @param h The coroutine handle to resume.
136 :
137 : @see get_current_frame_allocator, set_current_frame_allocator
138 : */
139 : // tag::safe_resume[]
140 : inline void
141 50405 : safe_resume(std::coroutine_handle<> h) noexcept
142 : {
143 50405 : auto* saved = get_current_frame_allocator();
144 50405 : h.resume();
145 50405 : set_current_frame_allocator(saved);
146 50405 : }
147 : // end::safe_resume[]
148 :
149 : } // namespace capy
150 : } // namespace boost
151 :
152 : #endif
|