GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 40 / 0 / 40
Functions: 100.0% 9 / 0 / 9
Branches: 100.0% 8 / 0 / 8

libs/capy/include/boost/capy/ex/async_event.hpp
Line Branch Exec Source
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
4/4
std::__n4861::coroutine_handle<void> boost::capy::async_event::wait_awaiter::await_suspend<boost::capy::(anonymous namespace)::queuing_executor>(std::__n4861::coroutine_handle<void>, boost::capy::(anonymous namespace)::queuing_executor const&, std::stop_token):
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
std::__n4861::coroutine_handle<void> boost::capy::async_event::wait_awaiter::await_suspend<boost::capy::executor_ref>(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref const&, std::stop_token):
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
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
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 24 times.
41 while(head_)
151 {
152 17 auto* waiter = head_;
153 17 head_ = head_->next_;
154
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 13 times.
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
188