include/boost/corosio/signal_set.hpp

95.7% Lines (22/0/23) 91.7% List of functions (11/0/12)
signal_set.hpp
f(x) Functions (12)
Function Calls Lines Blocks
boost::corosio::operator|(boost::corosio::signal_set::flags_t, boost::corosio::signal_set::flags_t) :123 7x 100.0% 100.0% boost::corosio::operator&(boost::corosio::signal_set::flags_t, boost::corosio::signal_set::flags_t) :130 880x 100.0% 100.0% boost::corosio::operator|=(boost::corosio::signal_set::flags_t&, boost::corosio::signal_set::flags_t) :137 2x 100.0% 100.0% boost::corosio::signal_set::signal_set<>(boost::capy::execution_context&, int) :210 47x 100.0% 71.0% boost::corosio::signal_set::signal_set<int, int>(boost::capy::execution_context&, int, int, int) :210 3x 100.0% 82.0% boost::corosio::signal_set::signal_set<int>(boost::capy::execution_context&, int, int) :210 12x 100.0% 78.0% boost::corosio::signal_set::signal_set<>(boost::capy::execution_context&, int)::{lambda(std::error_code)#1}::operator()(std::error_code) const :213 6x 80.0% 50.0% boost::corosio::signal_set::signal_set<int>(boost::capy::execution_context&, int, int)::{lambda(std::error_code)#1}::operator()(std::error_code) const :213 0 0.0% 0.0% boost::corosio::signal_set::signal_set<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :230 2x 100.0% 100.0% boost::corosio::signal_set::signal_set<boost::corosio::io_context::executor_type, int>(boost::corosio::io_context::executor_type const&, int, int) :249 2x 100.0% 100.0% boost::corosio::signal_set::add(int) :320 145x 100.0% 100.0% boost::corosio::signal_set::get() const :351 253x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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_SIGNAL_SET_HPP
13 #define BOOST_COROSIO_SIGNAL_SET_HPP
14
15 #include <boost/corosio/detail/config.hpp>
16 #include <boost/corosio/io/io_signal_set.hpp>
17 #include <boost/capy/ex/execution_context.hpp>
18 #include <boost/capy/concept/executor.hpp>
19
20 #include <concepts>
21 #include <system_error>
22 #include <type_traits>
23
24 /*
25 Signal Set Public API
26 =====================
27
28 This header provides the public interface for asynchronous signal handling.
29 The implementation is split across platform-specific files:
30 - posix/signals.cpp: Uses sigaction() for robust signal handling
31 - iocp/signals.cpp: Uses C runtime signal() (Windows lacks sigaction)
32
33 Key design decisions:
34
35 1. Abstract flag values: The flags_t enum uses arbitrary bit positions
36 (not SA_RESTART, etc.) to avoid including <signal.h> in public headers.
37 The POSIX implementation maps these to actual SA_* constants internally.
38
39 2. Flag conflict detection: When multiple signal_sets register for the
40 same signal, they must use compatible flags. The first registration
41 establishes the flags; subsequent registrations must match or use
42 dont_care.
43
44 3. Polymorphic implementation: implementation is an abstract base that
45 platform-specific implementations (posix_signal, win_signal)
46 derive from. This allows the public API to be platform-agnostic.
47
48 4. The inline add(int) overload avoids a virtual call for the common case
49 of adding signals without flags (delegates to add(int, none)).
50 */
51
52 namespace boost::corosio {
53
54 /** An asynchronous signal set for coroutine I/O.
55
56 This class provides the ability to perform an asynchronous wait
57 for one or more signals to occur. The signal set registers for
58 signals using sigaction() on POSIX systems or the C runtime
59 signal() function on Windows.
60
61 @par Thread Safety
62 Distinct objects: Safe.@n
63 Shared objects: Unsafe. A signal_set must not have concurrent
64 wait operations.
65
66 @par Semantics
67 Wraps platform signal handling (sigaction on POSIX, C runtime
68 signal() on Windows). Operations dispatch to OS signal APIs
69 via the io_context reactor.
70
71 @par Supported Signals
72 On Windows, the following signals are supported:
73 SIGINT, SIGTERM, SIGABRT, SIGFPE, SIGILL, SIGSEGV.
74
75 @par Example
76 @par !example wait_for_shutdown
77 */
78 class BOOST_COROSIO_DECL signal_set : public io_signal_set
79 {
80 public:
81 /** Flags for signal registration.
82
83 These flags control the behavior of signal handling. Multiple
84 flags can be combined using the bitwise OR operator.
85
86 @note Flags only have effect on POSIX systems. On Windows,
87 only `none` and `dont_care` are supported; other flags return
88 `operation_not_supported`.
89 */
90 enum flags_t : unsigned
91 {
92 /// Use existing flags if signal is already registered.
93 /// When adding a signal that's already registered by another
94 /// signal_set, this flag indicates acceptance of whatever
95 /// flags were used for the existing registration.
96 dont_care = 1u << 16,
97
98 /// No special flags.
99 none = 0,
100
101 /// Restart interrupted system calls.
102 /// Equivalent to SA_RESTART on POSIX systems.
103 restart = 1u << 0,
104
105 /// Don't generate SIGCHLD when children stop.
106 /// Equivalent to SA_NOCLDSTOP on POSIX systems.
107 no_child_stop = 1u << 1,
108
109 /// Don't create zombie processes on child termination.
110 /// Equivalent to SA_NOCLDWAIT on POSIX systems.
111 no_child_wait = 1u << 2,
112
113 /// Don't block the signal while its handler runs.
114 /// Equivalent to SA_NODEFER on POSIX systems.
115 no_defer = 1u << 3,
116
117 /// Reset handler to SIG_DFL after one invocation.
118 /// Equivalent to SA_RESETHAND on POSIX systems.
119 reset_handler = 1u << 4
120 };
121
122 /// Combine two flag values.
123 7x friend constexpr flags_t operator|(flags_t a, flags_t b) noexcept
124 {
125 return static_cast<flags_t>(
126 7x static_cast<unsigned>(a) | static_cast<unsigned>(b));
127 }
128
129 /// Mask two flag values.
130 880x friend constexpr flags_t operator&(flags_t a, flags_t b) noexcept
131 {
132 return static_cast<flags_t>(
133 880x static_cast<unsigned>(a) & static_cast<unsigned>(b));
134 }
135
136 /// Compound assignment OR.
137 2x friend constexpr flags_t& operator|=(flags_t& a, flags_t b) noexcept
138 {
139 2x return a = a | b;
140 }
141
142 /// Compound assignment AND.
143 friend constexpr flags_t& operator&=(flags_t& a, flags_t b) noexcept
144 {
145 return a = a & b;
146 }
147
148 /// Bitwise NOT (complement).
149 friend constexpr flags_t operator~(flags_t a) noexcept
150 {
151 return static_cast<flags_t>(~static_cast<unsigned>(a));
152 }
153
154 /** Define backend hooks for signal set operations.
155
156 Platform backends derive from this to provide signal
157 registration via sigaction (POSIX) or the C runtime
158 signal() function (Windows).
159 */
160 struct implementation : io_signal_set::implementation
161 {
162 /** Register a signal with the given flags.
163
164 @param signal_number The signal to register.
165 @param flags Platform-specific signal handling flags.
166
167 @return Error code on failure, empty on success.
168 */
169 virtual std::error_code add(int signal_number, flags_t flags) = 0;
170
171 /** Unregister a signal.
172
173 @param signal_number The signal to remove.
174
175 @return Error code on failure, empty on success.
176 */
177 virtual std::error_code remove(int signal_number) = 0;
178
179 /** Unregister all signals.
180
181 @return Error code on failure, empty on success.
182 */
183 virtual std::error_code clear() = 0;
184 };
185
186 /** Destructor.
187
188 Cancels any pending operations and releases signal resources.
189 */
190 ~signal_set() override;
191
192 /** Construct an empty signal set.
193
194 @param ctx The execution context that will own this signal set.
195 */
196 explicit signal_set(capy::execution_context& ctx);
197
198 /** Construct a signal set with initial signals.
199
200 @param ctx The execution context that will own this signal set.
201 @param signal First signal number to add.
202 @param signals Additional signal numbers to add.
203
204 @throws std::system_error Thrown on failure.
205
206 @see add for the non-throwing form: construct with the
207 context alone, then `add()` each signal.
208 */
209 template<std::convertible_to<int>... Signals>
210 62x signal_set(capy::execution_context& ctx, int signal, Signals... signals)
211 62x : signal_set(ctx)
212 {
213 6x auto check = [](std::error_code ec) {
214 6x if (ec)
215 throw std::system_error(ec);
216 };
217 62x check(add(signal));
218 15x (check(add(signals)), ...);
219 62x }
220
221 /** Construct an empty signal set from an executor.
222
223 The signal set is associated with the executor's context.
224
225 @param ex The executor whose context will own this signal set.
226 */
227 template<class Ex>
228 requires(!std::same_as<std::remove_cvref_t<Ex>, signal_set>) &&
229 capy::Executor<Ex>
230 2x explicit signal_set(Ex const& ex) : signal_set(ex.context())
231 {
232 2x }
233
234 /** Construct a signal set with initial signals from an executor.
235
236 The signal set is associated with the executor's context.
237
238 @param ex The executor whose context will own this signal set.
239 @param signal First signal number to add.
240 @param signals Additional signal numbers to add.
241
242 @throws std::system_error Thrown on failure.
243
244 @see add for the non-throwing form: construct with the
245 executor alone, then `add()` each signal.
246 */
247 template<class Ex, std::convertible_to<int>... Signals>
248 requires capy::Executor<Ex>
249 2x signal_set(Ex const& ex, int signal, Signals... signals)
250 2x : signal_set(ex.context(), signal, signals...)
251 {
252 2x }
253
254 /** Move constructor.
255
256 Transfers ownership of the signal set resources.
257
258 @param other The signal set to move from.
259
260 @pre No awaitables returned by @p other's methods exist.
261 @pre The execution context associated with @p other must
262 outlive this signal set.
263 */
264 signal_set(signal_set&& other) noexcept;
265
266 /** Move assignment operator.
267
268 Closes any existing signal set and transfers ownership.
269
270 @param other The signal set to move from.
271
272 @pre No awaitables returned by either `*this` or @p other's
273 methods exist.
274 @pre The execution context associated with @p other must
275 outlive this signal set.
276
277 @return Reference to this signal set.
278 */
279 signal_set& operator=(signal_set&& other) noexcept;
280
281 signal_set(signal_set const&) = delete;
282 signal_set& operator=(signal_set const&) = delete;
283
284 /** Add a signal to the signal set.
285
286 This function adds the specified signal to the set with the
287 specified flags. It has no effect if the signal is already
288 in the set with the same flags.
289
290 If the signal is already registered globally (by another
291 signal_set) and the flags differ, an error is returned
292 unless one of them has the `dont_care` flag.
293
294 The first signal registration on an execution context
295 installs the process signal-delivery pipe; if that
296 installation fails the error is returned, and the next
297 call retries it.
298
299 @param signal_number The signal to be added to the set.
300 @param flags The flags to apply when registering the signal.
301 On POSIX systems, these map to sigaction() flags.
302 On Windows, only `none` and `dont_care` are supported;
303 other flags cause `errc::operation_not_supported` to
304 be returned.
305
306 @return Success, or an error if the signal could not be added.
307 Returns `errc::invalid_argument` if the signal is already
308 registered with different flags.
309 */
310 [[nodiscard]] std::error_code add(int signal_number, flags_t flags);
311
312 /** Add a signal to the signal set with default flags.
313
314 This is equivalent to calling `add(signal_number, none)`.
315
316 @param signal_number The signal to be added to the set.
317
318 @return Success, or an error if the signal could not be added.
319 */
320 145x [[nodiscard]] std::error_code add(int signal_number)
321 {
322 145x return add(signal_number, none);
323 }
324
325 /** Remove a signal from the signal set.
326
327 This function removes the specified signal from the set. It has
328 no effect if the signal is not in the set.
329
330 @param signal_number The signal to be removed from the set.
331
332 @return Success, or an error if the signal could not be removed.
333 */
334 [[nodiscard]] std::error_code remove(int signal_number);
335
336 /** Remove all signals from the signal set.
337
338 This function removes all signals from the set. It has no effect
339 if the set is already empty.
340
341 @return Success, or an error if resetting any signal handler fails.
342 */
343 [[nodiscard]] std::error_code clear();
344
345 protected:
346 explicit signal_set(handle h) noexcept : io_signal_set(std::move(h)) {}
347
348 private:
349 void do_cancel() noexcept override;
350
351 253x implementation& get() const noexcept
352 {
353 253x return *static_cast<implementation*>(h_.get());
354 }
355 };
356
357 } // namespace boost::corosio
358
359 #endif
360