89.58% Lines (301/336) 96.88% Functions (31/32)
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_SERVICE_HPP 11   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_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/native/detail/posix/posix_signal.hpp> 18   #include <boost/corosio/native/detail/posix/posix_signal.hpp>
19   19  
20   #include <boost/corosio/detail/config.hpp> 20   #include <boost/corosio/detail/config.hpp>
21   #include <boost/capy/ex/execution_context.hpp> 21   #include <boost/capy/ex/execution_context.hpp>
22   #include <boost/corosio/detail/scheduler.hpp> 22   #include <boost/corosio/detail/scheduler.hpp>
23   #include <boost/capy/error.hpp> 23   #include <boost/capy/error.hpp>
24   24  
25   #include <mutex> 25   #include <mutex>
26   26  
27   #include <errno.h> 27   #include <errno.h>
28   #include <fcntl.h> 28   #include <fcntl.h>
29   #include <signal.h> 29   #include <signal.h>
30   #include <unistd.h> 30   #include <unistd.h>
31   31  
32   /* 32   /*
33   POSIX Signal Service 33   POSIX Signal Service
34   ==================== 34   ====================
35   35  
36   Concrete signal service implementation for POSIX backends. Manages signal 36   Concrete signal service implementation for POSIX backends. Manages signal
37   registrations via sigaction() and dispatches completions through the 37   registrations via sigaction() and dispatches completions through the
38   scheduler. One instance per execution_context, created by 38   scheduler. One instance per execution_context, created by
39   get_signal_service(). 39   get_signal_service().
40   40  
41   See the block comment further down for the full architecture overview. 41   See the block comment further down for the full architecture overview.
42   */ 42   */
43   43  
44   /* 44   /*
45   POSIX Signal Implementation 45   POSIX Signal Implementation
46   =========================== 46   ===========================
47   47  
48   This file implements signal handling for POSIX systems using sigaction(). 48   This file implements signal handling for POSIX systems using sigaction().
49   The implementation supports signal flags (SA_RESTART, etc.) and integrates 49   The implementation supports signal flags (SA_RESTART, etc.) and integrates
50   with any POSIX-compatible scheduler via the abstract scheduler interface. 50   with any POSIX-compatible scheduler via the abstract scheduler interface.
51   51  
52   Architecture Overview 52   Architecture Overview
53   --------------------- 53   ---------------------
54   54  
55   Three layers manage signal registrations: 55   Three layers manage signal registrations:
56   56  
57   1. signal_state (global singleton) 57   1. signal_state (global singleton)
58   - Tracks the global service list and per-signal registration counts 58   - Tracks the global service list and per-signal registration counts
59   - Stores the flags used for first registration of each signal (for 59   - Stores the flags used for first registration of each signal (for
60   conflict detection when multiple signal_sets register same signal) 60   conflict detection when multiple signal_sets register same signal)
61   - Owns the mutex that protects signal handler installation/removal 61   - Owns the mutex that protects signal handler installation/removal
62   62  
63   2. posix_signal_service (one per execution_context) 63   2. posix_signal_service (one per execution_context)
64   - Maintains registrations_[] table indexed by signal number 64   - Maintains registrations_[] table indexed by signal number
65   - Each slot is a doubly-linked list of signal_registrations for that signal 65   - Each slot is a doubly-linked list of signal_registrations for that signal
66   - Also maintains impl_list_ of all posix_signal objects it owns 66   - Also maintains impl_list_ of all posix_signal objects it owns
67   67  
68   3. posix_signal (one per signal_set) 68   3. posix_signal (one per signal_set)
69   - Owns a singly-linked list (sorted by signal number) of signal_registrations 69   - Owns a singly-linked list (sorted by signal number) of signal_registrations
70   - Contains the pending_op_ used for wait operations 70   - Contains the pending_op_ used for wait operations
71   71  
72   Signal Delivery Flow 72   Signal Delivery Flow
73   -------------------- 73   --------------------
74   74  
75   Delivery uses the self-pipe trick so the signal handler itself performs 75   Delivery uses the self-pipe trick so the signal handler itself performs
76   only async-signal-safe work (mirrors Boost.Asio): 76   only async-signal-safe work (mirrors Boost.Asio):
77   77  
78   1. Signal arrives -> corosio_posix_signal_handler(). The handler only 78   1. Signal arrives -> corosio_posix_signal_handler(). The handler only
79   write()s the signal number to the global self-pipe (write_fd) and 79   write()s the signal number to the global self-pipe (write_fd) and
80   restores errno. No locks, no allocation, no scheduler dispatch. 80   restores errno. No locks, no allocation, no scheduler dispatch.
81   81  
82   2. The read end of the pipe is watched by one backend's event loop 82   2. The read end of the pipe is watched by one backend's event loop
83   (registered via scheduler::register_signal_reader on the first 83   (registered via scheduler::register_signal_reader on the first
84   registration). When it becomes readable the backend drains it 84   registration). When it becomes readable the backend drains it
85   (drain_signal_pipe) and calls deliver_signal() in normal context. 85   (drain_signal_pipe) and calls deliver_signal() in normal context.
86   86  
87   3. deliver_signal() iterates all posix_signal_service services: 87   3. deliver_signal() iterates all posix_signal_service services:
88   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op 88   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op
89   to the scheduler for immediate completion 89   to the scheduler for immediate completion
90   - Otherwise, increment reg->undelivered to queue the signal 90   - Otherwise, increment reg->undelivered to queue the signal
91   91  
92   4. When wait() is called via start_wait(): 92   4. When wait() is called via start_wait():
93   - First check for queued signals (undelivered > 0); if found, post 93   - First check for queued signals (undelivered > 0); if found, post
94   immediate completion without blocking 94   immediate completion without blocking
95   - Otherwise, set waiting_ = true and call work_started() to keep 95   - Otherwise, set waiting_ = true and call work_started() to keep
96   the io_context alive 96   the io_context alive
97   97  
98   Locking Protocol 98   Locking Protocol
99   ---------------- 99   ----------------
100   100  
101   Two mutex levels exist (MUST acquire in this order to avoid deadlock): 101   Two mutex levels exist (MUST acquire in this order to avoid deadlock):
102   1. signal_state::mutex - protects handler registration and service list 102   1. signal_state::mutex - protects handler registration and service list
103   2. posix_signal_service::mutex_ - protects per-service registration tables 103   2. posix_signal_service::mutex_ - protects per-service registration tables
104   104  
105   Async-Signal-Safety 105   Async-Signal-Safety
106   ------------------- 106   -------------------
107   107  
108   The C signal handler (corosio_posix_signal_handler) performs only 108   The C signal handler (corosio_posix_signal_handler) performs only
109   async-signal-safe operations: it reads the single global write_fd and 109   async-signal-safe operations: it reads the single global write_fd and
110   calls write(), saving/restoring errno. It never locks a mutex, allocates 110   calls write(), saving/restoring errno. It never locks a mutex, allocates
111   memory, or dispatches through the scheduler. All of that happens in 111   memory, or dispatches through the scheduler. All of that happens in
112   deliver_signal(), which runs in normal thread context from the backend 112   deliver_signal(), which runs in normal thread context from the backend
113   event loop after draining the self-pipe. There is therefore no 113   event loop after draining the self-pipe. There is therefore no
114   self-deadlock risk if a signal arrives while a thread holds state->mutex 114   self-deadlock risk if a signal arrives while a thread holds state->mutex
115   or service->mutex_. 115   or service->mutex_.
116   116  
117   Flag Handling 117   Flag Handling
118   ------------- 118   -------------
119   119  
120   - Flags are abstract values in the public API (signal_set::flags_t) 120   - Flags are abstract values in the public API (signal_set::flags_t)
121   - flags_supported() validates that requested flags are available on 121   - flags_supported() validates that requested flags are available on
122   this platform; returns false if SA_NOCLDWAIT is unavailable and 122   this platform; returns false if SA_NOCLDWAIT is unavailable and
123   no_child_wait is requested 123   no_child_wait is requested
124   - to_sigaction_flags() maps validated flags to actual SA_* constants 124   - to_sigaction_flags() maps validated flags to actual SA_* constants
125   - First registration of a signal establishes the flags; subsequent 125   - First registration of a signal establishes the flags; subsequent
126   registrations must be compatible (same flags or dont_care) 126   registrations must be compatible (same flags or dont_care)
127   - Requesting unavailable flags returns operation_not_supported 127   - Requesting unavailable flags returns operation_not_supported
128   128  
129   Work Tracking 129   Work Tracking
130   ------------- 130   -------------
131   131  
132   When waiting for a signal: 132   When waiting for a signal:
133   - start_wait() calls sched_->work_started() to prevent io_context::run() 133   - start_wait() calls sched_->work_started() to prevent io_context::run()
134   from returning while we wait 134   from returning while we wait
135   - signal_op::svc is set to point to the service 135   - signal_op::svc is set to point to the service
136   - signal_op::operator()() calls work_finished() after resuming the coroutine 136   - signal_op::operator()() calls work_finished() after resuming the coroutine
137   137  
138   If a signal was already queued (undelivered > 0), no work tracking is needed 138   If a signal was already queued (undelivered > 0), no work tracking is needed
139   because completion is posted immediately. 139   because completion is posted immediately.
140   */ 140   */
141   141  
142   namespace boost::corosio { 142   namespace boost::corosio {
143   143  
144   namespace detail { 144   namespace detail {
145   145  
146   /** Signal service for POSIX backends. 146   /** Signal service for POSIX backends.
147   147  
148   Manages signal registrations via sigaction() and dispatches signal 148   Manages signal registrations via sigaction() and dispatches signal
149   completions through the scheduler. One instance per execution_context. 149   completions through the scheduler. One instance per execution_context.
150   */ 150   */
151   class BOOST_COROSIO_DECL posix_signal_service final 151   class BOOST_COROSIO_DECL posix_signal_service final
152   : public capy::execution_context::service 152   : public capy::execution_context::service
153   , public io_object::io_service 153   , public io_object::io_service
154   { 154   {
155   public: 155   public:
156   using key_type = posix_signal_service; 156   using key_type = posix_signal_service;
157   157  
158   posix_signal_service(capy::execution_context& ctx, scheduler& sched); 158   posix_signal_service(capy::execution_context& ctx, scheduler& sched);
159   ~posix_signal_service() override; 159   ~posix_signal_service() override;
160   160  
161   posix_signal_service(posix_signal_service const&) = delete; 161   posix_signal_service(posix_signal_service const&) = delete;
162   posix_signal_service& operator=(posix_signal_service const&) = delete; 162   posix_signal_service& operator=(posix_signal_service const&) = delete;
163   163  
164   io_object::implementation* construct() override; 164   io_object::implementation* construct() override;
165   165  
HITCBC 166   112 void destroy(io_object::implementation* p) override 166   112 void destroy(io_object::implementation* p) override
167   { 167   {
HITCBC 168   112 auto& impl = static_cast<posix_signal&>(*p); 168   112 auto& impl = static_cast<posix_signal&>(*p);
HITCBC 169   112 [[maybe_unused]] auto n = impl.clear(); 169   112 [[maybe_unused]] auto n = impl.clear();
HITCBC 170   112 impl.cancel(); 170   112 impl.cancel();
HITCBC 171   112 destroy_impl(impl); 171   112 destroy_impl(impl);
HITCBC 172   112 } 172   112 }
173   173  
174   void shutdown() override; 174   void shutdown() override;
175   175  
176   void destroy_impl(posix_signal& impl); 176   void destroy_impl(posix_signal& impl);
177   177  
178   std::error_code add_signal( 178   std::error_code add_signal(
179   posix_signal& impl, int signal_number, signal_set::flags_t flags); 179   posix_signal& impl, int signal_number, signal_set::flags_t flags);
180   180  
181   std::error_code remove_signal(posix_signal& impl, int signal_number); 181   std::error_code remove_signal(posix_signal& impl, int signal_number);
182   182  
183   std::error_code clear_signals(posix_signal& impl); 183   std::error_code clear_signals(posix_signal& impl);
184   184  
185   void cancel_wait(posix_signal& impl); 185   void cancel_wait(posix_signal& impl);
186   void start_wait(posix_signal& impl, signal_op* op); 186   void start_wait(posix_signal& impl, signal_op* op);
187   187  
188   static void deliver_signal(int signal_number); 188   static void deliver_signal(int signal_number);
189   189  
190   void work_started() noexcept; 190   void work_started() noexcept;
191   void work_finished() noexcept; 191   void work_finished() noexcept;
192   void post(signal_op* op); 192   void post(signal_op* op);
193   193  
194   private: 194   private:
195   static void add_service(posix_signal_service* service); 195   static void add_service(posix_signal_service* service);
196   static void remove_service(posix_signal_service* service); 196   static void remove_service(posix_signal_service* service);
197   197  
198   scheduler* sched_; 198   scheduler* sched_;
199   std::mutex mutex_; 199   std::mutex mutex_;
200   200  
201   // Registers the signal self-pipe's read end with sched_ exactly once per 201   // Registers the signal self-pipe's read end with sched_ exactly once per
202   // service, so every io_context that waits on a signal can drain the pipe. 202   // service, so every io_context that waits on a signal can drain the pipe.
203   // A once_flag (not a bool under mutex_) because registration must run 203   // A once_flag (not a bool under mutex_) because registration must run
204   // without holding mutex_ or the signal-state mutex — see add_signal. 204   // without holding mutex_ or the signal-state mutex — see add_signal.
205   std::once_flag reader_once_; 205   std::once_flag reader_once_;
206   206  
207   intrusive_list<posix_signal> impl_list_; 207   intrusive_list<posix_signal> impl_list_;
208   208  
209   // Per-signal registration table 209   // Per-signal registration table
210   signal_registration* registrations_[max_signal_number]; 210   signal_registration* registrations_[max_signal_number];
211   211  
212   // Registration counts for each signal 212   // Registration counts for each signal
213   std::size_t registration_count_[max_signal_number]; 213   std::size_t registration_count_[max_signal_number];
214   214  
215   // Linked list of all posix_signal_service services for signal delivery 215   // Linked list of all posix_signal_service services for signal delivery
216   posix_signal_service* next_ = nullptr; 216   posix_signal_service* next_ = nullptr;
217   posix_signal_service* prev_ = nullptr; 217   posix_signal_service* prev_ = nullptr;
218   }; 218   };
219   219  
220   /** Get or create the signal service for the given context. 220   /** Get or create the signal service for the given context.
221   221  
222   This function is called by the concrete scheduler during initialization 222   This function is called by the concrete scheduler during initialization
223   to create the signal service with a reference to itself. 223   to create the signal service with a reference to itself.
224   224  
225   @param ctx Reference to the owning execution_context. 225   @param ctx Reference to the owning execution_context.
226   @param sched Reference to the scheduler for posting completions. 226   @param sched Reference to the scheduler for posting completions.
227   @return Reference to the signal service. 227   @return Reference to the signal service.
228   */ 228   */
229   posix_signal_service& 229   posix_signal_service&
230   get_signal_service(capy::execution_context& ctx, scheduler& sched); 230   get_signal_service(capy::execution_context& ctx, scheduler& sched);
231   231  
232   } // namespace detail 232   } // namespace detail
233   233  
234   } // namespace boost::corosio 234   } // namespace boost::corosio
235   235  
236   // --------------------------------------------------------------------------- 236   // ---------------------------------------------------------------------------
237   // Inline implementation 237   // Inline implementation
238   // --------------------------------------------------------------------------- 238   // ---------------------------------------------------------------------------
239   239  
240   namespace boost::corosio { 240   namespace boost::corosio {
241   241  
242   namespace detail { 242   namespace detail {
243   243  
244   namespace posix_signal_detail { 244   namespace posix_signal_detail {
245   245  
246   struct signal_state 246   struct signal_state
247   { 247   {
248   std::mutex mutex; 248   std::mutex mutex;
249   posix_signal_service* service_list = nullptr; 249   posix_signal_service* service_list = nullptr;
250   std::size_t registration_count[max_signal_number] = {}; 250   std::size_t registration_count[max_signal_number] = {};
251   signal_set::flags_t registered_flags[max_signal_number] = {}; 251   signal_set::flags_t registered_flags[max_signal_number] = {};
252   252  
253   // Self-pipe used to defer signal delivery out of handler context. 253   // Self-pipe used to defer signal delivery out of handler context.
254   // The C handler writes the signal number to write_fd (async-signal- 254   // The C handler writes the signal number to write_fd (async-signal-
255   // safe); a backend event loop drains read_fd and calls deliver_signal() 255   // safe); a backend event loop drains read_fd and calls deliver_signal()
256   // in normal context. Created once (on the first signal registration) and 256   // in normal context. Created once (on the first signal registration) and
257   // kept for the process lifetime. Each posix_signal_service registers the 257   // kept for the process lifetime. Each posix_signal_service registers the
258   // read end with its own scheduler (see reader_once_) so every running 258   // read end with its own scheduler (see reader_once_) so every running
259   // io_context can drain it; multiple readers on one pipe are safe because 259   // io_context can drain it; multiple readers on one pipe are safe because
260   // each signal is a fixed sizeof(int) record read atomically. 260   // each signal is a fixed sizeof(int) record read atomically.
261   int read_fd = -1; 261   int read_fd = -1;
262   int write_fd = -1; 262   int write_fd = -1;
263   }; 263   };
264   264  
265   BOOST_COROSIO_DECL signal_state* get_signal_state(); 265   BOOST_COROSIO_DECL signal_state* get_signal_state();
266   266  
267   // Check if requested flags are supported on this platform. 267   // Check if requested flags are supported on this platform.
268   // Returns true if all flags are supported, false otherwise. 268   // Returns true if all flags are supported, false otherwise.
269   inline bool 269   inline bool
HITCBC 270   114 flags_supported([[maybe_unused]] signal_set::flags_t flags) 270   114 flags_supported([[maybe_unused]] signal_set::flags_t flags)
271   { 271   {
272   #ifndef SA_NOCLDWAIT 272   #ifndef SA_NOCLDWAIT
273   if (flags & signal_set::no_child_wait) 273   if (flags & signal_set::no_child_wait)
274   return false; 274   return false;
275   #endif 275   #endif
HITCBC 276   114 return true; 276   114 return true;
277   } 277   }
278   278  
279   // Map abstract flags to sigaction() flags. 279   // Map abstract flags to sigaction() flags.
280   // Caller must ensure flags_supported() returns true first. 280   // Caller must ensure flags_supported() returns true first.
281   inline int 281   inline int
HITCBC 282   96 to_sigaction_flags(signal_set::flags_t flags) 282   96 to_sigaction_flags(signal_set::flags_t flags)
283   { 283   {
HITCBC 284   96 int sa_flags = 0; 284   96 int sa_flags = 0;
HITCBC 285   96 if (flags & signal_set::restart) 285   96 if (flags & signal_set::restart)
HITCBC 286   18 sa_flags |= SA_RESTART; 286   18 sa_flags |= SA_RESTART;
HITCBC 287   96 if (flags & signal_set::no_child_stop) 287   96 if (flags & signal_set::no_child_stop)
MISUBC 288   sa_flags |= SA_NOCLDSTOP; 288   sa_flags |= SA_NOCLDSTOP;
289   #ifdef SA_NOCLDWAIT 289   #ifdef SA_NOCLDWAIT
HITCBC 290   96 if (flags & signal_set::no_child_wait) 290   96 if (flags & signal_set::no_child_wait)
MISUBC 291   sa_flags |= SA_NOCLDWAIT; 291   sa_flags |= SA_NOCLDWAIT;
292   #endif 292   #endif
HITCBC 293   96 if (flags & signal_set::no_defer) 293   96 if (flags & signal_set::no_defer)
HITCBC 294   2 sa_flags |= SA_NODEFER; 294   2 sa_flags |= SA_NODEFER;
HITCBC 295   96 if (flags & signal_set::reset_handler) 295   96 if (flags & signal_set::reset_handler)
MISUBC 296   sa_flags |= SA_RESETHAND; 296   sa_flags |= SA_RESETHAND;
HITCBC 297   96 return sa_flags; 297   96 return sa_flags;
298   } 298   }
299   299  
300   // Check if two flag values are compatible 300   // Check if two flag values are compatible
301   inline bool 301   inline bool
HITCBC 302   18 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested) 302   18 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
303   { 303   {
304   // dont_care is always compatible 304   // dont_care is always compatible
HITCBC 305   34 if ((existing & signal_set::dont_care) || 305   34 if ((existing & signal_set::dont_care) ||
HITCBC 306   16 (requested & signal_set::dont_care)) 306   16 (requested & signal_set::dont_care))
HITCBC 307   6 return true; 307   6 return true;
308   308  
309   // Mask out dont_care bit for comparison 309   // Mask out dont_care bit for comparison
HITCBC 310   12 constexpr auto mask = ~signal_set::dont_care; 310   12 constexpr auto mask = ~signal_set::dont_care;
HITCBC 311   12 return (existing & mask) == (requested & mask); 311   12 return (existing & mask) == (requested & mask);
312   } 312   }
313   313  
314   // Lazily create the global signal self-pipe. Idempotent; call under 314   // Lazily create the global signal self-pipe. Idempotent; call under
315   // state->mutex before installing the first signal handler so write_fd is 315   // state->mutex before installing the first signal handler so write_fd is
316   // valid by the time the handler can fire. Both ends are non-blocking and 316   // valid by the time the handler can fire. Both ends are non-blocking and
317   // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler). 317   // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler).
318   // Returns false and leaves the fds at -1 if creation fails. 318   // Returns false and leaves the fds at -1 if creation fails.
319   inline bool 319   inline bool
HITCBC 320   114 open_signal_pipe(signal_state* state) 320   114 open_signal_pipe(signal_state* state)
321   { 321   {
HITCBC 322   114 if (state->read_fd >= 0) 322   114 if (state->read_fd >= 0)
HITCBC 323   112 return true; 323   112 return true;
324   324  
325   int fds[2]; 325   int fds[2];
HITCBC 326   2 if (::pipe(fds) < 0) 326   2 if (::pipe(fds) < 0)
MISUBC 327   return false; 327   return false;
328   328  
HITCBC 329   6 for (int i = 0; i < 2; ++i) 329   6 for (int i = 0; i < 2; ++i)
330   { 330   {
HITCBC 331   4 int fl = ::fcntl(fds[i], F_GETFL, 0); 331   4 int fl = ::fcntl(fds[i], F_GETFL, 0);
HITCBC 332   8 if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 || 332   8 if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 ||
HITCBC 333   4 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) 333   4 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1)
334   { 334   {
MISUBC 335   ::close(fds[0]); 335   ::close(fds[0]);
MISUBC 336   ::close(fds[1]); 336   ::close(fds[1]);
MISUBC 337   return false; 337   return false;
338   } 338   }
339   } 339   }
340   340  
HITCBC 341   2 state->read_fd = fds[0]; 341   2 state->read_fd = fds[0];
HITCBC 342   2 state->write_fd = fds[1]; 342   2 state->write_fd = fds[1];
HITCBC 343   2 return true; 343   2 return true;
344   } 344   }
345   345  
346   // C signal handler. Async-signal-safe: it touches only the single global 346   // C signal handler. Async-signal-safe: it touches only the single global
347   // write_fd (an int set before any handler is installed) and calls write(), 347   // write_fd (an int set before any handler is installed) and calls write(),
348   // which POSIX lists as async-signal-safe. errno is saved and restored so an 348   // which POSIX lists as async-signal-safe. errno is saved and restored so an
349   // interrupted foreground syscall is unaffected. A full pipe (write returns 349   // interrupted foreground syscall is unaffected. A full pipe (write returns
350   // EAGAIN) or a short write is intentionally dropped — the reactor still 350   // EAGAIN) or a short write is intentionally dropped — the reactor still
351   // coalesces because deliver_signal reports the signal to every waiting set. 351   // coalesces because deliver_signal reports the signal to every waiting set.
352   inline void 352   inline void
HITCBC 353   300 corosio_posix_signal_handler(int signal_number) 353   300 corosio_posix_signal_handler(int signal_number)
354   { 354   {
HITCBC 355   300 int saved_errno = errno; 355   300 int saved_errno = errno;
HITCBC 356   300 signal_state* state = get_signal_state(); 356   300 signal_state* state = get_signal_state();
357   [[maybe_unused]] ssize_t r = 357   [[maybe_unused]] ssize_t r =
HITCBC 358   300 ::write(state->write_fd, &signal_number, sizeof(int)); 358   300 ::write(state->write_fd, &signal_number, sizeof(int));
HITCBC 359   300 errno = saved_errno; 359   300 errno = saved_errno;
360   // With sigaction(), the handler persists automatically (unlike some 360   // With sigaction(), the handler persists automatically (unlike some
361   // signal() implementations that reset to SIG_DFL). 361   // signal() implementations that reset to SIG_DFL).
HITCBC 362   300 } 362   300 }
363   363  
364   // Drain the signal self-pipe and deliver each pending signal. Runs in normal 364   // Drain the signal self-pipe and deliver each pending signal. Runs in normal
365   // thread context from the backend event loop, so deliver_signal()'s mutex 365   // thread context from the backend event loop, so deliver_signal()'s mutex
366   // locking and scheduler post are safe here. Reads until EAGAIN (edge- 366   // locking and scheduler post are safe here. Reads until EAGAIN (edge-
367   // triggered backends require a full drain per readiness event). 367   // triggered backends require a full drain per readiness event).
368   inline void 368   inline void
HITCBC 369   300 drain_signal_pipe() 369   300 drain_signal_pipe()
370   { 370   {
HITCBC 371   300 signal_state* state = get_signal_state(); 371   300 signal_state* state = get_signal_state();
372   int signal_number; 372   int signal_number;
HITCBC 373   600 while (::read(state->read_fd, &signal_number, sizeof(int)) == 373   600 while (::read(state->read_fd, &signal_number, sizeof(int)) ==
374   static_cast<ssize_t>(sizeof(int))) 374   static_cast<ssize_t>(sizeof(int)))
375   { 375   {
HITCBC 376   300 posix_signal_service::deliver_signal(signal_number); 376   300 posix_signal_service::deliver_signal(signal_number);
377   } 377   }
HITCBC 378   300 } 378   300 }
379   379  
380   } // namespace posix_signal_detail 380   } // namespace posix_signal_detail
381   381  
382   // signal_op implementation 382   // signal_op implementation
383   383  
384   inline void 384   inline void
HITCBC 385   302 signal_op::operator()() 385   302 signal_op::operator()()
386   { 386   {
HITCBC 387   302 if (ec_out) 387   302 if (ec_out)
HITCBC 388   302 *ec_out = {}; 388   302 *ec_out = {};
HITCBC 389   302 if (signal_out) 389   302 if (signal_out)
HITCBC 390   302 *signal_out = signal_number; 390   302 *signal_out = signal_number;
391   391  
392   // Capture svc before resuming (coro may destroy us) 392   // Capture svc before resuming (coro may destroy us)
HITCBC 393   302 auto* service = svc; 393   302 auto* service = svc;
HITCBC 394   302 svc = nullptr; 394   302 svc = nullptr;
395   395  
HITCBC 396   302 cont.h = h; 396   302 cont.h = h;
HITCBC 397   302 d.post(cont); 397   302 d.post(cont);
398   398  
399   // Balance the work_started() from start_wait 399   // Balance the work_started() from start_wait
HITCBC 400   302 if (service) 400   302 if (service)
HITCBC 401   302 service->work_finished(); 401   302 service->work_finished();
HITCBC 402   302 } 402   302 }
403   403  
404   inline void 404   inline void
MISUBC 405   signal_op::destroy() 405   signal_op::destroy()
406   { 406   {
407   // No-op: signal_op is embedded in posix_signal 407   // No-op: signal_op is embedded in posix_signal
MISUBC 408   } 408   }
409   409  
410   // posix_signal implementation 410   // posix_signal implementation
411   411  
HITCBC 412   112 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept 412   112 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
HITCBC 413   112 : svc_(svc) 413   112 : svc_(svc)
414   { 414   {
HITCBC 415   112 } 415   112 }
416   416  
417   inline std::coroutine_handle<> 417   inline std::coroutine_handle<>
HITCBC 418   310 posix_signal::wait( 418   310 posix_signal::wait(
419   std::coroutine_handle<> h, 419   std::coroutine_handle<> h,
420   capy::executor_ref d, 420   capy::executor_ref d,
421   std::stop_token token, 421   std::stop_token token,
422   std::error_code* ec, 422   std::error_code* ec,
423   int* signal_out) 423   int* signal_out)
424   { 424   {
HITCBC 425   310 pending_op_.h = h; 425   310 pending_op_.h = h;
HITCBC 426   310 pending_op_.d = d; 426   310 pending_op_.d = d;
HITCBC 427   310 pending_op_.ec_out = ec; 427   310 pending_op_.ec_out = ec;
HITCBC 428   310 pending_op_.signal_out = signal_out; 428   310 pending_op_.signal_out = signal_out;
HITCBC 429   310 pending_op_.signal_number = 0; 429   310 pending_op_.signal_number = 0;
430   430  
HITCBC 431   310 if (token.stop_requested()) 431   310 if (token.stop_requested())
432   { 432   {
HITCBC 433   2 if (ec) 433   2 if (ec)
HITCBC 434   2 *ec = make_error_code(capy::error::canceled); 434   2 *ec = make_error_code(capy::error::canceled);
HITCBC 435   2 if (signal_out) 435   2 if (signal_out)
HITCBC 436   2 *signal_out = 0; 436   2 *signal_out = 0;
HITCBC 437   2 pending_op_.cont.h = h; 437   2 pending_op_.cont.h = h;
HITCBC 438   2 d.post(pending_op_.cont); 438   2 d.post(pending_op_.cont);
439   // completion is always posted to scheduler queue, never inline. 439   // completion is always posted to scheduler queue, never inline.
HITCBC 440   2 return std::noop_coroutine(); 440   2 return std::noop_coroutine();
441   } 441   }
442   442  
HITCBC 443   308 svc_.start_wait(*this, &pending_op_); 443   308 svc_.start_wait(*this, &pending_op_);
444   // completion is always posted to scheduler queue, never inline. 444   // completion is always posted to scheduler queue, never inline.
HITCBC 445   308 return std::noop_coroutine(); 445   308 return std::noop_coroutine();
446   } 446   }
447   447  
448   inline std::error_code 448   inline std::error_code
HITCBC 449   118 posix_signal::add(int signal_number, signal_set::flags_t flags) 449   118 posix_signal::add(int signal_number, signal_set::flags_t flags)
450   { 450   {
HITCBC 451   118 return svc_.add_signal(*this, signal_number, flags); 451   118 return svc_.add_signal(*this, signal_number, flags);
452   } 452   }
453   453  
454   inline std::error_code 454   inline std::error_code
HITCBC 455   6 posix_signal::remove(int signal_number) 455   6 posix_signal::remove(int signal_number)
456   { 456   {
HITCBC 457   6 return svc_.remove_signal(*this, signal_number); 457   6 return svc_.remove_signal(*this, signal_number);
458   } 458   }
459   459  
460   inline std::error_code 460   inline std::error_code
HITCBC 461   116 posix_signal::clear() 461   116 posix_signal::clear()
462   { 462   {
HITCBC 463   116 return svc_.clear_signals(*this); 463   116 return svc_.clear_signals(*this);
464   } 464   }
465   465  
466   inline void 466   inline void
HITCBC 467   126 posix_signal::cancel() 467   126 posix_signal::cancel()
468   { 468   {
HITCBC 469   126 svc_.cancel_wait(*this); 469   126 svc_.cancel_wait(*this);
HITCBC 470   126 } 470   126 }
471   471  
472   // posix_signal_service implementation 472   // posix_signal_service implementation
473   473  
HITCBC 474   1217 inline posix_signal_service::posix_signal_service( 474   1225 inline posix_signal_service::posix_signal_service(
HITCBC 475   1217 capy::execution_context&, scheduler& sched) 475   1225 capy::execution_context&, scheduler& sched)
HITCBC 476   1217 : sched_(&sched) 476   1225 : sched_(&sched)
477   { 477   {
HITCBC 478   79105 for (int i = 0; i < max_signal_number; ++i) 478   79625 for (int i = 0; i < max_signal_number; ++i)
479   { 479   {
HITCBC 480   77888 registrations_[i] = nullptr; 480   78400 registrations_[i] = nullptr;
HITCBC 481   77888 registration_count_[i] = 0; 481   78400 registration_count_[i] = 0;
482   } 482   }
HITCBC 483   1217 add_service(this); 483   1225 add_service(this);
HITCBC 484   1217 } 484   1225 }
485   485  
HITCBC 486   2434 inline posix_signal_service::~posix_signal_service() 486   2450 inline posix_signal_service::~posix_signal_service()
487   { 487   {
HITCBC 488   1217 remove_service(this); 488   1225 remove_service(this);
HITCBC 489   2434 } 489   2450 }
490   490  
491   inline void 491   inline void
HITCBC 492   1217 posix_signal_service::shutdown() 492   1225 posix_signal_service::shutdown()
493   { 493   {
HITCBC 494   1217 std::lock_guard lock(mutex_); 494   1225 std::lock_guard lock(mutex_);
495   495  
HITCBC 496   1217 for (auto* impl = impl_list_.pop_front(); impl != nullptr; 496   1225 for (auto* impl = impl_list_.pop_front(); impl != nullptr;
MISUBC 497   impl = impl_list_.pop_front()) 497   impl = impl_list_.pop_front())
498   { 498   {
MISUBC 499   while (auto* reg = impl->signals_) 499   while (auto* reg = impl->signals_)
500   { 500   {
MISUBC 501   impl->signals_ = reg->next_in_set; 501   impl->signals_ = reg->next_in_set;
MISUBC 502   delete reg; 502   delete reg;
MISUBC 503   } 503   }
MISUBC 504   delete impl; 504   delete impl;
505   } 505   }
HITCBC 506   1217 } 506   1225 }
507   507  
508   inline io_object::implementation* 508   inline io_object::implementation*
HITCBC 509   112 posix_signal_service::construct() 509   112 posix_signal_service::construct()
510   { 510   {
HITCBC 511   112 auto* impl = new posix_signal(*this); 511   112 auto* impl = new posix_signal(*this);
512   512  
513   { 513   {
HITCBC 514   112 std::lock_guard lock(mutex_); 514   112 std::lock_guard lock(mutex_);
HITCBC 515   112 impl_list_.push_back(impl); 515   112 impl_list_.push_back(impl);
HITCBC 516   112 } 516   112 }
517   517  
HITCBC 518   112 return impl; 518   112 return impl;
519   } 519   }
520   520  
521   inline void 521   inline void
HITCBC 522   112 posix_signal_service::destroy_impl(posix_signal& impl) 522   112 posix_signal_service::destroy_impl(posix_signal& impl)
523   { 523   {
524   { 524   {
HITCBC 525   112 std::lock_guard lock(mutex_); 525   112 std::lock_guard lock(mutex_);
HITCBC 526   112 impl_list_.remove(&impl); 526   112 impl_list_.remove(&impl);
HITCBC 527   112 } 527   112 }
528   528  
HITCBC 529   112 delete &impl; 529   112 delete &impl;
HITCBC 530   112 } 530   112 }
531   531  
532   inline std::error_code 532   inline std::error_code
HITCBC 533   118 posix_signal_service::add_signal( 533   118 posix_signal_service::add_signal(
534   posix_signal& impl, int signal_number, signal_set::flags_t flags) 534   posix_signal& impl, int signal_number, signal_set::flags_t flags)
535   { 535   {
HITCBC 536   118 if (signal_number < 0 || signal_number >= max_signal_number) 536   118 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 537   4 return make_error_code(std::errc::invalid_argument); 537   4 return make_error_code(std::errc::invalid_argument);
538   538  
539   // Validate that requested flags are supported on this platform 539   // Validate that requested flags are supported on this platform
540   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems) 540   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
HITCBC 541   114 if (!posix_signal_detail::flags_supported(flags)) 541   114 if (!posix_signal_detail::flags_supported(flags))
MISUBC 542   return make_error_code(std::errc::operation_not_supported); 542   return make_error_code(std::errc::operation_not_supported);
543   543  
544   posix_signal_detail::signal_state* state = 544   posix_signal_detail::signal_state* state =
HITCBC 545   114 posix_signal_detail::get_signal_state(); 545   114 posix_signal_detail::get_signal_state();
546   546  
547   // Ensure the global self-pipe exists and this service's scheduler is 547   // Ensure the global self-pipe exists and this service's scheduler is
548   // watching its read end, BEFORE taking the registration locks. The 548   // watching its read end, BEFORE taking the registration locks. The
549   // reactor drain path locks the descriptor mutex and then the signal-state 549   // reactor drain path locks the descriptor mutex and then the signal-state
550   // and service mutexes; register_signal_reader locks the descriptor mutex 550   // and service mutexes; register_signal_reader locks the descriptor mutex
551   // (via register_descriptor), so it must run holding neither of those or 551   // (via register_descriptor), so it must run holding neither of those or
552   // the lock order would invert (a real deadlock, caught by TSan). call_once 552   // the lock order would invert (a real deadlock, caught by TSan). call_once
553   // makes the once-per-service registration safe when two signal_sets on 553   // makes the once-per-service registration safe when two signal_sets on
554   // this context race add() from different threads. 554   // this context race add() from different threads.
555   { 555   {
HITCBC 556   114 std::lock_guard state_lock(state->mutex); 556   114 std::lock_guard state_lock(state->mutex);
HITCBC 557   114 if (!posix_signal_detail::open_signal_pipe(state)) 557   114 if (!posix_signal_detail::open_signal_pipe(state))
MISUBC 558   return make_error_code(std::errc::io_error); 558   return make_error_code(std::errc::io_error);
HITCBC 559   114 } 559   114 }
HITCBC 560   114 std::call_once(reader_once_, [this, state] { 560   114 std::call_once(reader_once_, [this, state] {
HITCBC 561   82 sched_->register_signal_reader(state->read_fd); 561   82 sched_->register_signal_reader(state->read_fd);
HITCBC 562   82 }); 562   82 });
563   563  
HITCBC 564   114 std::lock_guard state_lock(state->mutex); 564   114 std::lock_guard state_lock(state->mutex);
HITCBC 565   114 std::lock_guard lock(mutex_); 565   114 std::lock_guard lock(mutex_);
566   566  
567   // Find insertion point (list is sorted by signal number) 567   // Find insertion point (list is sorted by signal number)
HITCBC 568   114 signal_registration** insertion_point = &impl.signals_; 568   114 signal_registration** insertion_point = &impl.signals_;
HITCBC 569   114 signal_registration* reg = impl.signals_; 569   114 signal_registration* reg = impl.signals_;
HITCBC 570   128 while (reg && reg->signal_number < signal_number) 570   128 while (reg && reg->signal_number < signal_number)
571   { 571   {
HITCBC 572   14 insertion_point = &reg->next_in_set; 572   14 insertion_point = &reg->next_in_set;
HITCBC 573   14 reg = reg->next_in_set; 573   14 reg = reg->next_in_set;
574   } 574   }
575   575  
576   // Already registered in this set - check flag compatibility 576   // Already registered in this set - check flag compatibility
577   // (same signal_set adding same signal twice with different flags) 577   // (same signal_set adding same signal twice with different flags)
HITCBC 578   114 if (reg && reg->signal_number == signal_number) 578   114 if (reg && reg->signal_number == signal_number)
579   { 579   {
HITCBC 580   10 if (!posix_signal_detail::flags_compatible(reg->flags, flags)) 580   10 if (!posix_signal_detail::flags_compatible(reg->flags, flags))
HITCBC 581   2 return make_error_code(std::errc::invalid_argument); 581   2 return make_error_code(std::errc::invalid_argument);
HITCBC 582   8 return {}; 582   8 return {};
583   } 583   }
584   584  
585   // Check flag compatibility with global registration 585   // Check flag compatibility with global registration
586   // (different signal_set already registered this signal with different flags) 586   // (different signal_set already registered this signal with different flags)
HITCBC 587   104 if (state->registration_count[signal_number] > 0) 587   104 if (state->registration_count[signal_number] > 0)
588   { 588   {
HITCBC 589   8 if (!posix_signal_detail::flags_compatible( 589   8 if (!posix_signal_detail::flags_compatible(
590   state->registered_flags[signal_number], flags)) 590   state->registered_flags[signal_number], flags))
HITCBC 591   2 return make_error_code(std::errc::invalid_argument); 591   2 return make_error_code(std::errc::invalid_argument);
592   } 592   }
593   593  
HITCBC 594   102 auto* new_reg = new signal_registration; 594   102 auto* new_reg = new signal_registration;
HITCBC 595   102 new_reg->signal_number = signal_number; 595   102 new_reg->signal_number = signal_number;
HITCBC 596   102 new_reg->flags = flags; 596   102 new_reg->flags = flags;
HITCBC 597   102 new_reg->owner = &impl; 597   102 new_reg->owner = &impl;
HITCBC 598   102 new_reg->undelivered = 0; 598   102 new_reg->undelivered = 0;
599   599  
600   // Install signal handler on first global registration 600   // Install signal handler on first global registration
HITCBC 601   102 if (state->registration_count[signal_number] == 0) 601   102 if (state->registration_count[signal_number] == 0)
602   { 602   {
HITCBC 603   96 struct sigaction sa = {}; 603   96 struct sigaction sa = {};
HITCBC 604   96 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; 604   96 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
HITCBC 605   96 sigemptyset(&sa.sa_mask); 605   96 sigemptyset(&sa.sa_mask);
HITCBC 606   96 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); 606   96 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
607   607  
HITCBC 608   96 if (::sigaction(signal_number, &sa, nullptr) < 0) 608   96 if (::sigaction(signal_number, &sa, nullptr) < 0)
609   { 609   {
MISUBC 610   delete new_reg; 610   delete new_reg;
MISUBC 611   return make_error_code(std::errc::invalid_argument); 611   return make_error_code(std::errc::invalid_argument);
612   } 612   }
613   613  
614   // Store the flags used for first registration 614   // Store the flags used for first registration
HITCBC 615   96 state->registered_flags[signal_number] = flags; 615   96 state->registered_flags[signal_number] = flags;
616   } 616   }
617   617  
HITCBC 618   102 new_reg->next_in_set = reg; 618   102 new_reg->next_in_set = reg;
HITCBC 619   102 *insertion_point = new_reg; 619   102 *insertion_point = new_reg;
620   620  
HITCBC 621   102 new_reg->next_in_table = registrations_[signal_number]; 621   102 new_reg->next_in_table = registrations_[signal_number];
HITCBC 622   102 new_reg->prev_in_table = nullptr; 622   102 new_reg->prev_in_table = nullptr;
HITCBC 623   102 if (registrations_[signal_number]) 623   102 if (registrations_[signal_number])
HITCBC 624   6 registrations_[signal_number]->prev_in_table = new_reg; 624   6 registrations_[signal_number]->prev_in_table = new_reg;
HITCBC 625   102 registrations_[signal_number] = new_reg; 625   102 registrations_[signal_number] = new_reg;
626   626  
HITCBC 627   102 ++state->registration_count[signal_number]; 627   102 ++state->registration_count[signal_number];
HITCBC 628   102 ++registration_count_[signal_number]; 628   102 ++registration_count_[signal_number];
629   629  
HITCBC 630   102 return {}; 630   102 return {};
HITCBC 631   114 } 631   114 }
632   632  
633   inline std::error_code 633   inline std::error_code
HITCBC 634   6 posix_signal_service::remove_signal(posix_signal& impl, int signal_number) 634   6 posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
635   { 635   {
HITCBC 636   6 if (signal_number < 0 || signal_number >= max_signal_number) 636   6 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 637   2 return make_error_code(std::errc::invalid_argument); 637   2 return make_error_code(std::errc::invalid_argument);
638   638  
639   posix_signal_detail::signal_state* state = 639   posix_signal_detail::signal_state* state =
HITCBC 640   4 posix_signal_detail::get_signal_state(); 640   4 posix_signal_detail::get_signal_state();
HITCBC 641   4 std::lock_guard state_lock(state->mutex); 641   4 std::lock_guard state_lock(state->mutex);
HITCBC 642   4 std::lock_guard lock(mutex_); 642   4 std::lock_guard lock(mutex_);
643   643  
HITCBC 644   4 signal_registration** deletion_point = &impl.signals_; 644   4 signal_registration** deletion_point = &impl.signals_;
HITCBC 645   4 signal_registration* reg = impl.signals_; 645   4 signal_registration* reg = impl.signals_;
HITCBC 646   4 while (reg && reg->signal_number < signal_number) 646   4 while (reg && reg->signal_number < signal_number)
647   { 647   {
MISUBC 648   deletion_point = &reg->next_in_set; 648   deletion_point = &reg->next_in_set;
MISUBC 649   reg = reg->next_in_set; 649   reg = reg->next_in_set;
650   } 650   }
651   651  
HITCBC 652   4 if (!reg || reg->signal_number != signal_number) 652   4 if (!reg || reg->signal_number != signal_number)
HITCBC 653   2 return {}; 653   2 return {};
654   654  
655   // Restore default handler on last global unregistration 655   // Restore default handler on last global unregistration
HITCBC 656   2 if (state->registration_count[signal_number] == 1) 656   2 if (state->registration_count[signal_number] == 1)
657   { 657   {
HITCBC 658   2 struct sigaction sa = {}; 658   2 struct sigaction sa = {};
HITCBC 659   2 sa.sa_handler = SIG_DFL; 659   2 sa.sa_handler = SIG_DFL;
HITCBC 660   2 sigemptyset(&sa.sa_mask); 660   2 sigemptyset(&sa.sa_mask);
HITCBC 661   2 sa.sa_flags = 0; 661   2 sa.sa_flags = 0;
662   662  
HITCBC 663   2 if (::sigaction(signal_number, &sa, nullptr) < 0) 663   2 if (::sigaction(signal_number, &sa, nullptr) < 0)
MISUBC 664   return make_error_code(std::errc::invalid_argument); 664   return make_error_code(std::errc::invalid_argument);
665   665  
666   // Clear stored flags 666   // Clear stored flags
HITCBC 667   2 state->registered_flags[signal_number] = signal_set::none; 667   2 state->registered_flags[signal_number] = signal_set::none;
668   } 668   }
669   669  
HITCBC 670   2 *deletion_point = reg->next_in_set; 670   2 *deletion_point = reg->next_in_set;
671   671  
HITCBC 672   2 if (registrations_[signal_number] == reg) 672   2 if (registrations_[signal_number] == reg)
HITCBC 673   2 registrations_[signal_number] = reg->next_in_table; 673   2 registrations_[signal_number] = reg->next_in_table;
HITCBC 674   2 if (reg->prev_in_table) 674   2 if (reg->prev_in_table)
MISUBC 675   reg->prev_in_table->next_in_table = reg->next_in_table; 675   reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 676   2 if (reg->next_in_table) 676   2 if (reg->next_in_table)
MISUBC 677   reg->next_in_table->prev_in_table = reg->prev_in_table; 677   reg->next_in_table->prev_in_table = reg->prev_in_table;
678   678  
HITCBC 679   2 --state->registration_count[signal_number]; 679   2 --state->registration_count[signal_number];
HITCBC 680   2 --registration_count_[signal_number]; 680   2 --registration_count_[signal_number];
681   681  
HITCBC 682   2 delete reg; 682   2 delete reg;
HITCBC 683   2 return {}; 683   2 return {};
HITCBC 684   4 } 684   4 }
685   685  
686   inline std::error_code 686   inline std::error_code
HITCBC 687   116 posix_signal_service::clear_signals(posix_signal& impl) 687   116 posix_signal_service::clear_signals(posix_signal& impl)
688   { 688   {
689   posix_signal_detail::signal_state* state = 689   posix_signal_detail::signal_state* state =
HITCBC 690   116 posix_signal_detail::get_signal_state(); 690   116 posix_signal_detail::get_signal_state();
HITCBC 691   116 std::lock_guard state_lock(state->mutex); 691   116 std::lock_guard state_lock(state->mutex);
HITCBC 692   116 std::lock_guard lock(mutex_); 692   116 std::lock_guard lock(mutex_);
693   693  
HITCBC 694   116 std::error_code first_error; 694   116 std::error_code first_error;
695   695  
HITCBC 696   216 while (signal_registration* reg = impl.signals_) 696   216 while (signal_registration* reg = impl.signals_)
697   { 697   {
HITCBC 698   100 int signal_number = reg->signal_number; 698   100 int signal_number = reg->signal_number;
699   699  
HITCBC 700   100 if (state->registration_count[signal_number] == 1) 700   100 if (state->registration_count[signal_number] == 1)
701   { 701   {
HITCBC 702   94 struct sigaction sa = {}; 702   94 struct sigaction sa = {};
HITCBC 703   94 sa.sa_handler = SIG_DFL; 703   94 sa.sa_handler = SIG_DFL;
HITCBC 704   94 sigemptyset(&sa.sa_mask); 704   94 sigemptyset(&sa.sa_mask);
HITCBC 705   94 sa.sa_flags = 0; 705   94 sa.sa_flags = 0;
706   706  
HITCBC 707   94 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) 707   94 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
MISUBC 708   first_error = make_error_code(std::errc::invalid_argument); 708   first_error = make_error_code(std::errc::invalid_argument);
709   709  
710   // Clear stored flags 710   // Clear stored flags
HITCBC 711   94 state->registered_flags[signal_number] = signal_set::none; 711   94 state->registered_flags[signal_number] = signal_set::none;
712   } 712   }
713   713  
HITCBC 714   100 impl.signals_ = reg->next_in_set; 714   100 impl.signals_ = reg->next_in_set;
715   715  
HITCBC 716   100 if (registrations_[signal_number] == reg) 716   100 if (registrations_[signal_number] == reg)
HITCBC 717   100 registrations_[signal_number] = reg->next_in_table; 717   100 registrations_[signal_number] = reg->next_in_table;
HITCBC 718   100 if (reg->prev_in_table) 718   100 if (reg->prev_in_table)
MISUBC 719   reg->prev_in_table->next_in_table = reg->next_in_table; 719   reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 720   100 if (reg->next_in_table) 720   100 if (reg->next_in_table)
HITCBC 721   6 reg->next_in_table->prev_in_table = reg->prev_in_table; 721   6 reg->next_in_table->prev_in_table = reg->prev_in_table;
722   722  
HITCBC 723   100 --state->registration_count[signal_number]; 723   100 --state->registration_count[signal_number];
HITCBC 724   100 --registration_count_[signal_number]; 724   100 --registration_count_[signal_number];
725   725  
HITCBC 726   100 delete reg; 726   100 delete reg;
HITCBC 727   100 } 727   100 }
728   728  
HITCBC 729   116 if (first_error) 729   116 if (first_error)
MISUBC 730   return first_error; 730   return first_error;
HITCBC 731   116 return {}; 731   116 return {};
HITCBC 732   116 } 732   116 }
733   733  
734   inline void 734   inline void
HITCBC 735   126 posix_signal_service::cancel_wait(posix_signal& impl) 735   126 posix_signal_service::cancel_wait(posix_signal& impl)
736   { 736   {
HITCBC 737   126 bool was_waiting = false; 737   126 bool was_waiting = false;
HITCBC 738   126 signal_op* op = nullptr; 738   126 signal_op* op = nullptr;
739   739  
740   { 740   {
HITCBC 741   126 std::lock_guard lock(mutex_); 741   126 std::lock_guard lock(mutex_);
HITCBC 742   126 impl.cancelled_ = true; 742   126 impl.cancelled_ = true;
HITCBC 743   126 if (impl.waiting_) 743   126 if (impl.waiting_)
744   { 744   {
HITCBC 745   4 was_waiting = true; 745   4 was_waiting = true;
HITCBC 746   4 impl.waiting_ = false; 746   4 impl.waiting_ = false;
HITCBC 747   4 op = &impl.pending_op_; 747   4 op = &impl.pending_op_;
748   } 748   }
HITCBC 749   126 } 749   126 }
750   750  
HITCBC 751   126 if (was_waiting) 751   126 if (was_waiting)
752   { 752   {
HITCBC 753   4 if (op->ec_out) 753   4 if (op->ec_out)
HITCBC 754   4 *op->ec_out = make_error_code(capy::error::canceled); 754   4 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 755   4 if (op->signal_out) 755   4 if (op->signal_out)
HITCBC 756   4 *op->signal_out = 0; 756   4 *op->signal_out = 0;
HITCBC 757   4 op->cont.h = op->h; 757   4 op->cont.h = op->h;
HITCBC 758   4 op->d.post(op->cont); 758   4 op->d.post(op->cont);
HITCBC 759   4 sched_->work_finished(); 759   4 sched_->work_finished();
760   } 760   }
HITCBC 761   126 } 761   126 }
762   762  
763   inline void 763   inline void
HITCBC 764   308 posix_signal_service::start_wait(posix_signal& impl, signal_op* op) 764   308 posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
765   { 765   {
766   { 766   {
HITCBC 767   308 std::lock_guard lock(mutex_); 767   308 std::lock_guard lock(mutex_);
768   768  
769   // Check if cancel() was called before this wait started 769   // Check if cancel() was called before this wait started
HITCBC 770   308 if (impl.cancelled_) 770   308 if (impl.cancelled_)
771   { 771   {
HITCBC 772   2 impl.cancelled_ = false; 772   2 impl.cancelled_ = false;
HITCBC 773   2 if (op->ec_out) 773   2 if (op->ec_out)
HITCBC 774   2 *op->ec_out = make_error_code(capy::error::canceled); 774   2 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 775   2 if (op->signal_out) 775   2 if (op->signal_out)
HITCBC 776   2 *op->signal_out = 0; 776   2 *op->signal_out = 0;
HITCBC 777   2 op->cont.h = op->h; 777   2 op->cont.h = op->h;
HITCBC 778   2 op->d.post(op->cont); 778   2 op->d.post(op->cont);
HITCBC 779   2 return; 779   2 return;
780   } 780   }
781   781  
782   // Check for queued signals first (signal arrived before wait started) 782   // Check for queued signals first (signal arrived before wait started)
HITCBC 783   306 signal_registration* reg = impl.signals_; 783   306 signal_registration* reg = impl.signals_;
HITCBC 784   614 while (reg) 784   614 while (reg)
785   { 785   {
HITCBC 786   308 if (reg->undelivered > 0) 786   308 if (reg->undelivered > 0)
787   { 787   {
MISUBC 788   --reg->undelivered; 788   --reg->undelivered;
MISUBC 789   op->signal_number = reg->signal_number; 789   op->signal_number = reg->signal_number;
790   // svc=nullptr: no work_finished needed since we never called work_started 790   // svc=nullptr: no work_finished needed since we never called work_started
MISUBC 791   op->svc = nullptr; 791   op->svc = nullptr;
MISUBC 792   sched_->post(op); 792   sched_->post(op);
MISUBC 793   return; 793   return;
794   } 794   }
HITCBC 795   308 reg = reg->next_in_set; 795   308 reg = reg->next_in_set;
796   } 796   }
797   797  
798   // No queued signals - wait for delivery 798   // No queued signals - wait for delivery
HITCBC 799   306 impl.waiting_ = true; 799   306 impl.waiting_ = true;
800   // svc=this: signal_op::operator() will call work_finished() to balance this 800   // svc=this: signal_op::operator() will call work_finished() to balance this
HITCBC 801   306 op->svc = this; 801   306 op->svc = this;
HITCBC 802   306 sched_->work_started(); 802   306 sched_->work_started();
HITCBC 803   308 } 803   308 }
804   } 804   }
805   805  
806   inline void 806   inline void
HITCBC 807   300 posix_signal_service::deliver_signal(int signal_number) 807   300 posix_signal_service::deliver_signal(int signal_number)
808   { 808   {
HITCBC 809   300 if (signal_number < 0 || signal_number >= max_signal_number) 809   300 if (signal_number < 0 || signal_number >= max_signal_number)
MISUBC 810   return; 810   return;
811   811  
812   posix_signal_detail::signal_state* state = 812   posix_signal_detail::signal_state* state =
HITCBC 813   300 posix_signal_detail::get_signal_state(); 813   300 posix_signal_detail::get_signal_state();
HITCBC 814   300 std::lock_guard lock(state->mutex); 814   300 std::lock_guard lock(state->mutex);
815   815  
HITCBC 816   300 posix_signal_service* service = state->service_list; 816   300 posix_signal_service* service = state->service_list;
HITCBC 817   600 while (service) 817   600 while (service)
818   { 818   {
HITCBC 819   300 std::lock_guard svc_lock(service->mutex_); 819   300 std::lock_guard svc_lock(service->mutex_);
820   820  
HITCBC 821   300 signal_registration* reg = service->registrations_[signal_number]; 821   300 signal_registration* reg = service->registrations_[signal_number];
HITCBC 822   602 while (reg) 822   602 while (reg)
823   { 823   {
HITCBC 824   302 posix_signal* impl = static_cast<posix_signal*>(reg->owner); 824   302 posix_signal* impl = static_cast<posix_signal*>(reg->owner);
825   825  
HITCBC 826   302 if (impl->waiting_) 826   302 if (impl->waiting_)
827   { 827   {
HITCBC 828   302 impl->waiting_ = false; 828   302 impl->waiting_ = false;
HITCBC 829   302 impl->pending_op_.signal_number = signal_number; 829   302 impl->pending_op_.signal_number = signal_number;
HITCBC 830   302 service->post(&impl->pending_op_); 830   302 service->post(&impl->pending_op_);
831   } 831   }
832   else 832   else
833   { 833   {
MISUBC 834   ++reg->undelivered; 834   ++reg->undelivered;
835   } 835   }
836   836  
HITCBC 837   302 reg = reg->next_in_table; 837   302 reg = reg->next_in_table;
838   } 838   }
839   839  
HITCBC 840   300 service = service->next_; 840   300 service = service->next_;
HITCBC 841   300 } 841   300 }
HITCBC 842   300 } 842   300 }
843   843  
844   inline void 844   inline void
845   posix_signal_service::work_started() noexcept 845   posix_signal_service::work_started() noexcept
846   { 846   {
847   sched_->work_started(); 847   sched_->work_started();
848   } 848   }
849   849  
850   inline void 850   inline void
HITCBC 851   302 posix_signal_service::work_finished() noexcept 851   302 posix_signal_service::work_finished() noexcept
852   { 852   {
HITCBC 853   302 sched_->work_finished(); 853   302 sched_->work_finished();
HITCBC 854   302 } 854   302 }
855   855  
856   inline void 856   inline void
HITCBC 857   302 posix_signal_service::post(signal_op* op) 857   302 posix_signal_service::post(signal_op* op)
858   { 858   {
HITCBC 859   302 sched_->post(op); 859   302 sched_->post(op);
HITCBC 860   302 } 860   302 }
861   861  
862   inline void 862   inline void
HITCBC 863   1217 posix_signal_service::add_service(posix_signal_service* service) 863   1225 posix_signal_service::add_service(posix_signal_service* service)
864   { 864   {
865   posix_signal_detail::signal_state* state = 865   posix_signal_detail::signal_state* state =
HITCBC 866   1217 posix_signal_detail::get_signal_state(); 866   1225 posix_signal_detail::get_signal_state();
HITCBC 867   1217 std::lock_guard lock(state->mutex); 867   1225 std::lock_guard lock(state->mutex);
868   868  
HITCBC 869   1217 service->next_ = state->service_list; 869   1225 service->next_ = state->service_list;
HITCBC 870   1217 service->prev_ = nullptr; 870   1225 service->prev_ = nullptr;
HITCBC 871   1217 if (state->service_list) 871   1225 if (state->service_list)
HITCBC 872   5 state->service_list->prev_ = service; 872   5 state->service_list->prev_ = service;
HITCBC 873   1217 state->service_list = service; 873   1225 state->service_list = service;
HITCBC 874   1217 } 874   1225 }
875   875  
876   inline void 876   inline void
HITCBC 877   1217 posix_signal_service::remove_service(posix_signal_service* service) 877   1225 posix_signal_service::remove_service(posix_signal_service* service)
878   { 878   {
879   posix_signal_detail::signal_state* state = 879   posix_signal_detail::signal_state* state =
HITCBC 880   1217 posix_signal_detail::get_signal_state(); 880   1225 posix_signal_detail::get_signal_state();
HITCBC 881   1217 std::lock_guard lock(state->mutex); 881   1225 std::lock_guard lock(state->mutex);
882   882  
HITCBC 883   1217 if (service->next_ || service->prev_ || state->service_list == service) 883   1225 if (service->next_ || service->prev_ || state->service_list == service)
884   { 884   {
HITCBC 885   1217 if (state->service_list == service) 885   1225 if (state->service_list == service)
HITCBC 886   1217 state->service_list = service->next_; 886   1225 state->service_list = service->next_;
HITCBC 887   1217 if (service->prev_) 887   1225 if (service->prev_)
MISUBC 888   service->prev_->next_ = service->next_; 888   service->prev_->next_ = service->next_;
HITCBC 889   1217 if (service->next_) 889   1225 if (service->next_)
HITCBC 890   5 service->next_->prev_ = service->prev_; 890   5 service->next_->prev_ = service->prev_;
HITCBC 891   1217 service->next_ = nullptr; 891   1225 service->next_ = nullptr;
HITCBC 892   1217 service->prev_ = nullptr; 892   1225 service->prev_ = nullptr;
893   } 893   }
HITCBC 894   1217 } 894   1225 }
895   895  
896   // get_signal_service - factory function 896   // get_signal_service - factory function
897   897  
898   inline posix_signal_service& 898   inline posix_signal_service&
HITCBC 899   1217 get_signal_service(capy::execution_context& ctx, scheduler& sched) 899   1225 get_signal_service(capy::execution_context& ctx, scheduler& sched)
900   { 900   {
HITCBC 901   1217 return ctx.make_service<posix_signal_service>(sched); 901   1225 return ctx.make_service<posix_signal_service>(sched);
902   } 902   }
903   903  
904   } // namespace detail 904   } // namespace detail
905   } // namespace boost::corosio 905   } // namespace boost::corosio
906   906  
907   #endif // BOOST_COROSIO_POSIX 907   #endif // BOOST_COROSIO_POSIX
908   908  
909   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 909   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP