1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
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/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_IO_IO_SIGNAL_SET_HPP
10  
#ifndef BOOST_COROSIO_IO_IO_SIGNAL_SET_HPP
11  
#define BOOST_COROSIO_IO_IO_SIGNAL_SET_HPP
11  
#define BOOST_COROSIO_IO_IO_SIGNAL_SET_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/io/io_object.hpp>
14  
#include <boost/corosio/io/io_object.hpp>
15  
#include <boost/capy/io_result.hpp>
15  
#include <boost/capy/io_result.hpp>
16  
#include <boost/capy/error.hpp>
16  
#include <boost/capy/error.hpp>
17  
#include <boost/capy/ex/executor_ref.hpp>
17  
#include <boost/capy/ex/executor_ref.hpp>
18  
#include <boost/capy/ex/io_env.hpp>
18  
#include <boost/capy/ex/io_env.hpp>
19  

19  

20  
#include <coroutine>
20  
#include <coroutine>
21  
#include <stop_token>
21  
#include <stop_token>
22  
#include <system_error>
22  
#include <system_error>
23  

23  

24  
namespace boost::corosio {
24  
namespace boost::corosio {
25  

25  

26  
/** Abstract base for asynchronous signal sets.
26  
/** Abstract base for asynchronous signal sets.
27  

27  

28  
    Provides the common signal set interface: `wait` and `cancel`.
28  
    Provides the common signal set interface: `wait` and `cancel`.
29  
    Concrete classes like @ref signal_set add signal registration
29  
    Concrete classes like @ref signal_set add signal registration
30  
    (add, remove, clear) and platform-specific flags.
30  
    (add, remove, clear) and platform-specific flags.
31  

31  

32  
    @par Thread Safety
32  
    @par Thread Safety
33  
    Distinct objects: Safe.
33  
    Distinct objects: Safe.
34  
    Shared objects: Unsafe.
34  
    Shared objects: Unsafe.
35  

35  

36  
    @see signal_set, io_object
36  
    @see signal_set, io_object
37  
*/
37  
*/
38  
class BOOST_COROSIO_DECL io_signal_set : public io_object
38  
class BOOST_COROSIO_DECL io_signal_set : public io_object
39  
{
39  
{
40  
    struct wait_awaitable
40  
    struct wait_awaitable
41  
    {
41  
    {
42  
        io_signal_set& s_;
42  
        io_signal_set& s_;
43  
        std::stop_token token_;
43  
        std::stop_token token_;
44  
        mutable std::error_code ec_;
44  
        mutable std::error_code ec_;
45  
        mutable int signal_number_ = 0;
45  
        mutable int signal_number_ = 0;
46  

46  

47  
        explicit wait_awaitable(io_signal_set& s) noexcept : s_(s) {}
47  
        explicit wait_awaitable(io_signal_set& s) noexcept : s_(s) {}
48  

48  

49  
        bool await_ready() const noexcept
49  
        bool await_ready() const noexcept
50  
        {
50  
        {
51  
            return token_.stop_requested();
51  
            return token_.stop_requested();
52  
        }
52  
        }
53  

53  

54  
        capy::io_result<int> await_resume() const noexcept
54  
        capy::io_result<int> await_resume() const noexcept
55  
        {
55  
        {
56  
            if (token_.stop_requested())
56  
            if (token_.stop_requested())
57  
                return {capy::error::canceled};
57  
                return {capy::error::canceled};
58  
            return {ec_, signal_number_};
58  
            return {ec_, signal_number_};
59  
        }
59  
        }
60  

60  

61  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
61  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
62  
            -> std::coroutine_handle<>
62  
            -> std::coroutine_handle<>
63  
        {
63  
        {
64  
            token_ = env->stop_token;
64  
            token_ = env->stop_token;
65  
            return s_.get().wait(
65  
            return s_.get().wait(
66  
                h, env->executor, token_, &ec_, &signal_number_);
66  
                h, env->executor, token_, &ec_, &signal_number_);
67  
        }
67  
        }
68  
    };
68  
    };
69  

69  

70  
public:
70  
public:
71  
    /** Define backend hooks for signal set wait and cancel.
71  
    /** Define backend hooks for signal set wait and cancel.
72  

72  

73  
        Platform backends derive from this to implement
73  
        Platform backends derive from this to implement
74  
        signal delivery notification.
74  
        signal delivery notification.
75  
    */
75  
    */
76  
    struct implementation : io_object::implementation
76  
    struct implementation : io_object::implementation
77  
    {
77  
    {
78  
        /** Initiate an asynchronous wait for a signal.
78  
        /** Initiate an asynchronous wait for a signal.
79  

79  

80  
            @param h Coroutine handle to resume on completion.
80  
            @param h Coroutine handle to resume on completion.
81  
            @param ex Executor for dispatching the completion.
81  
            @param ex Executor for dispatching the completion.
82  
            @param token Stop token for cancellation.
82  
            @param token Stop token for cancellation.
83  
            @param ec Output error code.
83  
            @param ec Output error code.
84  
            @param signo Output signal number.
84  
            @param signo Output signal number.
85  

85  

86  
            @return Coroutine handle to resume immediately.
86  
            @return Coroutine handle to resume immediately.
87  
        */
87  
        */
88  
        virtual std::coroutine_handle<> wait(
88  
        virtual std::coroutine_handle<> wait(
89  
            std::coroutine_handle<> h,
89  
            std::coroutine_handle<> h,
90  
            capy::executor_ref ex,
90  
            capy::executor_ref ex,
91  
            std::stop_token token,
91  
            std::stop_token token,
92  
            std::error_code* ec,
92  
            std::error_code* ec,
93  
            int* signo) = 0;
93  
            int* signo) = 0;
94  

94  

95  
        /** Cancel all pending wait operations.
95  
        /** Cancel all pending wait operations.
96  

96  

97  
            Cancelled waiters complete with an error that
97  
            Cancelled waiters complete with an error that
98  
            compares equal to `capy::cond::canceled`.
98  
            compares equal to `capy::cond::canceled`.
99  
        */
99  
        */
100  
        virtual void cancel() = 0;
100  
        virtual void cancel() = 0;
101  
    };
101  
    };
102  

102  

103  
    /** Cancel all operations associated with the signal set.
103  
    /** Cancel all operations associated with the signal set.
104  

104  

105  
        Forces the completion of any pending asynchronous wait
105  
        Forces the completion of any pending asynchronous wait
106  
        operations. Each cancelled operation completes with an error
106  
        operations. Each cancelled operation completes with an error
107  
        code that compares equal to `capy::cond::canceled`.
107  
        code that compares equal to `capy::cond::canceled`.
108  

108  

109  
        Cancellation does not alter the set of registered signals.
109  
        Cancellation does not alter the set of registered signals.
110  
    */
110  
    */
111  
    void cancel()
111  
    void cancel()
112  
    {
112  
    {
113  
        do_cancel();
113  
        do_cancel();
114  
    }
114  
    }
115  

115  

116  
    /** Wait for a signal to be delivered.
116  
    /** Wait for a signal to be delivered.
117  

117  

118  
        The operation supports cancellation via `std::stop_token` through
118  
        The operation supports cancellation via `std::stop_token` through
119  
        the affine awaitable protocol. If the associated stop token is
119  
        the affine awaitable protocol. If the associated stop token is
120  
        triggered, the operation completes immediately with an error
120  
        triggered, the operation completes immediately with an error
121  
        that compares equal to `capy::cond::canceled`.
121  
        that compares equal to `capy::cond::canceled`.
122  

122  

123  
        This signal set must outlive the returned awaitable.
123  
        This signal set must outlive the returned awaitable.
124  

124  

125  
        @return An awaitable that completes with `io_result<int>`.
125  
        @return An awaitable that completes with `io_result<int>`.
126  
            Returns the signal number when a signal is delivered,
126  
            Returns the signal number when a signal is delivered,
127  
            or an error code on failure.
127  
            or an error code on failure.
128  
    */
128  
    */
129  
    auto wait()
129  
    auto wait()
130  
    {
130  
    {
131  
        return wait_awaitable(*this);
131  
        return wait_awaitable(*this);
132  
    }
132  
    }
133  

133  

134  
protected:
134  
protected:
135  
    /** Dispatch cancel to the concrete implementation. */
135  
    /** Dispatch cancel to the concrete implementation. */
136  
    virtual void do_cancel() = 0;
136  
    virtual void do_cancel() = 0;
137  

137  

138  
    explicit io_signal_set(handle h) noexcept : io_object(std::move(h)) {}
138  
    explicit io_signal_set(handle h) noexcept : io_object(std::move(h)) {}
139  

139  

140  
    /// Move construct.
140  
    /// Move construct.
141  
    io_signal_set(io_signal_set&& other) noexcept : io_object(std::move(other))
141  
    io_signal_set(io_signal_set&& other) noexcept : io_object(std::move(other))
142  
    {
142  
    {
143  
    }
143  
    }
144  

144  

145  
    /// Move assign.
145  
    /// Move assign.
146  
    io_signal_set& operator=(io_signal_set&& other) noexcept
146  
    io_signal_set& operator=(io_signal_set&& other) noexcept
147  
    {
147  
    {
148  
        if (this != &other)
148  
        if (this != &other)
149  
            h_ = std::move(other.h_);
149  
            h_ = std::move(other.h_);
150  
        return *this;
150  
        return *this;
151  
    }
151  
    }
152  

152  

153  
    io_signal_set(io_signal_set const&)            = delete;
153  
    io_signal_set(io_signal_set const&)            = delete;
154  
    io_signal_set& operator=(io_signal_set const&) = delete;
154  
    io_signal_set& operator=(io_signal_set const&) = delete;
155  

155  

156  
private:
156  
private:
157  
    implementation& get() const noexcept
157  
    implementation& get() const noexcept
158  
    {
158  
    {
159  
        return *static_cast<implementation*>(h_.get());
159  
        return *static_cast<implementation*>(h_.get());
160  
    }
160  
    }
161  
};
161  
};
162  

162  

163  
} // namespace boost::corosio
163  
} // namespace boost::corosio
164  

164  

165  
#endif
165  
#endif