include/boost/corosio/native/detail/iocp/win_signal.hpp

100.0% Lines (10 / 10) 50.0% Branches (3 / 6) 100.0% Functions (2 / 2)
win_signal.hpp
f(x) Functions (2)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_SIGNAL_HPP
13 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_SIGNAL_HPP
14
15 #include <boost/corosio/detail/platform.hpp>
16
17 #if BOOST_COROSIO_HAS_IOCP
18
19 #include <boost/corosio/detail/config.hpp>
20 #include <boost/corosio/signal_set.hpp>
21 #include <boost/corosio/detail/intrusive.hpp>
22 #include <boost/corosio/detail/object_ref.hpp>
23 #include <boost/corosio/detail/scheduler_op.hpp>
24 #include <boost/corosio/io/io_object.hpp>
25 #include <boost/capy/continuation.hpp>
26 #include <boost/capy/ex/executor_ref.hpp>
27
28 #include <coroutine>
29 #include <cstddef>
30 #include <cstdint>
31 #include <optional>
32 #include <stop_token>
33 #include <system_error>
34
35 namespace boost::corosio::detail {
36
37 // Forward declarations
38 class win_signals;
39 class win_signal;
40
41 // Maximum signal number supported
42 enum
43 {
44 max_signal_number = 32
45 };
46
47 /** Signal wait operation state. */
48 struct signal_op : scheduler_op
49 {
50 /// The awaitable's continuation; see coro_op::cont.
51 capy::continuation* cont = nullptr;
52 capy::executor_ref d;
53 /// Holds the owning set while this op sits in the scheduler queue.
54 detail::object_ref keep;
55 std::error_code* ec_out = nullptr;
56 int* signal_out = nullptr;
57 int signal_number = 0;
58 signal_op* next_in_queue = nullptr;
59 win_signals* svc = nullptr;
60
61 static void do_complete(
62 void* owner,
63 scheduler_op* base,
64 std::uint32_t bytes,
65 std::uint32_t error);
66
67 signal_op() noexcept;
68 };
69
70 /** Per-signal registration tracking. */
71 struct signal_registration
72 {
73 int signal_number = 0;
74 win_signal* owner = nullptr;
75 std::size_t undelivered = 0;
76 signal_registration* next_in_table = nullptr;
77 signal_registration* prev_in_table = nullptr;
78 signal_registration* next_in_set = nullptr;
79 };
80
81 /** Signal set implementation for Windows.
82
83 This class contains the state for a single signal_set, including
84 registered signals and pending wait operation.
85
86 @note Internal implementation detail. Users interact with signal_set class.
87 */
88 class win_signal final
89 : public signal_set::implementation
90 , public intrusive_list<win_signal>::node
91 {
92 friend class win_signals;
93
94 win_signals& svc_;
95 signal_registration* signals_ = nullptr;
96 signal_op pending_op_;
97 bool waiting_ = false;
98 bool cancelled_ = false;
99
100 /** Routes a stop request into the service's per-operation cancel.
101
102 Deliberately NOT `cancel_wait`: that sets the sticky `cancelled_`
103 latch, which belongs to `cancel()` and scopes to the whole set. A
104 stop token scopes to one wait, so a late fire must be a no-op.
105 */
106 struct token_canceller
107 {
108 win_signal* self;
109 void operator()() const noexcept;
110 };
111
112 /** Armed for the duration of one wait; see wait().
113
114 Never reset while the service's mutex is held: ~stop_callback
115 blocks until a concurrently running callback returns, and that
116 callback takes the same mutex.
117 */
118 std::optional<std::stop_callback<token_canceller>> stop_cb_;
119
120 /** Set when a stop request arrives for the current wait.
121
122 Closes the same lost-wakeup race Task 2 documents: the callback is
123 armed before `start_wait` takes the lock, so a request landing in
124 that window would otherwise be dropped and the wait would park
125 forever. Distinct from the sticky per-set `cancelled_`.
126 */
127 bool token_cancelled_ = false;
128
129 public:
130 explicit win_signal(win_signals& svc) noexcept;
131
132 /// Recycle into the owning service's pool. Defined out-of-line
133 /// after `win_signals` is complete (needs `svc_.pool_`).
134 void retire() noexcept override;
135
136 /** Reset recycled state for reuse.
137
138 @pre refs_ == 0, no registrations, no wait in flight.
139 */
140 void reuse() noexcept;
141
142 std::coroutine_handle<> wait(
143 capy::continuation&,
144 capy::executor_ref,
145 std::stop_token,
146 std::error_code*,
147 int*) override;
148
149 std::error_code add(int signal_number, signal_set::flags_t flags) override;
150 std::error_code remove(int signal_number) override;
151 std::error_code clear() override;
152 void cancel() noexcept override;
153
154 /// Disarm the wait's stop callback. Called before teardown.
155 1693x void disarm_stop() noexcept
156 {
157 1693x stop_cb_.reset();
158 1693x }
159 };
160
161 inline void
162 4x win_signal::reuse() noexcept
163 {
164
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4 times.
4x BOOST_COROSIO_ASSERT(signals_ == nullptr);
165
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 4 times.
4x BOOST_COROSIO_ASSERT(!waiting_);
166
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 4 times.
4x BOOST_COROSIO_ASSERT(!stop_cb_);
167 4x cancelled_ = false;
168 4x token_cancelled_ = false;
169 4x }
170
171 } // namespace boost::corosio::detail
172
173 #endif // BOOST_COROSIO_HAS_IOCP
174
175 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_SIGNAL_HPP
176