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 % 10 10
Test Date: 2026-10-17 15:55:14 Functions: 100.0 % 3 3

           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                 : #if (defined(BOOST_CAPY_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && \
      41                 :     !defined(BOOST_CAPY_STATIC_LINK)
      42                 : 
      43                 : /** Return this thread's frame allocator slot.
      44                 : 
      45                 :     A shared library build defines it in the library: an inline
      46                 :     definition gives each module its own slot, and a coroutine created
      47                 :     in one would not see the allocator a launcher in another installed.
      48                 :     A static build keeps it inline, since resuming a coroutine writes
      49                 :     it.
      50                 : 
      51                 :     @return A reference to the calling thread's slot.
      52                 : */
      53                 : BOOST_CAPY_DECL
      54                 : std::pmr::memory_resource*&
      55                 : current_frame_allocator_ref() noexcept;
      56                 : 
      57                 : #else
      58                 : 
      59                 : // MSVC may keep an inlined thread_local's address in a coroutine
      60                 : // frame across a suspension, so a resumption on another thread
      61                 : // would write the first thread's slot.
      62                 : #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1) && !defined(__clang__)
      63                 : BOOST_CAPY_NOINLINE
      64                 : #endif
      65                 : inline std::pmr::memory_resource*&
      66                 : current_frame_allocator_ref() noexcept
      67                 : {
      68                 :     static thread_local std::pmr::memory_resource* mr = nullptr;
      69                 :     return mr;
      70                 : }
      71                 : 
      72                 : #endif
      73                 : 
      74                 : } // namespace detail
      75                 : 
      76                 : /** Return the current frame allocator for this thread.
      77                 : 
      78                 :     These accessors exist to implement the allocator
      79                 :     propagation portion of the @ref ContextAwaitable protocol.
      80                 :     Launcher functions (`run_async`, `run`) set the
      81                 :     thread-local value before invoking a child coroutine.
      82                 :     The child's `promise_type::operator new` reads it to
      83                 :     allocate the coroutine frame from the correct resource.
      84                 : 
      85                 :     The value is only valid during a narrow execution
      86                 :     window. Between a coroutine's resumption
      87                 :     and the next suspension point, the protocol guarantees
      88                 :     that TLS contains the allocator associated with the
      89                 :     currently running chain. Outside that window the value
      90                 :     is indeterminate. Only code that implements an
      91                 :     @ref ContextAwaitable should call these functions.
      92                 : 
      93                 :     A return value of `nullptr` means "not specified" -
      94                 :     no allocator is established for this chain.
      95                 :     The awaitable is free to use whatever allocation
      96                 :     strategy makes best sense (e.g.
      97                 :     `std::pmr::new_delete_resource()`).
      98                 : 
      99                 :     Use of the frame allocator is optional. An awaitable
     100                 :     that does not consult this value to allocate its
     101                 :     coroutine frame is never wrong. However, a conforming
     102                 :     awaitable must still propagate the allocator faithfully
     103                 :     so that downstream coroutines can use it.
     104                 : 
     105                 :     @return The thread-local memory_resource pointer,
     106                 :     or `nullptr` if none is set.
     107                 : 
     108                 :     @see set_current_frame_allocator, ContextAwaitable
     109                 : */
     110                 : inline
     111                 : std::pmr::memory_resource*
     112 HIT      373205 : get_current_frame_allocator() noexcept
     113                 : {
     114          373205 :     return detail::current_frame_allocator_ref();
     115                 : }
     116                 : 
     117                 : /** Set the current frame allocator for this thread.
     118                 : 
     119                 :     Installs @p mr as the frame allocator read by the
     120                 :     next coroutine's `promise_type::operator new` on
     121                 :     this thread. Only launcher functions and
     122                 :     @ref ContextAwaitable machinery should call this; see
     123                 :     @ref get_current_frame_allocator for the full protocol
     124                 :     description.
     125                 : 
     126                 :     Passing `nullptr` means "not specified" - no
     127                 :     particular allocator is established for the chain.
     128                 : 
     129                 :     @param mr The memory_resource to install, or
     130                 :     `nullptr` to clear.
     131                 : 
     132                 :     @see get_current_frame_allocator, ContextAwaitable
     133                 : */
     134                 : inline void
     135          478865 : set_current_frame_allocator(
     136                 :     std::pmr::memory_resource* mr) noexcept
     137                 : {
     138          478865 :     detail::current_frame_allocator_ref() = mr;
     139          478865 : }
     140                 : 
     141                 : /** Resume a coroutine handle with frame-allocator TLS protection.
     142                 : 
     143                 :     Saves the current thread-local frame allocator before
     144                 :     calling `h.resume()`, then restores it after the call
     145                 :     returns. This prevents a resumed coroutine's
     146                 :     `await_resume` from permanently overwriting the caller's
     147                 :     allocator value.
     148                 : 
     149                 :     Between a coroutine's resumption and its next child
     150                 :     invocation, arbitrary user code may run. If that code
     151                 :     resumes a coroutine from a different chain on this
     152                 :     thread, the other coroutine's `await_resume` overwrites
     153                 :     TLS with its own allocator. Without save/restore, the
     154                 :     original coroutine's next child would allocate from
     155                 :     the wrong resource.
     156                 : 
     157                 :     Event loops, strand dispatch loops, and any code that
     158                 :     calls `.resume()` on a coroutine handle should use
     159                 :     this function instead of calling `.resume()` directly.
     160                 :     See the @ref Executor concept documentation for details.
     161                 : 
     162                 :     @param h The coroutine handle to resume.
     163                 : 
     164                 :     @see get_current_frame_allocator, set_current_frame_allocator
     165                 : */
     166                 : // tag::safe_resume[]
     167                 : inline void
     168          154907 : safe_resume(std::coroutine_handle<> h) noexcept
     169                 : {
     170          154907 :     auto* saved = get_current_frame_allocator();
     171          154907 :     h.resume();
     172          154907 :     set_current_frame_allocator(saved);
     173          154907 : }
     174                 : // end::safe_resume[]
     175                 : 
     176                 : } // namespace capy
     177                 : } // namespace boost
     178                 : 
     179                 : #endif
        

Generated by: LCOV version 2.3