LCOV - code coverage report
Current view: top level - boost/capy - io_awaitable.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 29 29
Test Date: 2026-01-21 22:59:19 Functions: 96.4 % 83 80

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_IO_AWAITABLE_HPP
      11              : #define BOOST_CAPY_IO_AWAITABLE_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/coro.hpp>
      15              : #include <boost/capy/ex/executor_ref.hpp>
      16              : #include <boost/capy/ex/get_stop_token.hpp>
      17              : #include <boost/capy/ex/stop_token.hpp>
      18              : 
      19              : #include <coroutine>
      20              : #include <type_traits>
      21              : 
      22              : namespace boost {
      23              : namespace capy {
      24              : 
      25              : /** Tag type for coroutine executor retrieval.
      26              : 
      27              :     This tag is returned by @ref get_executor and intercepted by a
      28              :     promise type's `await_transform` to yield the coroutine's current
      29              :     executor. The tag itself carries no data; it serves only as a
      30              :     sentinel for compile-time dispatch.
      31              : 
      32              :     @see get_executor
      33              :     @see io_awaitable_support
      34              : */
      35              : struct get_executor_tag {};
      36              : 
      37              : /** Return a tag that yields the current executor when awaited.
      38              : 
      39              :     Use `co_await get_executor()` inside a coroutine whose promise
      40              :     type supports executor access (e.g., inherits from
      41              :     @ref io_awaitable_support). The returned executor reflects the
      42              :     executor this coroutine is bound to.
      43              : 
      44              :     @par Example
      45              :     @code
      46              :     task<void> example()
      47              :     {
      48              :         executor_ref ex = co_await get_executor();
      49              :         // ex is the executor this coroutine is bound to
      50              :     }
      51              :     @endcode
      52              : 
      53              :     @par Behavior
      54              :     @li If no executor was set, returns a default-constructed
      55              :         `executor_ref` (where `operator bool()` returns `false`).
      56              :     @li This operation never suspends; `await_ready()` always returns `true`.
      57              : 
      58              :     @return A tag that `await_transform` intercepts to return the executor.
      59              : 
      60              :     @see get_executor_tag
      61              :     @see io_awaitable_support
      62              : */
      63            4 : inline get_executor_tag get_executor() noexcept
      64              : {
      65            4 :     return {};
      66              : }
      67              : 
      68              : /** CRTP mixin that adds I/O awaitable support to a promise type.
      69              : 
      70              :     Inherit from this class to enable these capabilities in your coroutine:
      71              : 
      72              :     1. **Stop token storage** — The mixin stores the `capy::stop_token`
      73              :        that was passed when your coroutine was awaited.
      74              : 
      75              :     2. **Stop token access** — Coroutine code can retrieve the token via
      76              :        `co_await get_stop_token()`.
      77              : 
      78              :     3. **Executor storage** — The mixin stores the `executor_ref`
      79              :        that this coroutine is bound to.
      80              : 
      81              :     4. **Executor access** — Coroutine code can retrieve the executor via
      82              :        `co_await get_executor()`.
      83              : 
      84              :     @tparam Derived The derived promise type (CRTP pattern).
      85              : 
      86              :     @par Basic Usage
      87              : 
      88              :     For coroutines that need to access their stop token or executor:
      89              : 
      90              :     @code
      91              :     struct my_task
      92              :     {
      93              :         struct promise_type : io_awaitable_support<promise_type>
      94              :         {
      95              :             my_task get_return_object();
      96              :             std::suspend_always initial_suspend() noexcept;
      97              :             std::suspend_always final_suspend() noexcept;
      98              :             void return_void();
      99              :             void unhandled_exception();
     100              :         };
     101              : 
     102              :         // ... awaitable interface ...
     103              :     };
     104              : 
     105              :     my_task example()
     106              :     {
     107              :         auto token = co_await get_stop_token();
     108              :         auto ex = co_await get_executor();
     109              :         // Use token and ex...
     110              :     }
     111              :     @endcode
     112              : 
     113              :     @par Custom Awaitable Transformation
     114              : 
     115              :     If your promise needs to transform awaitables (e.g., for affinity or
     116              :     logging), override `transform_awaitable` instead of `await_transform`:
     117              : 
     118              :     @code
     119              :     struct promise_type : io_awaitable_support<promise_type>
     120              :     {
     121              :         template<typename A>
     122              :         auto transform_awaitable(A&& a)
     123              :         {
     124              :             // Your custom transformation logic
     125              :             return std::forward<A>(a);
     126              :         }
     127              :     };
     128              :     @endcode
     129              : 
     130              :     The mixin's `await_transform` intercepts @ref get_stop_token_tag and
     131              :     @ref get_executor_tag, then delegates all other awaitables to your
     132              :     `transform_awaitable`.
     133              : 
     134              :     @par Making Your Coroutine an IoAwaitable
     135              : 
     136              :     The mixin handles the "inside the coroutine" part—accessing the token
     137              :     and executor. To receive these when your coroutine is awaited (satisfying
     138              :     @ref IoAwaitable), implement the `await_suspend` overload on your
     139              :     coroutine return type:
     140              : 
     141              :     @code
     142              :     struct my_task
     143              :     {
     144              :         struct promise_type : io_awaitable_support<promise_type> { ... };
     145              : 
     146              :         std::coroutine_handle<promise_type> h_;
     147              : 
     148              :         // IoAwaitable await_suspend receives and stores the token and executor
     149              :         template<class Ex>
     150              :         coro await_suspend(coro cont, Ex const& ex, capy::stop_token token)
     151              :         {
     152              :             h_.promise().set_stop_token(token);
     153              :             h_.promise().set_executor(ex);
     154              :             // ... rest of suspend logic ...
     155              :         }
     156              :     };
     157              :     @endcode
     158              : 
     159              :     @par Thread Safety
     160              :     The stop token and executor are stored during `await_suspend` and read
     161              :     during `co_await get_stop_token()` or `co_await get_executor()`. These
     162              :     occur on the same logical thread of execution, so no synchronization
     163              :     is required.
     164              : 
     165              :     @see get_stop_token
     166              :     @see get_executor
     167              :     @see IoAwaitable
     168              : */
     169              : template<typename Derived>
     170              : class io_awaitable_support
     171              : {
     172              :     capy::stop_token stop_token_;
     173              :     executor_ref ex_;
     174              : 
     175              : public:
     176              :     /** Store a stop token for later retrieval.
     177              : 
     178              :         Call this from your coroutine type's `await_suspend`
     179              :         overload to make the token available via `co_await get_stop_token()`.
     180              : 
     181              :         @param token The stop token to store.
     182              :     */
     183          161 :     void set_stop_token(capy::stop_token token) noexcept
     184              :     {
     185          161 :         stop_token_ = token;
     186          161 :     }
     187              : 
     188              :     /** Return the stored stop token.
     189              : 
     190              :         @return The stop token, or a default-constructed token if none was set.
     191              :     */
     192           66 :     capy::stop_token const& stop_token() const noexcept
     193              :     {
     194           66 :         return stop_token_;
     195              :     }
     196              : 
     197              :     /** Store an executor for later retrieval.
     198              : 
     199              :         Call this from your coroutine type's `await_suspend`
     200              :         overload to make the executor available via `co_await get_executor()`.
     201              : 
     202              :         @param ex The executor to store.
     203              :     */
     204          184 :     void set_executor(executor_ref ex) noexcept
     205              :     {
     206          184 :         ex_ = ex;
     207          184 :     }
     208              : 
     209              :     /** Return the stored executor.
     210              : 
     211              :         @return The executor, or a default-constructed executor_ref if none was set.
     212              :     */
     213           66 :     executor_ref executor() const noexcept
     214              :     {
     215           66 :         return ex_;
     216              :     }
     217              : 
     218              :     /** Transform an awaitable before co_await.
     219              : 
     220              :         Override this in your derived promise type to customize how
     221              :         awaitables are transformed. The default implementation passes
     222              :         the awaitable through unchanged.
     223              : 
     224              :         @param a The awaitable expression from `co_await a`.
     225              : 
     226              :         @return The transformed awaitable.
     227              :     */
     228              :     template<typename A>
     229              :     decltype(auto) transform_awaitable(A&& a)
     230              :     {
     231              :         return std::forward<A>(a);
     232              :     }
     233              : 
     234              :     /** Intercept co_await expressions.
     235              : 
     236              :         This function handles @ref get_stop_token_tag and @ref get_executor_tag
     237              :         specially, returning an awaiter that yields the stored value. All other
     238              :         awaitables are delegated to @ref transform_awaitable.
     239              : 
     240              :         @param t The awaited expression.
     241              : 
     242              :         @return An awaiter for the expression.
     243              :     */
     244              :     template<typename T>
     245           81 :     auto await_transform(T&& t)
     246              :     {
     247              :         if constexpr (std::is_same_v<std::decay_t<T>, get_stop_token_tag>)
     248              :         {
     249              :             struct awaiter
     250              :             {
     251              :                 capy::stop_token token_;
     252              : 
     253           12 :                 bool await_ready() const noexcept
     254              :                 {
     255           12 :                     return true;
     256              :                 }
     257              : 
     258            1 :                 void await_suspend(coro) const noexcept
     259              :                 {
     260            1 :                 }
     261              : 
     262           11 :                 capy::stop_token await_resume() const noexcept
     263              :                 {
     264           11 :                     return token_;
     265              :                 }
     266              :             };
     267           13 :             return awaiter{stop_token_};
     268              :         }
     269              :         else if constexpr (std::is_same_v<std::decay_t<T>, get_executor_tag>)
     270              :         {
     271              :             struct awaiter
     272              :             {
     273              :                 executor_ref ex_;
     274              : 
     275            2 :                 bool await_ready() const noexcept
     276              :                 {
     277            2 :                     return true;
     278              :                 }
     279              : 
     280            1 :                 void await_suspend(coro) const noexcept
     281              :                 {
     282            1 :                 }
     283              : 
     284            1 :                 executor_ref await_resume() const noexcept
     285              :                 {
     286            1 :                     return ex_;
     287              :                 }
     288              :             };
     289            3 :             return awaiter{ex_};
     290              :         }
     291              :         else
     292              :         {
     293           25 :             return static_cast<Derived*>(this)->transform_awaitable(
     294           65 :                 std::forward<T>(t));
     295              :         }
     296              :     }
     297              : };
     298              : 
     299              : } // namespace capy
     300              : } // namespace boost
     301              : 
     302              : #endif
        

Generated by: LCOV version 2.3