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.
 
72 +

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

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

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

 
95 +
        /** Cancel all pending wait operations.
79  

96  

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

102  

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

104  

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

108  

89  
        Cancellation does not alter the set of registered signals.
109  
        Cancellation does not alter the set of registered signals.
90  
    */
110  
    */
91  
    void cancel()
111  
    void cancel()
92  
    {
112  
    {
93  
        do_cancel();
113  
        do_cancel();
94  
    }
114  
    }
95  

115  

96  
    /** Wait for a signal to be delivered.
116  
    /** Wait for a signal to be delivered.
97  

117  

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

122  

103  
        This signal set must outlive the returned awaitable.
123  
        This signal set must outlive the returned awaitable.
104  

124  

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

133  

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

137  

118  
    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)) {}
119  

139  

120  
    /// Move construct.
140  
    /// Move construct.
121  
    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))
122  
    {
142  
    {
123  
    }
143  
    }
124  

144  

125  
    /// Move assign.
145  
    /// Move assign.
126  
    io_signal_set& operator=(io_signal_set&& other) noexcept
146  
    io_signal_set& operator=(io_signal_set&& other) noexcept
127  
    {
147  
    {
128  
        if (this != &other)
148  
        if (this != &other)
129  
            h_ = std::move(other.h_);
149  
            h_ = std::move(other.h_);
130  
        return *this;
150  
        return *this;
131  
    }
151  
    }
132  

152  

133  
    io_signal_set(io_signal_set const&)            = delete;
153  
    io_signal_set(io_signal_set const&)            = delete;
134  
    io_signal_set& operator=(io_signal_set const&) = delete;
154  
    io_signal_set& operator=(io_signal_set const&) = delete;
135  

155  

136  
private:
156  
private:
137  
    implementation& get() const noexcept
157  
    implementation& get() const noexcept
138  
    {
158  
    {
139  
        return *static_cast<implementation*>(h_.get());
159  
        return *static_cast<implementation*>(h_.get());
140  
    }
160  
    }
141  
};
161  
};
142  

162  

143  
} // namespace boost::corosio
163  
} // namespace boost::corosio
144  

164  

145  
#endif
165  
#endif