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  
    struct implementation : io_object::implementation
71  
    struct implementation : io_object::implementation
72  
    {
72  
    {
73  
        virtual std::coroutine_handle<> wait(
73  
        virtual std::coroutine_handle<> wait(
74  
            std::coroutine_handle<>,
74  
            std::coroutine_handle<>,
75  
            capy::executor_ref,
75  
            capy::executor_ref,
76  
            std::stop_token,
76  
            std::stop_token,
77  
            std::error_code*,
77  
            std::error_code*,
78  
            int*) = 0;
78  
            int*) = 0;
79  

79  

80  
        virtual void cancel() = 0;
80  
        virtual void cancel() = 0;
81  
    };
81  
    };
82  

82  

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

84  

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

88  

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

95  

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

97  

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

102  

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

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

113  

112  
protected:
114  
protected:
113  
    /** Dispatch cancel to the concrete implementation. */
115  
    /** Dispatch cancel to the concrete implementation. */
114  
    virtual void do_cancel() = 0;
116  
    virtual void do_cancel() = 0;
115  

117  

116  
    explicit io_signal_set(handle h) noexcept : io_object(std::move(h)) {}
118  
    explicit io_signal_set(handle h) noexcept : io_object(std::move(h)) {}
117  

119  

118  
    /// Move construct.
120  
    /// Move construct.
119  
    io_signal_set(io_signal_set&& other) noexcept : io_object(std::move(other))
121  
    io_signal_set(io_signal_set&& other) noexcept : io_object(std::move(other))
120  
    {
122  
    {
121  
    }
123  
    }
122  

124  

123  
    /// Move assign.
125  
    /// Move assign.
124  
    io_signal_set& operator=(io_signal_set&& other) noexcept
126  
    io_signal_set& operator=(io_signal_set&& other) noexcept
125  
    {
127  
    {
126  
        if (this != &other)
128  
        if (this != &other)
127  
            h_ = std::move(other.h_);
129  
            h_ = std::move(other.h_);
128  
        return *this;
130  
        return *this;
129  
    }
131  
    }
130  

132  

131  
    io_signal_set(io_signal_set const&)            = delete;
133  
    io_signal_set(io_signal_set const&)            = delete;
132  
    io_signal_set& operator=(io_signal_set const&) = delete;
134  
    io_signal_set& operator=(io_signal_set const&) = delete;
133  

135  

134  
private:
136  
private:
135  
    implementation& get() const noexcept
137  
    implementation& get() const noexcept
136  
    {
138  
    {
137  
        return *static_cast<implementation*>(h_.get());
139  
        return *static_cast<implementation*>(h_.get());
138  
    }
140  
    }
139  
};
141  
};
140  

142  

141  
} // namespace boost::corosio
143  
} // namespace boost::corosio
142  

144  

143  
#endif
145  
#endif