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

111  

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

115  

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

117  

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

122  

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

130  

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

133  

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

140  

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

142  

143  
#endif
143  
#endif