LCOV - code coverage report
Current view: top level - boost/capy/ex - async_event.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 32 32
Test Date: 2026-01-24 00:02:10 Functions: 100.0 % 9 9

            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_ASYNC_EVENT_HPP
      11              : #define BOOST_CAPY_ASYNC_EVENT_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/concept/executor.hpp>
      15              : #include <boost/capy/coro.hpp>
      16              : #include <boost/capy/ex/executor_ref.hpp>
      17              : 
      18              : #include <stop_token>
      19              : 
      20              : #include <coroutine>
      21              : #include <utility>
      22              : 
      23              : namespace boost {
      24              : namespace capy {
      25              : 
      26              : /** An asynchronous event for coroutines.
      27              : 
      28              :     This event provides a way to notify multiple coroutines that some
      29              :     condition has occurred. When a coroutine awaits an unset event, it
      30              :     suspends and is added to an intrusive wait queue. When the event is
      31              :     set, all waiting coroutines are resumed.
      32              : 
      33              :     @par Zero Allocation
      34              : 
      35              :     The wait queue node is embedded in the wait_awaiter, which lives on
      36              :     the coroutine frame during suspension. No heap allocation occurs.
      37              : 
      38              :     @par Thread Safety
      39              : 
      40              :     This event is NOT thread-safe. It is designed for single-threaded
      41              :     use where multiple coroutines may wait for a condition.
      42              : 
      43              :     @par Example
      44              :     @code
      45              :     async_event event;
      46              : 
      47              :     task<> waiter() {
      48              :         co_await event.wait();
      49              :         // ... event was set ...
      50              :     }
      51              : 
      52              :     task<> notifier() {
      53              :         // ... do some work ...
      54              :         event.set();  // Wake all waiters
      55              :     }
      56              :     @endcode
      57              : */
      58              : class async_event
      59              : {
      60              : public:
      61              :     class wait_awaiter;
      62              : 
      63              :     /** Awaiter returned by wait().
      64              : 
      65              :         The awaiter contains the intrusive list node, which is stored
      66              :         on the coroutine frame during suspension.
      67              :     */
      68              :     class wait_awaiter
      69              :     {
      70              :         friend class async_event;
      71              : 
      72              :         async_event* e_;
      73              :         wait_awaiter* next_ = nullptr;
      74              :         std::coroutine_handle<> h_;
      75              :         executor_ref ex_;
      76              : 
      77              :     public:
      78           24 :         explicit wait_awaiter(async_event* e) noexcept
      79           24 :             : e_(e)
      80              :         {
      81           24 :         }
      82              : 
      83           21 :         bool await_ready() const noexcept
      84              :         {
      85           21 :             return e_->set_;
      86              :         }
      87              : 
      88              :         bool await_suspend(std::coroutine_handle<> h) noexcept
      89              :         {
      90              :             h_ = h;
      91              :             ex_ = {};
      92              :             if(e_->tail_)
      93              :                 e_->tail_->next_ = this;
      94              :             else
      95              :                 e_->head_ = this;
      96              :             e_->tail_ = this;
      97              :             return true;
      98              :         }
      99              : 
     100              :         /** IoAwaitable protocol overload. */
     101              :         template<Executor Ex>
     102           17 :         auto await_suspend(
     103              :             std::coroutine_handle<> h,
     104              :             Ex const& ex,
     105              :             std::stop_token = {}) noexcept -> std::coroutine_handle<>
     106              :         {
     107           17 :             h_ = h;
     108           17 :             ex_ = ex;
     109           17 :             if(e_->tail_)
     110            6 :                 e_->tail_->next_ = this;
     111              :             else
     112           11 :                 e_->head_ = this;
     113           17 :             e_->tail_ = this;
     114           17 :             return std::noop_coroutine();
     115              :         }
     116              : 
     117           16 :         void await_resume() const noexcept
     118              :         {
     119           16 :         }
     120              :     };
     121              : 
     122              :     async_event() = default;
     123              : 
     124              :     // Non-copyable, non-movable
     125              :     async_event(async_event const&) = delete;
     126              :     async_event& operator=(async_event const&) = delete;
     127              : 
     128              :     /** Returns an awaiter that waits until the event is set.
     129              : 
     130              :         If the event is already set, returns immediately.
     131              :         Otherwise suspends until set() is called.
     132              : 
     133              :         @return An awaiter that suspends if the event is not set,
     134              :                 or completes immediately if set.
     135              :     */
     136           24 :     wait_awaiter wait() noexcept
     137              :     {
     138           24 :         return wait_awaiter{this};
     139              :     }
     140              : 
     141              :     /** Sets the event.
     142              : 
     143              :         All tasks waiting for the event will be immediately awakened.
     144              :         Subsequent calls to wait() will return immediately until
     145              :         clear() is called.
     146              :     */
     147           24 :     void set() noexcept
     148              :     {
     149           24 :         set_ = true;
     150           41 :         while(head_)
     151              :         {
     152           17 :             auto* waiter = head_;
     153           17 :             head_ = head_->next_;
     154           17 :             if(waiter->ex_)
     155            4 :                 waiter->ex_.dispatch(coro{waiter->h_}).resume();
     156              :             else
     157           13 :                 waiter->h_.resume();
     158              :         }
     159           24 :         tail_ = nullptr;
     160           24 :     }
     161              : 
     162              :     /** Clears the event.
     163              : 
     164              :         Subsequent calls to wait() will block until set() is called again.
     165              :     */
     166            7 :     void clear() noexcept
     167              :     {
     168            7 :         set_ = false;
     169            7 :     }
     170              : 
     171              :     /** Returns true if the event is currently set.
     172              :     */
     173           13 :     bool is_set() const noexcept
     174              :     {
     175           13 :         return set_;
     176              :     }
     177              : 
     178              : private:
     179              :     bool set_ = false;
     180              :     wait_awaiter* head_ = nullptr;
     181              :     wait_awaiter* tail_ = nullptr;
     182              : };
     183              : 
     184              : } // namespace capy
     185              : } // namespace boost
     186              : 
     187              : #endif
        

Generated by: LCOV version 2.3