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-03-24 19:38:46 Functions: 100.0 % 4 4

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

Generated by: LCOV version 2.3