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