LCOV - code coverage report
Current view: top level - corosio/detail - timeout_coro.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 27 27
Test Date: 2026-02-27 19:39:18 Functions: 100.0 % 11 11

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       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/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_DETAIL_TIMEOUT_CORO_HPP
      11                 : #define BOOST_COROSIO_DETAIL_TIMEOUT_CORO_HPP
      12                 : 
      13                 : #include <boost/capy/concept/io_awaitable.hpp>
      14                 : #include <boost/capy/ex/frame_allocator.hpp>
      15                 : #include <boost/capy/ex/io_awaitable_promise_base.hpp>
      16                 : #include <boost/capy/ex/io_env.hpp>
      17                 : 
      18                 : #include <coroutine>
      19                 : #include <stop_token>
      20                 : #include <type_traits>
      21                 : #include <utility>
      22                 : 
      23                 : /* Self-destroying coroutine that awaits a timer and signals a
      24                 :    stop_source on expiry. Created suspended (initial_suspend =
      25                 :    suspend_always); the caller sets an owned io_env copy then
      26                 :    resumes, which runs synchronously until the timer wait suspends.
      27                 :    At final_suspend, suspend_never destroys the frame — the
      28                 :    timeout_coro destructor is intentionally a no-op since the
      29                 :    handle is dangling after self-destruction. If the coroutine is
      30                 :    still suspended at shutdown, the timer service drains it via
      31                 :    completion_op::destroy().
      32                 : 
      33                 :    The promise reuses task<>'s transform_awaiter pattern (including
      34                 :    the MSVC symmetric-transfer workaround) to inject io_env into
      35                 :    IoAwaitable co_await expressions. */
      36                 : 
      37                 : namespace boost::corosio::detail {
      38                 : 
      39                 : /** Fire-and-forget coroutine for the timeout side of cancel_at.
      40                 : 
      41                 :     The coroutine awaits a timer and signals a stop_source if the
      42                 :     timer fires without being cancelled. It self-destroys at
      43                 :     final_suspend via suspend_never.
      44                 : 
      45                 :     @see make_timeout
      46                 : */
      47                 : struct timeout_coro
      48                 : {
      49                 :     struct promise_type : capy::io_awaitable_promise_base<promise_type>
      50                 :     {
      51                 :         capy::io_env env_storage_;
      52                 : 
      53                 :         /** Store an owned copy of the environment.
      54                 : 
      55                 :             The timeout coroutine can outlive the cancel_at_awaitable
      56                 :             that created it, so it must own its env rather than
      57                 :             pointing to external storage.
      58                 :         */
      59 HIT          24 :         void set_env_owned(capy::io_env env)
      60                 :         {
      61              24 :             env_storage_ = std::move(env);
      62              24 :             set_environment(&env_storage_);
      63              24 :         }
      64                 : 
      65              24 :         timeout_coro get_return_object() noexcept
      66                 :         {
      67                 :             return timeout_coro{
      68              24 :                 std::coroutine_handle<promise_type>::from_promise(*this)};
      69                 :         }
      70                 : 
      71              24 :         std::suspend_always initial_suspend() noexcept
      72                 :         {
      73              24 :             return {};
      74                 :         }
      75              24 :         std::suspend_never final_suspend() noexcept
      76                 :         {
      77              24 :             return {};
      78                 :         }
      79              24 :         void return_void() noexcept {}
      80                 :         void unhandled_exception() noexcept {}
      81                 : 
      82                 :         template<class Awaitable>
      83                 :         struct transform_awaiter
      84                 :         {
      85                 :             std::decay_t<Awaitable> a_;
      86                 :             promise_type* p_;
      87                 : 
      88              24 :             bool await_ready() noexcept
      89                 :             {
      90              24 :                 return a_.await_ready();
      91                 :             }
      92                 : 
      93              24 :             decltype(auto) await_resume()
      94                 :             {
      95              24 :                 capy::set_current_frame_allocator(
      96              24 :                     p_->environment()->frame_allocator);
      97              24 :                 return a_.await_resume();
      98                 :             }
      99                 : 
     100                 :             template<class Promise>
     101              24 :             auto await_suspend(std::coroutine_handle<Promise> h) noexcept
     102                 :             {
     103                 : #ifdef _MSC_VER
     104                 :                 using R = decltype(a_.await_suspend(h, p_->environment()));
     105                 :                 if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
     106                 :                     a_.await_suspend(h, p_->environment()).resume();
     107                 :                 else
     108                 :                     return a_.await_suspend(h, p_->environment());
     109                 : #else
     110              24 :                 return a_.await_suspend(h, p_->environment());
     111                 : #endif
     112                 :             }
     113                 :         };
     114                 : 
     115                 :         template<class Awaitable>
     116              24 :         auto transform_awaitable(Awaitable&& a)
     117                 :         {
     118                 :             using A = std::decay_t<Awaitable>;
     119                 :             if constexpr (capy::IoAwaitable<A>)
     120                 :             {
     121                 :                 return transform_awaiter<Awaitable>{
     122              48 :                     std::forward<Awaitable>(a), this};
     123                 :             }
     124                 :             else
     125                 :             {
     126                 :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     127                 :             }
     128              24 :         }
     129                 :     };
     130                 : 
     131                 :     std::coroutine_handle<promise_type> h_;
     132                 : 
     133                 :     timeout_coro() noexcept : h_(nullptr) {}
     134                 : 
     135              24 :     explicit timeout_coro(std::coroutine_handle<promise_type> h) noexcept
     136              24 :         : h_(h)
     137                 :     {
     138              24 :     }
     139                 : 
     140                 :     // Self-destroying via suspend_never at final_suspend
     141                 :     ~timeout_coro() = default;
     142                 : 
     143                 :     timeout_coro(timeout_coro const&)            = delete;
     144                 :     timeout_coro& operator=(timeout_coro const&) = delete;
     145                 : 
     146                 :     timeout_coro(timeout_coro&& o) noexcept : h_(o.h_)
     147                 :     {
     148                 :         o.h_ = nullptr;
     149                 :     }
     150                 : 
     151                 :     timeout_coro& operator=(timeout_coro&& o) noexcept
     152                 :     {
     153                 :         h_   = o.h_;
     154                 :         o.h_ = nullptr;
     155                 :         return *this;
     156                 :     }
     157                 : };
     158                 : 
     159                 : /** Create a fire-and-forget timeout coroutine.
     160                 : 
     161                 :     Wait on the timer. If it fires without cancellation, signal
     162                 :     the stop source to cancel the paired inner operation.
     163                 : 
     164                 :     @tparam Timer Timer type (`timer` or `native_timer<B>`).
     165                 : 
     166                 :     @param t The timer to wait on (must have expiry set).
     167                 :     @param src Stop source to signal on timeout.
     168                 : */
     169                 : template<typename Timer>
     170                 : timeout_coro
     171              24 : make_timeout(Timer& t, std::stop_source src)
     172                 : {
     173                 :     auto [ec] = co_await t.wait();
     174                 :     if (!ec)
     175                 :         src.request_stop();
     176              48 : }
     177                 : 
     178                 : } // namespace boost::corosio::detail
     179                 : 
     180                 : #endif
        

Generated by: LCOV version 2.3