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