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

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

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

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

 
24 +
namespace boost::corosio {
 
25 +

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

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

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

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

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

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

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

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

 
70 +
public:
 
71 +
    struct implementation : io_object::implementation
 
72 +
    {
 
73 +
        virtual std::coroutine_handle<> wait(
 
74 +
            std::coroutine_handle<>,
 
75 +
            capy::executor_ref,
 
76 +
            std::stop_token,
 
77 +
            std::error_code*,
 
78 +
            int*) = 0;
 
79 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
143 +
#endif