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