include/boost/corosio/native/detail/posix/posix_signal.hpp

100.0% Lines (3 / 3) 100.0% Functions (1 / 1)
posix_signal.hpp
f(x) Functions (1)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_POSIX
17
18 #include <boost/corosio/detail/config.hpp>
19 #include <boost/corosio/signal_set.hpp>
20 #include <boost/corosio/detail/intrusive.hpp>
21 #include <boost/corosio/detail/scheduler_op.hpp>
22 #include <boost/capy/continuation.hpp>
23 #include <boost/capy/ex/executor_ref.hpp>
24
25 #include <coroutine>
26 #include <cstddef>
27 #include <optional>
28 #include <stop_token>
29 #include <system_error>
30
31 namespace boost::corosio {
32
33 namespace detail {
34
35 // Forward declarations
36 class posix_signal_service;
37
38 // Maximum signal number supported (NSIG is typically 64 on Linux)
39 enum
40 {
41 max_signal_number = 64
42 };
43
44 // signal_op - pending wait operation
45
46 struct signal_op : scheduler_op
47 {
48 std::coroutine_handle<> h;
49 capy::continuation cont;
50 capy::executor_ref d;
51 std::error_code* ec_out = nullptr;
52 int* signal_out = nullptr;
53 int signal_number = 0;
54 posix_signal_service* svc = nullptr; // For work_finished callback
55
56 void operator()() override;
57 void destroy() override;
58 };
59
60 // signal_registration - per-signal registration tracking
61
62 struct signal_registration
63 {
64 int signal_number = 0;
65 signal_set::flags_t flags = signal_set::none;
66 signal_set::implementation* owner = nullptr;
67 std::size_t undelivered = 0;
68 signal_registration* next_in_table = nullptr;
69 signal_registration* prev_in_table = nullptr;
70 signal_registration* next_in_set = nullptr;
71 };
72
73 // posix_signal - per-signal_set implementation
74
75 class posix_signal final
76 : public signal_set::implementation
77 , public intrusive_list<posix_signal>::node
78 {
79 friend class posix_signal_service;
80
81 posix_signal_service& svc_;
82 signal_registration* signals_ = nullptr;
83 signal_op pending_op_;
84 bool waiting_ = false;
85 bool cancelled_ = false;
86
87 /** Routes a stop request into the service's per-operation cancel.
88
89 Deliberately NOT `cancel_wait`: that sets the sticky `cancelled_`
90 latch, which belongs to `cancel()` and scopes to the whole set. A
91 stop token scopes to one wait, so a late fire must be a no-op
92 rather than poisoning the next wait.
93 */
94 struct token_canceller
95 {
96 posix_signal* self;
97 void operator()() const noexcept;
98 };
99
100 /** Armed for the duration of one wait; see wait().
101
102 Never reset while `posix_signal_service::mutex_` is held:
103 `~stop_callback` blocks until a concurrently running callback
104 returns, and that callback takes the same mutex.
105 */
106 std::optional<std::stop_callback<token_canceller>> stop_cb_;
107
108 /** Set when a stop request arrives for the current wait.
109
110 Closes a lost-wakeup race. The callback is armed before
111 `start_wait` takes the lock, so a request landing in that window
112 finds `waiting_ == false` and would otherwise return having done
113 nothing, leaving `start_wait` to park the wait forever. Every
114 other op survives this because `coro_op::on_cancel()` defaults to
115 `request_cancel()`, which sets a persistent flag the completion
116 decode reads later; this is that flag for the signal path.
117
118 Distinct from `cancelled_` on purpose: `cancelled_` is the sticky
119 per-set latch `cancel()` owns, this is per-operation.
120 */
121 bool token_cancelled_ = false;
122
123 public:
124 explicit posix_signal(posix_signal_service& svc) noexcept;
125
126 std::coroutine_handle<> wait(
127 std::coroutine_handle<>,
128 capy::executor_ref,
129 std::stop_token,
130 std::error_code*,
131 int*) override;
132
133 std::error_code add(int signal_number, signal_set::flags_t flags) override;
134 std::error_code remove(int signal_number) override;
135 std::error_code clear() override;
136 void cancel() noexcept override;
137
138 /// Disarm the wait's stop callback. Called before teardown.
139 184x void disarm_stop() noexcept
140 {
141 184x stop_cb_.reset();
142 184x }
143 };
144
145 } // namespace detail
146
147 } // namespace boost::corosio
148
149 #endif // BOOST_COROSIO_POSIX
150
151 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
152