1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_ASYNC_EVENT_HPP
10  
#ifndef BOOST_CAPY_ASYNC_EVENT_HPP
11  
#define BOOST_CAPY_ASYNC_EVENT_HPP
11  
#define BOOST_CAPY_ASYNC_EVENT_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/concept/executor.hpp>
14  
#include <boost/capy/concept/executor.hpp>
15  
#include <boost/capy/coro.hpp>
15  
#include <boost/capy/coro.hpp>
16  
#include <boost/capy/ex/executor_ref.hpp>
16  
#include <boost/capy/ex/executor_ref.hpp>
17  

17  

18  
#include <stop_token>
18  
#include <stop_token>
19  

19  

20  
#include <coroutine>
20  
#include <coroutine>
21  
#include <utility>
21  
#include <utility>
22  

22  

23  
namespace boost {
23  
namespace boost {
24  
namespace capy {
24  
namespace capy {
25  

25  

26  
/** An asynchronous event for coroutines.
26  
/** An asynchronous event for coroutines.
27  

27  

28  
    This event provides a way to notify multiple coroutines that some
28  
    This event provides a way to notify multiple coroutines that some
29  
    condition has occurred. When a coroutine awaits an unset event, it
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
30  
    suspends and is added to an intrusive wait queue. When the event is
31  
    set, all waiting coroutines are resumed.
31  
    set, all waiting coroutines are resumed.
32  

32  

33  
    @par Zero Allocation
33  
    @par Zero Allocation
34  

34  

35  
    The wait queue node is embedded in the wait_awaiter, which lives on
35  
    The wait queue node is embedded in the wait_awaiter, which lives on
36  
    the coroutine frame during suspension. No heap allocation occurs.
36  
    the coroutine frame during suspension. No heap allocation occurs.
37  

37  

38  
    @par Thread Safety
38  
    @par Thread Safety
39  

39  

40  
    This event is NOT thread-safe. It is designed for single-threaded
40  
    This event is NOT thread-safe. It is designed for single-threaded
41  
    use where multiple coroutines may wait for a condition.
41  
    use where multiple coroutines may wait for a condition.
42  

42  

43  
    @par Example
43  
    @par Example
44  
    @code
44  
    @code
45  
    async_event event;
45  
    async_event event;
46  

46  

47  
    task<> waiter() {
47  
    task<> waiter() {
48  
        co_await event.wait();
48  
        co_await event.wait();
49  
        // ... event was set ...
49  
        // ... event was set ...
50  
    }
50  
    }
51  

51  

52  
    task<> notifier() {
52  
    task<> notifier() {
53  
        // ... do some work ...
53  
        // ... do some work ...
54  
        event.set();  // Wake all waiters
54  
        event.set();  // Wake all waiters
55  
    }
55  
    }
56  
    @endcode
56  
    @endcode
57  
*/
57  
*/
58  
class async_event
58  
class async_event
59  
{
59  
{
60  
public:
60  
public:
61  
    class wait_awaiter;
61  
    class wait_awaiter;
62  

62  

63  
    /** Awaiter returned by wait().
63  
    /** Awaiter returned by wait().
64  

64  

65  
        The awaiter contains the intrusive list node, which is stored
65  
        The awaiter contains the intrusive list node, which is stored
66  
        on the coroutine frame during suspension.
66  
        on the coroutine frame during suspension.
67  
    */
67  
    */
68  
    class wait_awaiter
68  
    class wait_awaiter
69  
    {
69  
    {
70  
        friend class async_event;
70  
        friend class async_event;
71  

71  

72  
        async_event* e_;
72  
        async_event* e_;
73  
        wait_awaiter* next_ = nullptr;
73  
        wait_awaiter* next_ = nullptr;
74  
        std::coroutine_handle<> h_;
74  
        std::coroutine_handle<> h_;
75  
        executor_ref ex_;
75  
        executor_ref ex_;
76  

76  

77  
    public:
77  
    public:
78  
        explicit wait_awaiter(async_event* e) noexcept
78  
        explicit wait_awaiter(async_event* e) noexcept
79  
            : e_(e)
79  
            : e_(e)
80  
        {
80  
        {
81  
        }
81  
        }
82  

82  

83  
        bool await_ready() const noexcept
83  
        bool await_ready() const noexcept
84  
        {
84  
        {
85  
            return e_->set_;
85  
            return e_->set_;
86  
        }
86  
        }
87  

87  

88  
        bool await_suspend(std::coroutine_handle<> h) noexcept
88  
        bool await_suspend(std::coroutine_handle<> h) noexcept
89  
        {
89  
        {
90  
            h_ = h;
90  
            h_ = h;
91  
            ex_ = {};
91  
            ex_ = {};
92  
            if(e_->tail_)
92  
            if(e_->tail_)
93  
                e_->tail_->next_ = this;
93  
                e_->tail_->next_ = this;
94  
            else
94  
            else
95  
                e_->head_ = this;
95  
                e_->head_ = this;
96  
            e_->tail_ = this;
96  
            e_->tail_ = this;
97  
            return true;
97  
            return true;
98  
        }
98  
        }
99  

99  

100  
        /** IoAwaitable protocol overload. */
100  
        /** IoAwaitable protocol overload. */
101  
        std::coroutine_handle<>
101  
        std::coroutine_handle<>
102  
        await_suspend(
102  
        await_suspend(
103  
            std::coroutine_handle<> h,
103  
            std::coroutine_handle<> h,
104  
            executor_ref ex,
104  
            executor_ref ex,
105  
            std::stop_token = {}) noexcept
105  
            std::stop_token = {}) noexcept
106  
        {
106  
        {
107  
            h_ = h;
107  
            h_ = h;
108  
            ex_ = ex;
108  
            ex_ = ex;
109  
            if(e_->tail_)
109  
            if(e_->tail_)
110  
                e_->tail_->next_ = this;
110  
                e_->tail_->next_ = this;
111  
            else
111  
            else
112  
                e_->head_ = this;
112  
                e_->head_ = this;
113  
            e_->tail_ = this;
113  
            e_->tail_ = this;
114  
            return std::noop_coroutine();
114  
            return std::noop_coroutine();
115  
        }
115  
        }
116  

116  

117  
        void await_resume() const noexcept
117  
        void await_resume() const noexcept
118  
        {
118  
        {
119  
        }
119  
        }
120  
    };
120  
    };
121  

121  

122  
    async_event() = default;
122  
    async_event() = default;
123  

123  

124  
    // Non-copyable, non-movable
124  
    // Non-copyable, non-movable
125  
    async_event(async_event const&) = delete;
125  
    async_event(async_event const&) = delete;
126  
    async_event& operator=(async_event const&) = delete;
126  
    async_event& operator=(async_event const&) = delete;
127  

127  

128  
    /** Returns an awaiter that waits until the event is set.
128  
    /** Returns an awaiter that waits until the event is set.
129  

129  

130  
        If the event is already set, returns immediately.
130  
        If the event is already set, returns immediately.
131  
        Otherwise suspends until set() is called.
131  
        Otherwise suspends until set() is called.
132  

132  

133  
        @return An awaiter that suspends if the event is not set,
133  
        @return An awaiter that suspends if the event is not set,
134  
                or completes immediately if set.
134  
                or completes immediately if set.
135  
    */
135  
    */
136  
    wait_awaiter wait() noexcept
136  
    wait_awaiter wait() noexcept
137  
    {
137  
    {
138  
        return wait_awaiter{this};
138  
        return wait_awaiter{this};
139  
    }
139  
    }
140  

140  

141  
    /** Sets the event.
141  
    /** Sets the event.
142  

142  

143  
        All tasks waiting for the event will be immediately awakened.
143  
        All tasks waiting for the event will be immediately awakened.
144  
        Subsequent calls to wait() will return immediately until
144  
        Subsequent calls to wait() will return immediately until
145  
        clear() is called.
145  
        clear() is called.
146  
    */
146  
    */
147  
    void set() noexcept
147  
    void set() noexcept
148  
    {
148  
    {
149  
        set_ = true;
149  
        set_ = true;
150  
        while(head_)
150  
        while(head_)
151  
        {
151  
        {
152  
            auto* waiter = head_;
152  
            auto* waiter = head_;
153  
            head_ = head_->next_;
153  
            head_ = head_->next_;
154  
            if(waiter->ex_)
154  
            if(waiter->ex_)
155  
                waiter->ex_.dispatch(waiter->h_);
155  
                waiter->ex_.dispatch(waiter->h_);
156  
            else
156  
            else
157  
                waiter->h_.resume();
157  
                waiter->h_.resume();
158  
        }
158  
        }
159  
        tail_ = nullptr;
159  
        tail_ = nullptr;
160  
    }
160  
    }
161  

161  

162  
    /** Clears the event.
162  
    /** Clears the event.
163  

163  

164  
        Subsequent calls to wait() will block until set() is called again.
164  
        Subsequent calls to wait() will block until set() is called again.
165  
    */
165  
    */
166  
    void clear() noexcept
166  
    void clear() noexcept
167  
    {
167  
    {
168  
        set_ = false;
168  
        set_ = false;
169  
    }
169  
    }
170  

170  

171  
    /** Returns true if the event is currently set.
171  
    /** Returns true if the event is currently set.
172  
    */
172  
    */
173  
    bool is_set() const noexcept
173  
    bool is_set() const noexcept
174  
    {
174  
    {
175  
        return set_;
175  
        return set_;
176  
    }
176  
    }
177  

177  

178  
private:
178  
private:
179  
    bool set_ = false;
179  
    bool set_ = false;
180  
    wait_awaiter* head_ = nullptr;
180  
    wait_awaiter* head_ = nullptr;
181  
    wait_awaiter* tail_ = nullptr;
181  
    wait_awaiter* tail_ = nullptr;
182  
};
182  
};
183  

183  

184  
} // namespace capy
184  
} // namespace capy
185  
} // namespace boost
185  
} // namespace boost
186  

186  

187  
#endif
187  
#endif