TLA Line data 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 HIT 184 : void disarm_stop() noexcept
140 : {
141 184 : stop_cb_.reset();
142 184 : }
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
|