LCOV - code coverage report
Current view: top level - capy/ex - frame_allocator.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 12 12
Test Date: 2026-08-26 19:00:49 Functions: 100.0 % 4 4

           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
        

Generated by: LCOV version 2.3