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 % 25 25
Test Date: 2026-02-26 19:04:11 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
      50                 :         : capy::io_awaitable_promise_base<promise_type>
      51                 :     {
      52                 :         capy::io_env env_storage_;
      53                 : 
      54                 :         /** Store an owned copy of the environment.
      55                 : 
      56                 :             The timeout coroutine can outlive the cancel_at_awaitable
      57                 :             that created it, so it must own its env rather than
      58                 :             pointing to external storage.
      59                 :         */
      60 HIT          24 :         void set_env_owned(capy::io_env env)
      61                 :         {
      62              24 :             env_storage_ = std::move(env);
      63              24 :             set_environment(&env_storage_);
      64              24 :         }
      65                 : 
      66              24 :         timeout_coro get_return_object() noexcept
      67                 :         {
      68                 :             return timeout_coro{
      69                 :                 std::coroutine_handle<promise_type>::from_promise(
      70              24 :                     *this)};
      71                 :         }
      72                 : 
      73              24 :         std::suspend_always initial_suspend() noexcept { return {}; }
      74              24 :         std::suspend_never final_suspend() noexcept { return {}; }
      75              24 :         void return_void() noexcept {}
      76                 :         void unhandled_exception() noexcept {}
      77                 : 
      78                 :         template<class Awaitable>
      79                 :         struct transform_awaiter
      80                 :         {
      81                 :             std::decay_t<Awaitable> a_;
      82                 :             promise_type* p_;
      83                 : 
      84              24 :             bool await_ready() noexcept
      85                 :             {
      86              24 :                 return a_.await_ready();
      87                 :             }
      88                 : 
      89              24 :             decltype(auto) await_resume()
      90                 :             {
      91              24 :                 capy::set_current_frame_allocator(
      92              24 :                     p_->environment()->frame_allocator);
      93              24 :                 return a_.await_resume();
      94                 :             }
      95                 : 
      96                 :             template<class Promise>
      97              24 :             auto await_suspend(
      98                 :                 std::coroutine_handle<Promise> h) noexcept
      99                 :             {
     100                 : #ifdef _MSC_VER
     101                 :                 using R = decltype(
     102                 :                     a_.await_suspend(h, p_->environment()));
     103                 :                 if constexpr (std::is_same_v<
     104                 :                                   R, std::coroutine_handle<>>)
     105                 :                     a_.await_suspend(h, p_->environment())
     106                 :                         .resume();
     107                 :                 else
     108                 :                     return a_.await_suspend(
     109                 :                         h, p_->environment());
     110                 : #else
     111              24 :                 return a_.await_suspend(h, p_->environment());
     112                 : #endif
     113                 :             }
     114                 :         };
     115                 : 
     116                 :         template<class Awaitable>
     117              24 :         auto transform_awaitable(Awaitable&& a)
     118                 :         {
     119                 :             using A = std::decay_t<Awaitable>;
     120                 :             if constexpr (capy::IoAwaitable<A>)
     121                 :             {
     122                 :                 return transform_awaiter<Awaitable>{
     123              48 :                     std::forward<Awaitable>(a), this};
     124                 :             }
     125                 :             else
     126                 :             {
     127                 :                 static_assert(
     128                 :                     sizeof(A) == 0, "requires IoAwaitable");
     129                 :             }
     130              24 :         }
     131                 :     };
     132                 : 
     133                 :     std::coroutine_handle<promise_type> h_;
     134                 : 
     135                 :     timeout_coro() noexcept : h_(nullptr) {}
     136                 : 
     137              24 :     explicit timeout_coro(
     138                 :         std::coroutine_handle<promise_type> h) noexcept
     139              24 :         : h_(h)
     140                 :     {
     141              24 :     }
     142                 : 
     143                 :     // Self-destroying via suspend_never at final_suspend
     144                 :     ~timeout_coro() = default;
     145                 : 
     146                 :     timeout_coro(timeout_coro const&) = delete;
     147                 :     timeout_coro& operator=(timeout_coro const&) = delete;
     148                 : 
     149                 :     timeout_coro(timeout_coro&& o) noexcept
     150                 :         : h_(o.h_)
     151                 :     {
     152                 :         o.h_ = nullptr;
     153                 :     }
     154                 : 
     155                 :     timeout_coro& operator=(timeout_coro&& o) noexcept
     156                 :     {
     157                 :         h_   = o.h_;
     158                 :         o.h_ = nullptr;
     159                 :         return *this;
     160                 :     }
     161                 : };
     162                 : 
     163                 : /** Create a fire-and-forget timeout coroutine.
     164                 : 
     165                 :     Wait on the timer. If it fires without cancellation, signal
     166                 :     the stop source to cancel the paired inner operation.
     167                 : 
     168                 :     @tparam Timer Timer type (`timer` or `native_timer<B>`).
     169                 : 
     170                 :     @param t The timer to wait on (must have expiry set).
     171                 :     @param src Stop source to signal on timeout.
     172                 : */
     173                 : template<typename Timer>
     174              24 : timeout_coro make_timeout(Timer& t, std::stop_source src)
     175                 : {
     176                 :     auto [ec] = co_await t.wait();
     177                 :     if (!ec)
     178                 :         src.request_stop();
     179              48 : }
     180                 : 
     181                 : } // namespace boost::corosio::detail
     182                 : 
     183                 : #endif
        

Generated by: LCOV version 2.3