98.04% Lines (401/409) 97.06% Functions (33/34)
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/corosio/detail/object_pool.hpp> 21   #include <boost/corosio/detail/object_pool.hpp>
22   #include <boost/corosio/detail/object_ref.hpp> 22   #include <boost/corosio/detail/object_ref.hpp>
23   #include <boost/capy/continuation.hpp> 23   #include <boost/capy/continuation.hpp>
24   #include <boost/capy/ex/abandon.hpp> 24   #include <boost/capy/ex/abandon.hpp>
25   #include <boost/capy/ex/execution_context.hpp> 25   #include <boost/capy/ex/execution_context.hpp>
26   #include <boost/corosio/detail/scheduler.hpp> 26   #include <boost/corosio/detail/scheduler.hpp>
27   #include <boost/corosio/native/detail/make_err.hpp> 27   #include <boost/corosio/native/detail/make_err.hpp>
28   #include <boost/capy/error.hpp> 28   #include <boost/capy/error.hpp>
29   29  
30   #include <coroutine> 30   #include <coroutine>
31   #include <mutex> 31   #include <mutex>
32   #include <tuple> 32   #include <tuple>
33   #include <utility> 33   #include <utility>
34   #include <vector> 34   #include <vector>
35   35  
36   #include <errno.h> 36   #include <errno.h>
37   #include <fcntl.h> 37   #include <fcntl.h>
38   #include <signal.h> 38   #include <signal.h>
39   #include <unistd.h> 39   #include <unistd.h>
40   40  
41   /* 41   /*
42   POSIX Signal Service 42   POSIX Signal Service
43   ==================== 43   ====================
44   44  
45   Concrete signal service implementation for POSIX backends. Manages signal 45   Concrete signal service implementation for POSIX backends. Manages signal
46   registrations via sigaction() and dispatches completions through the 46   registrations via sigaction() and dispatches completions through the
47   scheduler. One instance per execution_context, created on first use 47   scheduler. One instance per execution_context, created on first use
48   by the public signal_set. 48   by the public signal_set.
49   49  
50   See the block comment further down for the full architecture overview. 50   See the block comment further down for the full architecture overview.
51   */ 51   */
52   52  
53   /* 53   /*
54   POSIX Signal Implementation 54   POSIX Signal Implementation
55   =========================== 55   ===========================
56   56  
57   This file implements signal handling for POSIX systems using sigaction(). 57   This file implements signal handling for POSIX systems using sigaction().
58   The implementation supports signal flags (SA_RESTART, etc.) and integrates 58   The implementation supports signal flags (SA_RESTART, etc.) and integrates
59   with any POSIX-compatible scheduler via the abstract scheduler interface. 59   with any POSIX-compatible scheduler via the abstract scheduler interface.
60   60  
61   Architecture Overview 61   Architecture Overview
62   --------------------- 62   ---------------------
63   63  
64   Three layers manage signal registrations: 64   Three layers manage signal registrations:
65   65  
66   1. signal_state (global singleton) 66   1. signal_state (global singleton)
67   - Tracks the global service list and per-signal registration counts 67   - Tracks the global service list and per-signal registration counts
68   - Stores the flags used for first registration of each signal (for 68   - Stores the flags used for first registration of each signal (for
69   conflict detection when multiple signal_sets register same signal) 69   conflict detection when multiple signal_sets register same signal)
70   - Owns the mutex that protects signal handler installation/removal 70   - Owns the mutex that protects signal handler installation/removal
71   71  
72   2. posix_signal_service (one per execution_context) 72   2. posix_signal_service (one per execution_context)
73   - Maintains registrations_[] table indexed by signal number 73   - Maintains registrations_[] table indexed by signal number
74   - Each slot is a doubly-linked list of signal_registrations for that signal 74   - Each slot is a doubly-linked list of signal_registrations for that signal
75   - Also owns a recycling pool of all posix_signal objects it owns 75   - Also owns a recycling pool of all posix_signal objects it owns
76   76  
77   3. posix_signal (one per signal_set) 77   3. posix_signal (one per signal_set)
78   - Owns a singly-linked list (sorted by signal number) of signal_registrations 78   - Owns a singly-linked list (sorted by signal number) of signal_registrations
79   - Contains the pending_op_ used for wait operations 79   - Contains the pending_op_ used for wait operations
80   80  
81   Signal Delivery Flow 81   Signal Delivery Flow
82   -------------------- 82   --------------------
83   83  
84   Delivery uses the self-pipe trick so the signal handler itself performs 84   Delivery uses the self-pipe trick so the signal handler itself performs
85   only async-signal-safe work (mirrors Boost.Asio): 85   only async-signal-safe work (mirrors Boost.Asio):
86   86  
87   1. Signal arrives -> corosio_posix_signal_handler(). The handler only 87   1. Signal arrives -> corosio_posix_signal_handler(). The handler only
88   write()s the signal number to the global self-pipe (write_fd) and 88   write()s the signal number to the global self-pipe (write_fd) and
89   restores errno. No locks, no allocation, no scheduler dispatch. 89   restores errno. No locks, no allocation, no scheduler dispatch.
90   90  
91   2. The read end of the pipe is watched by one backend's event loop 91   2. The read end of the pipe is watched by one backend's event loop
92   (registered via scheduler::register_signal_reader on the first 92   (registered via scheduler::register_signal_reader on the first
93   registration). When it becomes readable the backend drains it 93   registration). When it becomes readable the backend drains it
94   (drain_signal_pipe) and calls deliver_signal() in normal context. 94   (drain_signal_pipe) and calls deliver_signal() in normal context.
95   95  
96   3. deliver_signal() iterates all posix_signal_service services: 96   3. deliver_signal() iterates all posix_signal_service services:
97   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op 97   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op
98   to the scheduler for immediate completion 98   to the scheduler for immediate completion
99   - Otherwise, increment reg->undelivered to queue the signal 99   - Otherwise, increment reg->undelivered to queue the signal
100   100  
101   4. When wait() is called via start_wait(): 101   4. When wait() is called via start_wait():
102   - First check for queued signals (undelivered > 0); if found, post 102   - First check for queued signals (undelivered > 0); if found, post
103   immediate completion without blocking 103   immediate completion without blocking
104   - Otherwise, set waiting_ = true and call work_started() to keep 104   - Otherwise, set waiting_ = true and call work_started() to keep
105   the io_context alive 105   the io_context alive
106   106  
107   Locking Protocol 107   Locking Protocol
108   ---------------- 108   ----------------
109   109  
110   Two mutex levels exist (MUST acquire in this order to avoid deadlock): 110   Two mutex levels exist (MUST acquire in this order to avoid deadlock):
111   1. signal_state::mutex - protects handler registration and service list 111   1. signal_state::mutex - protects handler registration and service list
112   2. posix_signal_service::mutex_ - protects per-service registration tables 112   2. posix_signal_service::mutex_ - protects per-service registration tables
113   113  
114   Async-Signal-Safety 114   Async-Signal-Safety
115   ------------------- 115   -------------------
116   116  
117   The C signal handler (corosio_posix_signal_handler) performs only 117   The C signal handler (corosio_posix_signal_handler) performs only
118   async-signal-safe operations: it reads the single global write_fd and 118   async-signal-safe operations: it reads the single global write_fd and
119   calls write(), saving/restoring errno. It never locks a mutex, allocates 119   calls write(), saving/restoring errno. It never locks a mutex, allocates
120   memory, or dispatches through the scheduler. All of that happens in 120   memory, or dispatches through the scheduler. All of that happens in
121   deliver_signal(), which runs in normal thread context from the backend 121   deliver_signal(), which runs in normal thread context from the backend
122   event loop after draining the self-pipe. There is therefore no 122   event loop after draining the self-pipe. There is therefore no
123   self-deadlock risk if a signal arrives while a thread holds state->mutex 123   self-deadlock risk if a signal arrives while a thread holds state->mutex
124   or service->mutex_. 124   or service->mutex_.
125   125  
126   Flag Handling 126   Flag Handling
127   ------------- 127   -------------
128   128  
129   - Flags are abstract values in the public API (signal_set::flags_t) 129   - Flags are abstract values in the public API (signal_set::flags_t)
130   - flags_supported() validates that requested flags are available on 130   - flags_supported() validates that requested flags are available on
131   this platform; returns false if SA_NOCLDWAIT is unavailable and 131   this platform; returns false if SA_NOCLDWAIT is unavailable and
132   no_child_wait is requested 132   no_child_wait is requested
133   - to_sigaction_flags() maps validated flags to actual SA_* constants 133   - to_sigaction_flags() maps validated flags to actual SA_* constants
134   - First registration of a signal establishes the flags; subsequent 134   - First registration of a signal establishes the flags; subsequent
135   registrations must be compatible (same flags or dont_care) 135   registrations must be compatible (same flags or dont_care)
136   - Requesting unavailable flags returns operation_not_supported 136   - Requesting unavailable flags returns operation_not_supported
137   137  
138   Work Tracking 138   Work Tracking
139   ------------- 139   -------------
140   140  
141   When waiting for a signal: 141   When waiting for a signal:
142   - start_wait() calls sched_->work_started() to prevent io_context::run() 142   - start_wait() calls sched_->work_started() to prevent io_context::run()
143   from returning while we wait 143   from returning while we wait
144   - signal_op::svc is set to point to the service 144   - signal_op::svc is set to point to the service
145   - signal_op::operator()() calls work_finished() after resuming the coroutine 145   - signal_op::operator()() calls work_finished() after resuming the coroutine
146   146  
147   If a signal was already queued (undelivered > 0), no work tracking is needed 147   If a signal was already queued (undelivered > 0), no work tracking is needed
148   because completion is posted immediately. 148   because completion is posted immediately.
149   */ 149   */
150   150  
151   namespace boost::corosio { 151   namespace boost::corosio {
152   152  
153   namespace detail { 153   namespace detail {
154   154  
155   /** Signal service for POSIX backends. 155   /** Signal service for POSIX backends.
156   156  
157   Manages signal registrations via sigaction() and dispatches signal 157   Manages signal registrations via sigaction() and dispatches signal
158   completions through the scheduler. One instance per execution_context. 158   completions through the scheduler. One instance per execution_context.
159   */ 159   */
160   class BOOST_COROSIO_DECL posix_signal_service final 160   class BOOST_COROSIO_DECL posix_signal_service final
161   : public capy::execution_context::service 161   : public capy::execution_context::service
162   , public io_object::io_service 162   , public io_object::io_service
163   { 163   {
164   friend class posix_signal; 164   friend class posix_signal;
165   165  
166   public: 166   public:
167   using key_type = posix_signal_service; 167   using key_type = posix_signal_service;
168   168  
169   explicit posix_signal_service(capy::execution_context& ctx); 169   explicit posix_signal_service(capy::execution_context& ctx);
170   ~posix_signal_service() override; 170   ~posix_signal_service() override;
171   171  
172   posix_signal_service(posix_signal_service const&) = delete; 172   posix_signal_service(posix_signal_service const&) = delete;
173   posix_signal_service& operator=(posix_signal_service const&) = delete; 173   posix_signal_service& operator=(posix_signal_service const&) = delete;
174   174  
175   io_object::implementation* construct() override; 175   io_object::implementation* construct() override;
176   176  
HITCBC 177   3476 void destroy(io_object::implementation* p) override 177   3478 void destroy(io_object::implementation* p) override
178   { 178   {
HITCBC 179   3476 auto& impl = static_cast<posix_signal&>(*p); 179   3478 auto& impl = static_cast<posix_signal&>(*p);
HITCBC 180   3476 [[maybe_unused]] auto n = impl.clear(); 180   3478 [[maybe_unused]] auto n = impl.clear();
HITCBC 181   3476 impl.disarm_stop(); 181   3478 impl.disarm_stop();
HITCBC 182   3476 impl.cancel(); 182   3478 impl.cancel();
HITCBC 183   3476 release(&impl); 183   3478 release(&impl);
HITCBC 184   3476 } 184   3478 }
185   185  
186   /** Shut down the service. 186   /** Shut down the service.
187   187  
188   Gives every registration its live implementations still hold 188   Gives every registration its live implementations still hold
189   back to the process-global table. The implementations are 189   back to the process-global table. The implementations are
190   freed when their handles release them, or when the service is 190   freed when their handles release them, or when the service is
191   destroyed if a handle outlives it. 191   destroyed if a handle outlives it.
192   */ 192   */
193   void shutdown() override; 193   void shutdown() override;
194   194  
195   std::error_code add_signal( 195   std::error_code add_signal(
196   posix_signal& impl, int signal_number, signal_set::flags_t flags); 196   posix_signal& impl, int signal_number, signal_set::flags_t flags);
197   197  
198   std::error_code remove_signal(posix_signal& impl, int signal_number); 198   std::error_code remove_signal(posix_signal& impl, int signal_number);
199   199  
200   std::error_code clear_signals(posix_signal& impl); 200   std::error_code clear_signals(posix_signal& impl);
201   201  
202   void cancel_wait(posix_signal& impl) noexcept; 202   void cancel_wait(posix_signal& impl) noexcept;
203   void start_wait(posix_signal& impl, signal_op* op); 203   void start_wait(posix_signal& impl, signal_op* op);
204   204  
205   /** Cancel an in-flight wait on behalf of a stop token. 205   /** Cancel an in-flight wait on behalf of a stop token.
206   206  
207   Identical to @ref cancel_wait except that it does not set the 207   Identical to @ref cancel_wait except that it does not set the
208   sticky `cancelled_` latch: a stop token scopes to one operation, 208   sticky `cancelled_` latch: a stop token scopes to one operation,
209   so a request arriving after the wait completed must do nothing. 209   so a request arriving after the wait completed must do nothing.
210   */ 210   */
211   void cancel_wait_token(posix_signal& impl) noexcept; 211   void cancel_wait_token(posix_signal& impl) noexcept;
212   212  
213   /** Clear the per-operation stop flag before a new wait arms. 213   /** Clear the per-operation stop flag before a new wait arms.
214   214  
215   Lives here rather than on the implementation because `mutex_` is 215   Lives here rather than on the implementation because `mutex_` is
216   the service's; the service is a friend of `posix_signal`, not the 216   the service's; the service is a friend of `posix_signal`, not the
217   reverse. 217   reverse.
218   */ 218   */
HITCBC 219   205877 void reset_token_cancel(posix_signal& impl) noexcept 219   205719 void reset_token_cancel(posix_signal& impl) noexcept
220   { 220   {
HITCBC 221   205877 std::lock_guard lock(mutex_); 221   205719 std::lock_guard lock(mutex_);
HITCBC 222   205877 impl.token_cancelled_ = false; 222   205719 impl.token_cancelled_ = false;
HITCBC 223   205877 } 223   205719 }
224   224  
225   static void deliver_signal(int signal_number); 225   static void deliver_signal(int signal_number);
226   226  
227   void work_started() noexcept; 227   void work_started() noexcept;
228   void work_finished() noexcept; 228   void work_finished() noexcept;
229   void post(signal_op* op); 229   void post(signal_op* op);
230   230  
231   private: 231   private:
232   static void add_service(posix_signal_service* service); 232   static void add_service(posix_signal_service* service);
233   static void remove_service(posix_signal_service* service); 233   static void remove_service(posix_signal_service* service);
234   234  
235   scheduler* sched_; 235   scheduler* sched_;
236   std::mutex mutex_; 236   std::mutex mutex_;
237   237  
238   // Registers the signal self-pipe's read end with sched_ exactly once per 238   // Registers the signal self-pipe's read end with sched_ exactly once per
239   // service, so every io_context that waits on a signal can drain the pipe. 239   // service, so every io_context that waits on a signal can drain the pipe.
240   // A once_flag (not a bool under mutex_) because registration must run 240   // A once_flag (not a bool under mutex_) because registration must run
241   // without holding mutex_ or the signal-state mutex — see add_signal. 241   // without holding mutex_ or the signal-state mutex — see add_signal.
242   std::mutex reader_mutex_; 242   std::mutex reader_mutex_;
243   bool reader_registered_ = false; 243   bool reader_registered_ = false;
244   244  
245   object_pool<posix_signal> pool_; 245   object_pool<posix_signal> pool_;
246   246  
247   // Per-signal registration table 247   // Per-signal registration table
248   signal_registration* registrations_[max_signal_number]; 248   signal_registration* registrations_[max_signal_number];
249   249  
250   // Registration counts for each signal 250   // Registration counts for each signal
251   std::size_t registration_count_[max_signal_number]; 251   std::size_t registration_count_[max_signal_number];
252   252  
253   // Linked list of all posix_signal_service services for signal delivery 253   // Linked list of all posix_signal_service services for signal delivery
254   posix_signal_service* next_ = nullptr; 254   posix_signal_service* next_ = nullptr;
255   posix_signal_service* prev_ = nullptr; 255   posix_signal_service* prev_ = nullptr;
256   }; 256   };
257   257  
258   } // namespace detail 258   } // namespace detail
259   259  
260   } // namespace boost::corosio 260   } // namespace boost::corosio
261   261  
262   // --------------------------------------------------------------------------- 262   // ---------------------------------------------------------------------------
263   // Inline implementation 263   // Inline implementation
264   // --------------------------------------------------------------------------- 264   // ---------------------------------------------------------------------------
265   265  
266   namespace boost::corosio { 266   namespace boost::corosio {
267   267  
268   namespace detail { 268   namespace detail {
269   269  
270   namespace posix_signal_detail { 270   namespace posix_signal_detail {
271   271  
272   struct signal_state 272   struct signal_state
273   { 273   {
274   std::mutex mutex; 274   std::mutex mutex;
275   posix_signal_service* service_list = nullptr; 275   posix_signal_service* service_list = nullptr;
276   std::size_t registration_count[max_signal_number] = {}; 276   std::size_t registration_count[max_signal_number] = {};
277   signal_set::flags_t registered_flags[max_signal_number] = {}; 277   signal_set::flags_t registered_flags[max_signal_number] = {};
278   278  
279   // Self-pipe used to defer signal delivery out of handler context. 279   // Self-pipe used to defer signal delivery out of handler context.
280   // The C handler writes the signal number to write_fd (async-signal- 280   // The C handler writes the signal number to write_fd (async-signal-
281   // safe); a backend event loop drains read_fd and calls deliver_signal() 281   // safe); a backend event loop drains read_fd and calls deliver_signal()
282   // in normal context. Created once (on the first signal registration) and 282   // in normal context. Created once (on the first signal registration) and
283   // kept for the process lifetime. Each posix_signal_service registers the 283   // kept for the process lifetime. Each posix_signal_service registers the
284   // read end with its own scheduler (see reader_once_) so every running 284   // read end with its own scheduler (see reader_once_) so every running
285   // io_context can drain it; multiple readers on one pipe are safe because 285   // io_context can drain it; multiple readers on one pipe are safe because
286   // each signal is a fixed sizeof(int) record read atomically. 286   // each signal is a fixed sizeof(int) record read atomically.
287   int read_fd = -1; 287   int read_fd = -1;
288   int write_fd = -1; 288   int write_fd = -1;
289   }; 289   };
290   290  
291   BOOST_COROSIO_DECL signal_state* get_signal_state(); 291   BOOST_COROSIO_DECL signal_state* get_signal_state();
292   292  
293   // Check if requested flags are supported on this platform. 293   // Check if requested flags are supported on this platform.
294   // Returns true if all flags are supported, false otherwise. 294   // Returns true if all flags are supported, false otherwise.
295   inline bool 295   inline bool
HITCBC 296   3491 flags_supported([[maybe_unused]] signal_set::flags_t flags) 296   3493 flags_supported([[maybe_unused]] signal_set::flags_t flags)
297   { 297   {
298   #ifndef SA_NOCLDWAIT 298   #ifndef SA_NOCLDWAIT
299   if (flags & signal_set::no_child_wait) 299   if (flags & signal_set::no_child_wait)
300   return false; 300   return false;
301   #endif 301   #endif
HITCBC 302   3491 return true; 302   3493 return true;
303   } 303   }
304   304  
305   // Map abstract flags to sigaction() flags. 305   // Map abstract flags to sigaction() flags.
306   // Caller must ensure flags_supported() returns true first. 306   // Caller must ensure flags_supported() returns true first.
307   inline int 307   inline int
HITCBC 308   641 to_sigaction_flags(signal_set::flags_t flags) 308   643 to_sigaction_flags(signal_set::flags_t flags)
309   { 309   {
HITCBC 310   641 int sa_flags = 0; 310   643 int sa_flags = 0;
HITCBC 311   641 if (flags & signal_set::restart) 311   643 if (flags & signal_set::restart)
HITCBC 312   23 sa_flags |= SA_RESTART; 312   23 sa_flags |= SA_RESTART;
HITCBC 313   641 if (flags & signal_set::no_child_stop) 313   643 if (flags & signal_set::no_child_stop)
HITCBC 314   3 sa_flags |= SA_NOCLDSTOP; 314   3 sa_flags |= SA_NOCLDSTOP;
315   #ifdef SA_NOCLDWAIT 315   #ifdef SA_NOCLDWAIT
HITCBC 316   641 if (flags & signal_set::no_child_wait) 316   643 if (flags & signal_set::no_child_wait)
HITCBC 317   2 sa_flags |= SA_NOCLDWAIT; 317   2 sa_flags |= SA_NOCLDWAIT;
318   #endif 318   #endif
HITCBC 319   641 if (flags & signal_set::no_defer) 319   643 if (flags & signal_set::no_defer)
HITCBC 320   4 sa_flags |= SA_NODEFER; 320   4 sa_flags |= SA_NODEFER;
HITCBC 321   641 if (flags & signal_set::reset_handler) 321   643 if (flags & signal_set::reset_handler)
HITCBC 322   2 sa_flags |= SA_RESETHAND; 322   2 sa_flags |= SA_RESETHAND;
HITCBC 323   641 return sa_flags; 323   643 return sa_flags;
324   } 324   }
325   325  
326   // Check if two flag values are compatible 326   // Check if two flag values are compatible
327   inline bool 327   inline bool
HITCBC 328   2841 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested) 328   2841 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
329   { 329   {
330   // dont_care is always compatible 330   // dont_care is always compatible
HITCBC 331   5680 if ((existing & signal_set::dont_care) || 331   5680 if ((existing & signal_set::dont_care) ||
HITCBC 332   2839 (requested & signal_set::dont_care)) 332   2839 (requested & signal_set::dont_care))
HITCBC 333   7 return true; 333   7 return true;
334   334  
335   // Mask out dont_care bit for comparison 335   // Mask out dont_care bit for comparison
HITCBC 336   2834 constexpr auto mask = ~signal_set::dont_care; 336   2834 constexpr auto mask = ~signal_set::dont_care;
HITCBC 337   2834 return (existing & mask) == (requested & mask); 337   2834 return (existing & mask) == (requested & mask);
338   } 338   }
339   339  
340   // Lazily create the global signal self-pipe. Idempotent; call under 340   // Lazily create the global signal self-pipe. Idempotent; call under
341   // state->mutex before installing the first signal handler so write_fd is 341   // state->mutex before installing the first signal handler so write_fd is
342   // valid by the time the handler can fire. Both ends are non-blocking and 342   // valid by the time the handler can fire. Both ends are non-blocking and
343   // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler). 343   // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler).
344   // Returns the failing call's errno and leaves the fds at -1 if creation 344   // Returns the failing call's errno and leaves the fds at -1 if creation
345   // fails: an exhausted descriptor table and a rejected fcntl are different 345   // fails: an exhausted descriptor table and a rejected fcntl are different
346   // problems to the caller of add(). 346   // problems to the caller of add().
347   [[nodiscard]] inline std::error_code 347   [[nodiscard]] inline std::error_code
HITCBC 348   3491 open_signal_pipe(signal_state* state) 348   3493 open_signal_pipe(signal_state* state)
349   { 349   {
HITCBC 350   3491 if (state->read_fd >= 0) 350   3493 if (state->read_fd >= 0)
HITCBC 351   3475 return {}; 351   3477 return {};
352   352  
353   int fds[2]; 353   int fds[2];
HITCBC 354   16 if (::pipe(fds) < 0) 354   16 if (::pipe(fds) < 0)
HITCBC 355   1 return make_err(errno); 355   1 return make_err(errno);
356   356  
HITCBC 357   36 for (int i = 0; i < 2; ++i) 357   36 for (int i = 0; i < 2; ++i)
358   { 358   {
HITCBC 359   27 int fl = ::fcntl(fds[i], F_GETFL, 0); 359   27 int fl = ::fcntl(fds[i], F_GETFL, 0);
HITCBC 360   50 if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 || 360   50 if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 ||
HITCBC 361   23 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) 361   23 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1)
362   { 362   {
HITCBC 363   6 auto ec = make_err(errno); 363   6 auto ec = make_err(errno);
HITCBC 364   6 ::close(fds[0]); 364   6 ::close(fds[0]);
HITCBC 365   6 ::close(fds[1]); 365   6 ::close(fds[1]);
HITCBC 366   6 return ec; 366   6 return ec;
367   } 367   }
368   } 368   }
369   369  
HITCBC 370   9 state->read_fd = fds[0]; 370   9 state->read_fd = fds[0];
HITCBC 371   9 state->write_fd = fds[1]; 371   9 state->write_fd = fds[1];
HITCBC 372   9 return {}; 372   9 return {};
373   } 373   }
374   374  
375   // C signal handler. Async-signal-safe: it touches only the single global 375   // C signal handler. Async-signal-safe: it touches only the single global
376   // write_fd (an int set before any handler is installed) and calls write(), 376   // write_fd (an int set before any handler is installed) and calls write(),
377   // which POSIX lists as async-signal-safe. errno is saved and restored so an 377   // which POSIX lists as async-signal-safe. errno is saved and restored so an
378   // interrupted foreground syscall is unaffected. A full pipe (write returns 378   // interrupted foreground syscall is unaffected. A full pipe (write returns
379   // EAGAIN) or a short write is intentionally dropped — the reactor still 379   // EAGAIN) or a short write is intentionally dropped — the reactor still
380   // coalesces because deliver_signal reports the signal to every waiting set. 380   // coalesces because deliver_signal reports the signal to every waiting set.
381   inline void 381   inline void
HITCBC 382   1521 corosio_posix_signal_handler(int signal_number) 382   1521 corosio_posix_signal_handler(int signal_number)
383   { 383   {
HITCBC 384   1521 int saved_errno = errno; 384   1521 int saved_errno = errno;
HITCBC 385   1521 signal_state* state = get_signal_state(); 385   1521 signal_state* state = get_signal_state();
386   [[maybe_unused]] ssize_t r = 386   [[maybe_unused]] ssize_t r =
HITCBC 387   1521 ::write(state->write_fd, &signal_number, sizeof(int)); 387   1521 ::write(state->write_fd, &signal_number, sizeof(int));
HITCBC 388   1521 errno = saved_errno; 388   1521 errno = saved_errno;
389   // With sigaction(), the handler persists automatically (unlike some 389   // With sigaction(), the handler persists automatically (unlike some
390   // signal() implementations that reset to SIG_DFL). 390   // signal() implementations that reset to SIG_DFL).
HITCBC 391   1521 } 391   1521 }
392   392  
393   // Drain the signal self-pipe and deliver each pending signal. Runs in normal 393   // Drain the signal self-pipe and deliver each pending signal. Runs in normal
394   // thread context from the backend event loop, so deliver_signal()'s mutex 394   // thread context from the backend event loop, so deliver_signal()'s mutex
395   // locking and scheduler post are safe here. Reads until EAGAIN (edge- 395   // locking and scheduler post are safe here. Reads until EAGAIN (edge-
396   // triggered backends require a full drain per readiness event). 396   // triggered backends require a full drain per readiness event).
397   inline void 397   inline void
HITCBC 398   780 drain_signal_pipe() 398   982 drain_signal_pipe()
399   { 399   {
HITCBC 400   780 signal_state* state = get_signal_state(); 400   982 signal_state* state = get_signal_state();
401   int signal_number; 401   int signal_number;
HITCBC 402   2301 while (::read(state->read_fd, &signal_number, sizeof(int)) == 402   2503 while (::read(state->read_fd, &signal_number, sizeof(int)) ==
403   static_cast<ssize_t>(sizeof(int))) 403   static_cast<ssize_t>(sizeof(int)))
404   { 404   {
HITCBC 405   1521 posix_signal_service::deliver_signal(signal_number); 405   1521 posix_signal_service::deliver_signal(signal_number);
406   } 406   }
HITCBC 407   780 } 407   982 }
408   408  
409   } // namespace posix_signal_detail 409   } // namespace posix_signal_detail
410   410  
411   // signal_op implementation 411   // signal_op implementation
412   412  
413   inline void 413   inline void
HITCBC 414   1997 signal_op::operator()() noexcept 414   1853 signal_op::operator()() noexcept
415   { 415   {
HITCBC 416   1997 if (ec_out) 416   1853 if (ec_out)
HITCBC 417   1997 *ec_out = {}; 417   1853 *ec_out = {};
HITCBC 418   1997 if (signal_out) 418   1853 if (signal_out)
HITCBC 419   1997 *signal_out = signal_number; 419   1853 *signal_out = signal_number;
420   420  
421   // Copied out before the reference drops: this op is embedded in 421   // Copied out before the reference drops: this op is embedded in
422   // the set, which the release may recycle. 422   // the set, which the release may recycle.
HITCBC 423   1997 auto* service = svc; 423   1853 auto* service = svc;
HITCBC 424   1997 svc = nullptr; 424   1853 svc = nullptr;
HITCBC 425   1997 auto* c = cont; 425   1853 auto* c = cont;
HITCBC 426   1997 auto ex = d; 426   1853 auto ex = d;
HITCBC 427   1997 auto k = std::move(keep); 427   1853 auto k = std::move(keep);
428   428  
429   // A throw terminates: the wait is no longer parked, so nothing else 429   // A throw terminates: the wait is no longer parked, so nothing else
430   // could resume or abandon the coroutine. 430   // could resume or abandon the coroutine.
HITCBC 431   1997 ex.post(*c); 431   1853 ex.post(*c);
432   432  
433   // Balance the work_started() from start_wait 433   // Balance the work_started() from start_wait
HITCBC 434   1997 if (service) 434   1853 if (service)
HITCBC 435   751 service->work_finished(); 435   738 service->work_finished();
HITCBC 436   1997 } 436   1853 }
437   437  
438   inline void 438   inline void
MISUBC 439   ✗ signal_op::destroy() 439   ✗ signal_op::destroy()
440   { 440   {
441   // Embedded in posix_signal: the queued reference keeps it alive 441   // Embedded in posix_signal: the queued reference keeps it alive
442   // while the awaiting coroutine, which may own the set, is destroyed. 442   // while the awaiting coroutine, which may own the set, is destroyed.
MISUBC 443   ✗ auto* c = std::exchange(cont, nullptr); 443   ✗ auto* c = std::exchange(cont, nullptr);
MISUBC 444   ✗ auto k = std::move(keep); 444   ✗ auto k = std::move(keep);
MISUBC 445   ✗ if (c && c->h) 445   ✗ if (c && c->h)
MISUBC 446   ✗ capy::abandon(*c); 446   ✗ capy::abandon(*c);
MISUBC 447   ✗ } 447   ✗ }
448   448  
449   // posix_signal implementation 449   // posix_signal implementation
450   450  
HITCBC 451   3404 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept 451   3406 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
HITCBC 452   3404 : svc_(svc) 452   3406 : svc_(svc)
453   { 453   {
HITCBC 454   3404 } 454   3406 }
455   455  
456   inline std::coroutine_handle<> 456   inline std::coroutine_handle<>
HITCBC 457   205992 posix_signal::wait( 457   205842 posix_signal::wait(
458   capy::continuation& cont, 458   capy::continuation& cont,
459   capy::executor_ref d, 459   capy::executor_ref d,
460   std::stop_token token, 460   std::stop_token token,
461   std::error_code* ec, 461   std::error_code* ec,
462   int* signal_out) 462   int* signal_out)
463   { 463   {
HITCBC 464   205992 pending_op_.cont = &cont; 464   205842 pending_op_.cont = &cont;
HITCBC 465   205992 pending_op_.d = d; 465   205842 pending_op_.d = d;
HITCBC 466   205992 pending_op_.ec_out = ec; 466   205842 pending_op_.ec_out = ec;
HITCBC 467   205992 pending_op_.signal_out = signal_out; 467   205842 pending_op_.signal_out = signal_out;
HITCBC 468   205992 pending_op_.signal_number = 0; 468   205842 pending_op_.signal_number = 0;
469   469  
470   // Disarm any callback left over from a previous wait before doing 470   // Disarm any callback left over from a previous wait before doing
471   // anything else, including the early return below: otherwise that 471   // anything else, including the early return below: otherwise that
472   // path leaves this object owning a callback it no longer uses. 472   // path leaves this object owning a callback it no longer uses.
473   // Outside start_wait's lock on purpose: ~stop_callback blocks until a 473   // Outside start_wait's lock on purpose: ~stop_callback blocks until a
474   // concurrently running callback returns, and that callback takes 474   // concurrently running callback returns, and that callback takes
475   // posix_signal_service::mutex_. 475   // posix_signal_service::mutex_.
HITCBC 476   205992 stop_cb_.reset(); 476   205842 stop_cb_.reset();
477   477  
HITCBC 478   205992 if (token.stop_requested()) 478   205842 if (token.stop_requested())
479   { 479   {
HITCBC 480   115 if (ec) 480   123 if (ec)
HITCBC 481   115 *ec = make_error_code(capy::error::canceled); 481   123 *ec = make_error_code(capy::error::canceled);
HITCBC 482   115 if (signal_out) 482   123 if (signal_out)
HITCBC 483   115 *signal_out = 0; 483   123 *signal_out = 0;
HITCBC 484   115 d.post(*pending_op_.cont); 484   123 d.post(*pending_op_.cont);
485   // completion is always posted to scheduler queue, never inline. 485   // completion is always posted to scheduler queue, never inline.
HITCBC 486   115 return std::noop_coroutine(); 486   123 return std::noop_coroutine();
487   } 487   }
488   488  
489   // Clearing the flag before arming is load-bearing: reset_token_cancel 489   // Clearing the flag before arming is load-bearing: reset_token_cancel
490   // must run immediately before emplace, not before the early return 490   // must run immediately before emplace, not before the early return
491   // above. 491   // above.
HITCBC 492   205877 svc_.reset_token_cancel(*this); 492   205719 svc_.reset_token_cancel(*this);
HITCBC 493   205877 if (token.stop_possible()) 493   205719 if (token.stop_possible())
HITCBC 494   5135 stop_cb_.emplace(token, token_canceller{this}); 494   4977 stop_cb_.emplace(token, token_canceller{this});
495   495  
HITCBC 496   205877 svc_.start_wait(*this, &pending_op_); 496   205719 svc_.start_wait(*this, &pending_op_);
497   // completion is always posted to scheduler queue, never inline. 497   // completion is always posted to scheduler queue, never inline.
HITCBC 498   205877 return std::noop_coroutine(); 498   205719 return std::noop_coroutine();
499   } 499   }
500   500  
501   inline std::error_code 501   inline std::error_code
HITCBC 502   3495 posix_signal::add(int signal_number, signal_set::flags_t flags) 502   3497 posix_signal::add(int signal_number, signal_set::flags_t flags)
503   { 503   {
HITCBC 504   3495 return svc_.add_signal(*this, signal_number, flags); 504   3497 return svc_.add_signal(*this, signal_number, flags);
505   } 505   }
506   506  
507   inline std::error_code 507   inline std::error_code
HITCBC 508   26 posix_signal::remove(int signal_number) 508   26 posix_signal::remove(int signal_number)
509   { 509   {
HITCBC 510   26 return svc_.remove_signal(*this, signal_number); 510   26 return svc_.remove_signal(*this, signal_number);
511   } 511   }
512   512  
513   inline std::error_code 513   inline std::error_code
HITCBC 514   3490 posix_signal::clear() 514   3492 posix_signal::clear()
515   { 515   {
HITCBC 516   3490 return svc_.clear_signals(*this); 516   3492 return svc_.clear_signals(*this);
517   } 517   }
518   518  
519   inline void 519   inline void
HITCBC 520   203497 posix_signal::cancel() noexcept 520   203499 posix_signal::cancel() noexcept
521   { 521   {
HITCBC 522   203497 svc_.cancel_wait(*this); 522   203499 svc_.cancel_wait(*this);
HITCBC 523   203497 } 523   203499 }
524   524  
525   // posix_signal_service implementation 525   // posix_signal_service implementation
526   526  
HITCBC 527   583 inline posix_signal_service::posix_signal_service( 527   585 inline posix_signal_service::posix_signal_service(
HITCBC 528   583 capy::execution_context& ctx) 528   585 capy::execution_context& ctx)
HITCBC 529   583 : sched_(&get_scheduler(ctx)) 529   585 : sched_(&get_scheduler(ctx))
530   { 530   {
HITCBC 531   37895 for (int i = 0; i < max_signal_number; ++i) 531   38025 for (int i = 0; i < max_signal_number; ++i)
532   { 532   {
HITCBC 533   37312 registrations_[i] = nullptr; 533   37440 registrations_[i] = nullptr;
HITCBC 534   37312 registration_count_[i] = 0; 534   37440 registration_count_[i] = 0;
535   } 535   }
HITCBC 536   583 add_service(this); 536   585 add_service(this);
HITCBC 537   583 } 537   585 }
538   538  
HITCBC 539   1166 inline posix_signal_service::~posix_signal_service() 539   1170 inline posix_signal_service::~posix_signal_service()
540   { 540   {
HITCBC 541   583 remove_service(this); 541   585 remove_service(this);
HITCBC 542   1166 } 542   1170 }
543   543  
544   inline void 544   inline void
HITCBC 545   583 posix_signal_service::shutdown() 545   585 posix_signal_service::shutdown()
546   { 546   {
547   // Each visited impl is pinned with acquire() so a handle 547   // Each visited impl is pinned with acquire() so a handle
548   // destroy() racing this walk cannot free it mid-walk. The 548   // destroy() racing this walk cannot free it mid-walk. The
549   // matching release() runs after the locks drop: if that handle's 549   // matching release() runs after the locks drop: if that handle's
550   // destroy() raced in, this release() is the last one, and 550   // destroy() raced in, this release() is the last one, and
551   // ~posix_signal destroys an armed stop_cb_ whose ~stop_callback 551   // ~posix_signal destroys an armed stop_cb_ whose ~stop_callback
552   // waits on a token_canceller that takes mutex_. 552   // waits on a token_canceller that takes mutex_.
HITCBC 553   583 std::vector<posix_signal*> doomed; 553   585 std::vector<posix_signal*> doomed;
HITCBC 554   583 std::vector<capy::continuation> parked; 554   585 std::vector<capy::continuation> parked;
555   555  
556   { 556   {
557   posix_signal_detail::signal_state* state = 557   posix_signal_detail::signal_state* state =
HITCBC 558   583 posix_signal_detail::get_signal_state(); 558   585 posix_signal_detail::get_signal_state();
HITCBC 559   583 std::lock_guard state_lock(state->mutex); 559   585 std::lock_guard state_lock(state->mutex);
HITCBC 560   583 std::lock_guard lock(mutex_); 560   585 std::lock_guard lock(mutex_);
561   561  
HITCBC 562   583 pool_.shutdown( 562   585 pool_.shutdown(
HITCBC 563   583 [&](posix_signal* impl) 563   585 [&](posix_signal* impl)
564   { 564   {
HITCBC 565   3210 acquire(impl); 565   3210 acquire(impl);
HITCBC 566   3210 doomed.push_back(impl); 566   3210 doomed.push_back(impl);
HITCBC 567   3210 }); 567   3210 });
HITCBC 568   3793 for (auto* impl : doomed) 568   3795 for (auto* impl : doomed)
569   { 569   {
570   // The parked wait's coroutine is destroyed once the locks 570   // The parked wait's coroutine is destroyed once the locks
571   // drop: destroying it can destroy the set, which takes them. 571   // drop: destroying it can destroy the set, which takes them.
HITCBC 572   3210 if (std::exchange(impl->waiting_, false)) 572   3210 if (std::exchange(impl->waiting_, false))
573   { 573   {
HITCBC 574   3210 if (auto* c = std::exchange(impl->pending_op_.cont, nullptr)) 574   3210 if (auto* c = std::exchange(impl->pending_op_.cont, nullptr))
575   { 575   {
HITCBC 576   3210 if (c->h) 576   3210 if (c->h)
HITCBC 577   3210 parked.push_back(*c); 577   3210 parked.push_back(*c);
578   } 578   }
579   } 579   }
580   580  
HITCBC 581   6420 while (auto* reg = impl->signals_) 581   6420 while (auto* reg = impl->signals_)
582   { 582   {
HITCBC 583   3210 int const signal_number = reg->signal_number; 583   3210 int const signal_number = reg->signal_number;
584   584  
585   // The registration table outlives every io_context, so a set 585   // The registration table outlives every io_context, so a set
586   // still registered here has to give its count and disposition 586   // still registered here has to give its count and disposition
587   // back the way clear() would: otherwise the signal stays 587   // back the way clear() would: otherwise the signal stays
588   // installed with these flags and the next add() of it is 588   // installed with these flags and the next add() of it is
589   // refused. The per-node table unlink clear() also does is 589   // refused. The per-node table unlink clear() also does is
590   // skipped in favour of the wholesale null-out below. 590   // skipped in favour of the wholesale null-out below.
HITCBC 591   3210 if (state->registration_count[signal_number] == 1) 591   3210 if (state->registration_count[signal_number] == 1)
592   { 592   {
HITCBC 593   408 struct sigaction sa = {}; 593   408 struct sigaction sa = {};
HITCBC 594   408 sa.sa_handler = SIG_DFL; 594   408 sa.sa_handler = SIG_DFL;
HITCBC 595   408 sigemptyset(&sa.sa_mask); 595   408 sigemptyset(&sa.sa_mask);
HITCBC 596   408 sa.sa_flags = 0; 596   408 sa.sa_flags = 0;
HITCBC 597   408 std::ignore = ::sigaction(signal_number, &sa, nullptr); 597   408 std::ignore = ::sigaction(signal_number, &sa, nullptr);
HITCBC 598   408 state->registered_flags[signal_number] = signal_set::none; 598   408 state->registered_flags[signal_number] = signal_set::none;
599   } 599   }
600   600  
HITCBC 601   3210 --state->registration_count[signal_number]; 601   3210 --state->registration_count[signal_number];
HITCBC 602   3210 --registration_count_[signal_number]; 602   3210 --registration_count_[signal_number];
603   603  
HITCBC 604   3210 impl->signals_ = reg->next_in_set; 604   3210 impl->signals_ = reg->next_in_set;
HITCBC 605   3210 delete reg; 605   3210 delete reg;
HITCBC 606   3210 } 606   3210 }
607   } 607   }
608   608  
609   // Every live registration hung off an implementation this pool 609   // Every live registration hung off an implementation this pool
610   // owns, so the whole table goes stale at once and can be dropped 610   // owns, so the whole table goes stale at once and can be dropped
611   // wholesale rather than node by node. It has to be dropped: 611   // wholesale rather than node by node. It has to be dropped:
612   // deliver_signal() walks this service until the destructor 612   // deliver_signal() walks this service until the destructor
613   // unlinks it from the global list. 613   // unlinks it from the global list.
HITCBC 614   37895 for (int i = 0; i < max_signal_number; ++i) 614   38025 for (int i = 0; i < max_signal_number; ++i)
HITCBC 615   37312 registrations_[i] = nullptr; 615   37440 registrations_[i] = nullptr;
HITCBC 616   583 } 616   585 }
617   617  
HITCBC 618   3793 for (auto& c : parked) 618   3795 for (auto& c : parked)
HITCBC 619   3210 capy::abandon(c); 619   3210 capy::abandon(c);
620   620  
HITCBC 621   3793 for (auto* impl : doomed) 621   3795 for (auto* impl : doomed)
HITCBC 622   3210 release(impl); 622   3210 release(impl);
HITCBC 623   583 } 623   585 }
624   624  
625   inline io_object::implementation* 625   inline io_object::implementation*
HITCBC 626   3476 posix_signal_service::construct() 626   3478 posix_signal_service::construct()
627   { 627   {
HITCBC 628   3476 return pool_.acquire(*this); 628   3478 return pool_.acquire(*this);
629   } 629   }
630   630  
631   inline void 631   inline void
HITCBC 632   3476 posix_signal::retire() noexcept 632   3478 posix_signal::retire() noexcept
633   { 633   {
HITCBC 634   3476 svc_.pool_.recycle(this); 634   3478 svc_.pool_.recycle(this);
HITCBC 635   3476 } 635   3478 }
636   636  
637   inline std::error_code 637   inline std::error_code
HITCBC 638   3495 posix_signal_service::add_signal( 638   3497 posix_signal_service::add_signal(
639   posix_signal& impl, int signal_number, signal_set::flags_t flags) 639   posix_signal& impl, int signal_number, signal_set::flags_t flags)
640   { 640   {
HITCBC 641   3495 if (signal_number < 0 || signal_number >= max_signal_number) 641   3497 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 642   4 return make_error_code(std::errc::invalid_argument); 642   4 return make_error_code(std::errc::invalid_argument);
643   643  
644   // Validate that requested flags are supported on this platform 644   // Validate that requested flags are supported on this platform
645   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems) 645   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
HITCBC 646   3491 if (!posix_signal_detail::flags_supported(flags)) 646   3493 if (!posix_signal_detail::flags_supported(flags))
MISUBC 647   ✗ return make_error_code(std::errc::operation_not_supported); 647   ✗ return make_error_code(std::errc::operation_not_supported);
648   648  
649   posix_signal_detail::signal_state* state = 649   posix_signal_detail::signal_state* state =
HITCBC 650   3491 posix_signal_detail::get_signal_state(); 650   3493 posix_signal_detail::get_signal_state();
651   651  
652   // Ensure the global self-pipe exists and this service's scheduler is 652   // Ensure the global self-pipe exists and this service's scheduler is
653   // watching its read end, BEFORE taking the registration locks: 653   // watching its read end, BEFORE taking the registration locks:
654   // register_signal_reader takes the descriptor mutex (via 654   // register_signal_reader takes the descriptor mutex (via
655   // register_descriptor), which no path may take while holding the 655   // register_descriptor), which no path may take while holding the
656   // signal-state or service mutexes. call_once makes the once-per-service 656   // signal-state or service mutexes. call_once makes the once-per-service
657   // registration safe when two signal_sets on this context race add() 657   // registration safe when two signal_sets on this context race add()
658   // from different threads. 658   // from different threads.
659   { 659   {
HITCBC 660   3491 std::lock_guard state_lock(state->mutex); 660   3493 std::lock_guard state_lock(state->mutex);
HITCBC 661   3491 if (auto ec = posix_signal_detail::open_signal_pipe(state)) 661   3493 if (auto ec = posix_signal_detail::open_signal_pipe(state))
HITCBC 662   7 return ec; 662   7 return ec;
HITCBC 663   3491 } 663   3493 }
664   { 664   {
665   // Success-latched so a failed environmental registration 665   // Success-latched so a failed environmental registration
666   // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add() 666   // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add()
667   // instead of being lost; the code travels the return channel. 667   // instead of being lost; the code travels the return channel.
HITCBC 668   3484 std::lock_guard reg_lock(reader_mutex_); 668   3486 std::lock_guard reg_lock(reader_mutex_);
HITCBC 669   3484 if (!reader_registered_) 669   3486 if (!reader_registered_)
670   { 670   {
HITCBC 671   555 if (auto ec = sched_->register_signal_reader(state->read_fd)) 671   557 if (auto ec = sched_->register_signal_reader(state->read_fd))
HITCBC 672   2 return ec; 672   2 return ec;
HITCBC 673   553 reader_registered_ = true; 673   555 reader_registered_ = true;
674   } 674   }
HITCBC 675   3484 } 675   3486 }
676   676  
HITCBC 677   3482 std::lock_guard state_lock(state->mutex); 677   3484 std::lock_guard state_lock(state->mutex);
HITCBC 678   3482 std::lock_guard lock(mutex_); 678   3484 std::lock_guard lock(mutex_);
679   679  
680   // Find insertion point (list is sorted by signal number) 680   // Find insertion point (list is sorted by signal number)
HITCBC 681   3482 signal_registration** insertion_point = &impl.signals_; 681   3484 signal_registration** insertion_point = &impl.signals_;
HITCBC 682   3482 signal_registration* reg = impl.signals_; 682   3484 signal_registration* reg = impl.signals_;
HITCBC 683   3505 while (reg && reg->signal_number < signal_number) 683   3507 while (reg && reg->signal_number < signal_number)
684   { 684   {
HITCBC 685   23 insertion_point = &reg->next_in_set; 685   23 insertion_point = &reg->next_in_set;
HITCBC 686   23 reg = reg->next_in_set; 686   23 reg = reg->next_in_set;
687   } 687   }
688   688  
689   // Already registered in this set - check flag compatibility 689   // Already registered in this set - check flag compatibility
690   // (same signal_set adding same signal twice with different flags) 690   // (same signal_set adding same signal twice with different flags)
HITCBC 691   3482 if (reg && reg->signal_number == signal_number) 691   3484 if (reg && reg->signal_number == signal_number)
692   { 692   {
HITCBC 693   13 if (!posix_signal_detail::flags_compatible(reg->flags, flags)) 693   13 if (!posix_signal_detail::flags_compatible(reg->flags, flags))
HITCBC 694   4 return make_error_code(std::errc::invalid_argument); 694   4 return make_error_code(std::errc::invalid_argument);
HITCBC 695   9 return {}; 695   9 return {};
696   } 696   }
697   697  
698   // Check flag compatibility with global registration 698   // Check flag compatibility with global registration
699   // (different signal_set already registered this signal with different flags) 699   // (different signal_set already registered this signal with different flags)
HITCBC 700   3469 if (state->registration_count[signal_number] > 0) 700   3471 if (state->registration_count[signal_number] > 0)
701   { 701   {
HITCBC 702   2828 if (!posix_signal_detail::flags_compatible( 702   2828 if (!posix_signal_detail::flags_compatible(
703   state->registered_flags[signal_number], flags)) 703   state->registered_flags[signal_number], flags))
HITCBC 704   2 return make_error_code(std::errc::invalid_argument); 704   2 return make_error_code(std::errc::invalid_argument);
705   } 705   }
706   706  
HITCBC 707   3467 auto* new_reg = new signal_registration; 707   3469 auto* new_reg = new signal_registration;
HITCBC 708   3467 new_reg->signal_number = signal_number; 708   3469 new_reg->signal_number = signal_number;
HITCBC 709   3467 new_reg->flags = flags; 709   3469 new_reg->flags = flags;
HITCBC 710   3467 new_reg->owner = &impl; 710   3469 new_reg->owner = &impl;
HITCBC 711   3467 new_reg->undelivered = 0; 711   3469 new_reg->undelivered = 0;
712   712  
713   // Install signal handler on first global registration 713   // Install signal handler on first global registration
HITCBC 714   3467 if (state->registration_count[signal_number] == 0) 714   3469 if (state->registration_count[signal_number] == 0)
715   { 715   {
HITCBC 716   641 struct sigaction sa = {}; 716   643 struct sigaction sa = {};
HITCBC 717   641 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; 717   643 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
HITCBC 718   641 sigemptyset(&sa.sa_mask); 718   643 sigemptyset(&sa.sa_mask);
HITCBC 719   641 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); 719   643 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
720   720  
HITCBC 721   641 if (::sigaction(signal_number, &sa, nullptr) < 0) 721   643 if (::sigaction(signal_number, &sa, nullptr) < 0)
722   { 722   {
HITCBC 723   1 delete new_reg; 723   1 delete new_reg;
HITCBC 724   1 return make_error_code(std::errc::invalid_argument); 724   1 return make_error_code(std::errc::invalid_argument);
725   } 725   }
726   726  
727   // Store the flags used for first registration 727   // Store the flags used for first registration
HITCBC 728   640 state->registered_flags[signal_number] = flags; 728   642 state->registered_flags[signal_number] = flags;
729   } 729   }
730   730  
HITCBC 731   3466 new_reg->next_in_set = reg; 731   3468 new_reg->next_in_set = reg;
HITCBC 732   3466 *insertion_point = new_reg; 732   3468 *insertion_point = new_reg;
733   733  
HITCBC 734   3466 new_reg->next_in_table = registrations_[signal_number]; 734   3468 new_reg->next_in_table = registrations_[signal_number];
HITCBC 735   3466 new_reg->prev_in_table = nullptr; 735   3468 new_reg->prev_in_table = nullptr;
HITCBC 736   3466 if (registrations_[signal_number]) 736   3468 if (registrations_[signal_number])
HITCBC 737   2818 registrations_[signal_number]->prev_in_table = new_reg; 737   2818 registrations_[signal_number]->prev_in_table = new_reg;
HITCBC 738   3466 registrations_[signal_number] = new_reg; 738   3468 registrations_[signal_number] = new_reg;
739   739  
HITCBC 740   3466 ++state->registration_count[signal_number]; 740   3468 ++state->registration_count[signal_number];
HITCBC 741   3466 ++registration_count_[signal_number]; 741   3468 ++registration_count_[signal_number];
742   742  
HITCBC 743   3466 return {}; 743   3468 return {};
HITCBC 744   3482 } 744   3484 }
745   745  
746   inline std::error_code 746   inline std::error_code
HITCBC 747   26 posix_signal_service::remove_signal(posix_signal& impl, int signal_number) 747   26 posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
748   { 748   {
HITCBC 749   26 if (signal_number < 0 || signal_number >= max_signal_number) 749   26 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 750   2 return make_error_code(std::errc::invalid_argument); 750   2 return make_error_code(std::errc::invalid_argument);
751   751  
752   posix_signal_detail::signal_state* state = 752   posix_signal_detail::signal_state* state =
HITCBC 753   24 posix_signal_detail::get_signal_state(); 753   24 posix_signal_detail::get_signal_state();
HITCBC 754   24 std::lock_guard state_lock(state->mutex); 754   24 std::lock_guard state_lock(state->mutex);
HITCBC 755   24 std::lock_guard lock(mutex_); 755   24 std::lock_guard lock(mutex_);
756   756  
HITCBC 757   24 signal_registration** deletion_point = &impl.signals_; 757   24 signal_registration** deletion_point = &impl.signals_;
HITCBC 758   24 signal_registration* reg = impl.signals_; 758   24 signal_registration* reg = impl.signals_;
HITCBC 759   26 while (reg && reg->signal_number < signal_number) 759   26 while (reg && reg->signal_number < signal_number)
760   { 760   {
HITCBC 761   2 deletion_point = &reg->next_in_set; 761   2 deletion_point = &reg->next_in_set;
HITCBC 762   2 reg = reg->next_in_set; 762   2 reg = reg->next_in_set;
763   } 763   }
764   764  
HITCBC 765   24 if (!reg || reg->signal_number != signal_number) 765   24 if (!reg || reg->signal_number != signal_number)
HITCBC 766   3 return {}; 766   3 return {};
767   767  
768   // Restore default handler on last global unregistration 768   // Restore default handler on last global unregistration
HITCBC 769   21 if (state->registration_count[signal_number] == 1) 769   21 if (state->registration_count[signal_number] == 1)
770   { 770   {
HITCBC 771   17 struct sigaction sa = {}; 771   17 struct sigaction sa = {};
HITCBC 772   17 sa.sa_handler = SIG_DFL; 772   17 sa.sa_handler = SIG_DFL;
HITCBC 773   17 sigemptyset(&sa.sa_mask); 773   17 sigemptyset(&sa.sa_mask);
HITCBC 774   17 sa.sa_flags = 0; 774   17 sa.sa_flags = 0;
775   775  
HITCBC 776   17 if (::sigaction(signal_number, &sa, nullptr) < 0) 776   17 if (::sigaction(signal_number, &sa, nullptr) < 0)
HITCBC 777   1 return make_error_code(std::errc::invalid_argument); 777   1 return make_error_code(std::errc::invalid_argument);
778   778  
779   // Clear stored flags 779   // Clear stored flags
HITCBC 780   16 state->registered_flags[signal_number] = signal_set::none; 780   16 state->registered_flags[signal_number] = signal_set::none;
781   } 781   }
782   782  
HITCBC 783   20 *deletion_point = reg->next_in_set; 783   20 *deletion_point = reg->next_in_set;
784   784  
HITCBC 785   20 if (registrations_[signal_number] == reg) 785   20 if (registrations_[signal_number] == reg)
HITCBC 786   18 registrations_[signal_number] = reg->next_in_table; 786   18 registrations_[signal_number] = reg->next_in_table;
HITCBC 787   20 if (reg->prev_in_table) 787   20 if (reg->prev_in_table)
HITCBC 788   2 reg->prev_in_table->next_in_table = reg->next_in_table; 788   2 reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 789   20 if (reg->next_in_table) 789   20 if (reg->next_in_table)
HITCBC 790   2 reg->next_in_table->prev_in_table = reg->prev_in_table; 790   2 reg->next_in_table->prev_in_table = reg->prev_in_table;
791   791  
HITCBC 792   20 --state->registration_count[signal_number]; 792   20 --state->registration_count[signal_number];
HITCBC 793   20 --registration_count_[signal_number]; 793   20 --registration_count_[signal_number];
794   794  
HITCBC 795   20 delete reg; 795   20 delete reg;
HITCBC 796   20 return {}; 796   20 return {};
HITCBC 797   24 } 797   24 }
798   798  
799   inline std::error_code 799   inline std::error_code
HITCBC 800   3490 posix_signal_service::clear_signals(posix_signal& impl) 800   3492 posix_signal_service::clear_signals(posix_signal& impl)
801   { 801   {
802   posix_signal_detail::signal_state* state = 802   posix_signal_detail::signal_state* state =
HITCBC 803   3490 posix_signal_detail::get_signal_state(); 803   3492 posix_signal_detail::get_signal_state();
HITCBC 804   3490 std::lock_guard state_lock(state->mutex); 804   3492 std::lock_guard state_lock(state->mutex);
HITCBC 805   3490 std::lock_guard lock(mutex_); 805   3492 std::lock_guard lock(mutex_);
806   806  
HITCBC 807   3490 std::error_code first_error; 807   3492 std::error_code first_error;
808   808  
HITCBC 809   3726 while (signal_registration* reg = impl.signals_) 809   3730 while (signal_registration* reg = impl.signals_)
810   { 810   {
HITCBC 811   236 int signal_number = reg->signal_number; 811   238 int signal_number = reg->signal_number;
812   812  
HITCBC 813   236 if (state->registration_count[signal_number] == 1) 813   238 if (state->registration_count[signal_number] == 1)
814   { 814   {
HITCBC 815   216 struct sigaction sa = {}; 815   218 struct sigaction sa = {};
HITCBC 816   216 sa.sa_handler = SIG_DFL; 816   218 sa.sa_handler = SIG_DFL;
HITCBC 817   216 sigemptyset(&sa.sa_mask); 817   218 sigemptyset(&sa.sa_mask);
HITCBC 818   216 sa.sa_flags = 0; 818   218 sa.sa_flags = 0;
819   819  
HITCBC 820   216 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) 820   218 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
HITCBC 821   1 first_error = make_error_code(std::errc::invalid_argument); 821   1 first_error = make_error_code(std::errc::invalid_argument);
822   822  
823   // Clear stored flags 823   // Clear stored flags
HITCBC 824   216 state->registered_flags[signal_number] = signal_set::none; 824   218 state->registered_flags[signal_number] = signal_set::none;
825   } 825   }
826   826  
HITCBC 827   236 impl.signals_ = reg->next_in_set; 827   238 impl.signals_ = reg->next_in_set;
828   828  
HITCBC 829   236 if (registrations_[signal_number] == reg) 829   238 if (registrations_[signal_number] == reg)
HITCBC 830   234 registrations_[signal_number] = reg->next_in_table; 830   236 registrations_[signal_number] = reg->next_in_table;
HITCBC 831   236 if (reg->prev_in_table) 831   238 if (reg->prev_in_table)
HITCBC 832   2 reg->prev_in_table->next_in_table = reg->next_in_table; 832   2 reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 833   236 if (reg->next_in_table) 833   238 if (reg->next_in_table)
HITCBC 834   12 reg->next_in_table->prev_in_table = reg->prev_in_table; 834   12 reg->next_in_table->prev_in_table = reg->prev_in_table;
835   835  
HITCBC 836   236 --state->registration_count[signal_number]; 836   238 --state->registration_count[signal_number];
HITCBC 837   236 --registration_count_[signal_number]; 837   238 --registration_count_[signal_number];
838   838  
HITCBC 839   236 delete reg; 839   238 delete reg;
HITCBC 840   236 } 840   238 }
841   841  
HITCBC 842   3490 if (first_error) 842   3492 if (first_error)
HITCBC 843   1 return first_error; 843   1 return first_error;
HITCBC 844   3489 return {}; 844   3491 return {};
HITCBC 845   3490 } 845   3492 }
846   846  
847   inline void 847   inline void
HITCBC 848   203497 posix_signal_service::cancel_wait(posix_signal& impl) noexcept 848   203499 posix_signal_service::cancel_wait(posix_signal& impl) noexcept
849   { 849   {
HITCBC 850   203497 bool was_waiting = false; 850   203499 bool was_waiting = false;
HITCBC 851   203497 signal_op* op = nullptr; 851   203499 signal_op* op = nullptr;
852   852  
853   { 853   {
HITCBC 854   203497 std::lock_guard lock(mutex_); 854   203499 std::lock_guard lock(mutex_);
HITCBC 855   203497 impl.cancelled_ = true; 855   203499 impl.cancelled_ = true;
HITCBC 856   203497 if (impl.waiting_) 856   203499 if (impl.waiting_)
857   { 857   {
HITCBC 858   11 was_waiting = true; 858   11 was_waiting = true;
HITCBC 859   11 impl.waiting_ = false; 859   11 impl.waiting_ = false;
HITCBC 860   11 op = &impl.pending_op_; 860   11 op = &impl.pending_op_;
HITCBC 861   11 sched_->cancel_posting_begin(); 861   11 sched_->cancel_posting_begin();
862   } 862   }
HITCBC 863   203497 } 863   203499 }
864   864  
HITCBC 865   203497 if (was_waiting) 865   203499 if (was_waiting)
866   { 866   {
HITCBC 867   11 if (op->ec_out) 867   11 if (op->ec_out)
HITCBC 868   11 *op->ec_out = make_error_code(capy::error::canceled); 868   11 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 869   11 if (op->signal_out) 869   11 if (op->signal_out)
HITCBC 870   11 *op->signal_out = 0; 870   11 *op->signal_out = 0;
HITCBC 871   11 op->d.post(*op->cont); 871   11 op->d.post(*op->cont);
HITCBC 872   11 sched_->work_finished(); 872   11 sched_->work_finished();
HITCBC 873   11 sched_->cancel_posting_end(); 873   11 sched_->cancel_posting_end();
874   } 874   }
HITCBC 875   203497 } 875   203499 }
876   876  
877   inline void 877   inline void
HITCBC 878   1087 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept 878   994 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept
879   { 879   {
HITCBC 880   1087 bool was_waiting = false; 880   994 bool was_waiting = false;
HITCBC 881   1087 signal_op* op = nullptr; 881   994 signal_op* op = nullptr;
882   882  
883   { 883   {
HITCBC 884   1087 std::lock_guard lock(mutex_); 884   994 std::lock_guard lock(mutex_);
885   // Persist the request even when no wait is parked yet: wait() 885   // Persist the request even when no wait is parked yet: wait()
886   // arms the callback before start_wait takes this lock, and 886   // arms the callback before start_wait takes this lock, and
887   // start_wait consumes this flag. 887   // start_wait consumes this flag.
HITCBC 888   1087 impl.token_cancelled_ = true; 888   994 impl.token_cancelled_ = true;
HITCBC 889   1087 if (impl.waiting_) 889   994 if (impl.waiting_)
890   { 890   {
HITCBC 891   547 was_waiting = true; 891   497 was_waiting = true;
HITCBC 892   547 impl.waiting_ = false; 892   497 impl.waiting_ = false;
HITCBC 893   547 op = &impl.pending_op_; 893   497 op = &impl.pending_op_;
HITCBC 894   547 sched_->cancel_posting_begin(); 894   497 sched_->cancel_posting_begin();
895   } 895   }
HITCBC 896   1087 } 896   994 }
897   897  
HITCBC 898   1087 if (was_waiting) 898   994 if (was_waiting)
899   { 899   {
HITCBC 900   547 if (op->ec_out) 900   497 if (op->ec_out)
HITCBC 901   547 *op->ec_out = make_error_code(capy::error::canceled); 901   497 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 902   547 if (op->signal_out) 902   497 if (op->signal_out)
HITCBC 903   547 *op->signal_out = 0; 903   497 *op->signal_out = 0;
HITCBC 904   547 op->d.post(*op->cont); 904   497 op->d.post(*op->cont);
HITCBC 905   547 sched_->work_finished(); 905   497 sched_->work_finished();
HITCBC 906   547 sched_->cancel_posting_end(); 906   497 sched_->cancel_posting_end();
907   } 907   }
HITCBC 908   1087 } 908   994 }
909   909  
910   inline void 910   inline void
HITCBC 911   1087 posix_signal::token_canceller::operator()() const noexcept 911   994 posix_signal::token_canceller::operator()() const noexcept
912   { 912   {
HITCBC 913   1087 self->svc_.cancel_wait_token(*self); 913   994 self->svc_.cancel_wait_token(*self);
HITCBC 914   1087 } 914   994 }
915   915  
916   inline void 916   inline void
HITCBC 917   205877 posix_signal_service::start_wait(posix_signal& impl, signal_op* op) 917   205719 posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
918   { 918   {
919   { 919   {
HITCBC 920   205877 std::lock_guard lock(mutex_); 920   205719 std::lock_guard lock(mutex_);
921   921  
922   // Check if cancel() was called before this wait started 922   // Check if cancel() was called before this wait started
HITCBC 923   205877 if (impl.cancelled_) 923   205719 if (impl.cancelled_)
924   { 924   {
HITCBC 925   200002 impl.cancelled_ = false; 925   200002 impl.cancelled_ = false;
HITCBC 926   200002 if (op->ec_out) 926   200002 if (op->ec_out)
HITCBC 927   200002 *op->ec_out = make_error_code(capy::error::canceled); 927   200002 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 928   200002 if (op->signal_out) 928   200002 if (op->signal_out)
HITCBC 929   200002 *op->signal_out = 0; 929   200002 *op->signal_out = 0;
HITCBC 930   200002 op->d.post(*op->cont); 930   200002 op->d.post(*op->cont);
HITCBC 931   200002 return; 931   200002 return;
932   } 932   }
933   933  
934   // A stop request that arrived between wait() arming the callback 934   // A stop request that arrived between wait() arming the callback
935   // and this lock: complete now rather than parking forever. 935   // and this lock: complete now rather than parking forever.
HITCBC 936   5875 if (impl.token_cancelled_) 936   5717 if (impl.token_cancelled_)
937   { 937   {
HITCBC 938   110 impl.token_cancelled_ = false; 938   146 impl.token_cancelled_ = false;
HITCBC 939   110 if (op->ec_out) 939   146 if (op->ec_out)
HITCBC 940   110 *op->ec_out = make_error_code(capy::error::canceled); 940   146 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 941   110 if (op->signal_out) 941   146 if (op->signal_out)
HITCBC 942   110 *op->signal_out = 0; 942   146 *op->signal_out = 0;
HITCBC 943   110 op->d.post(*op->cont); 943   146 op->d.post(*op->cont);
HITCBC 944   110 return; 944   146 return;
945   } 945   }
946   946  
947   // Check for queued signals first (signal arrived before wait started) 947   // Check for queued signals first (signal arrived before wait started)
HITCBC 948   5765 signal_registration* reg = impl.signals_; 948   5571 signal_registration* reg = impl.signals_;
HITCBC 949   10288 while (reg) 949   10031 while (reg)
950   { 950   {
HITCBC 951   5769 if (reg->undelivered > 0) 951   5575 if (reg->undelivered > 0)
952   { 952   {
HITCBC 953   1246 --reg->undelivered; 953   1115 --reg->undelivered;
HITCBC 954   1246 op->signal_number = reg->signal_number; 954   1115 op->signal_number = reg->signal_number;
955   // svc=nullptr: no work_finished needed since we never called work_started 955   // svc=nullptr: no work_finished needed since we never called work_started
HITCBC 956   1246 op->svc = nullptr; 956   1115 op->svc = nullptr;
957   // The caller's wait() holds the handle's reference. 957   // The caller's wait() holds the handle's reference.
HITCBC 958   1246 op->keep = detail::object_ref(&impl); 958   1115 op->keep = detail::object_ref(&impl);
HITCBC 959   1246 sched_->post(op); 959   1115 sched_->post(op);
HITCBC 960   1246 return; 960   1115 return;
961   } 961   }
HITCBC 962   4523 reg = reg->next_in_set; 962   4460 reg = reg->next_in_set;
963   } 963   }
964   964  
965   // No queued signals - wait for delivery 965   // No queued signals - wait for delivery
HITCBC 966   4519 impl.waiting_ = true; 966   4456 impl.waiting_ = true;
967   // svc=this: signal_op::operator() will call work_finished() to balance this 967   // svc=this: signal_op::operator() will call work_finished() to balance this
HITCBC 968   4519 op->svc = this; 968   4456 op->svc = this;
HITCBC 969   4519 sched_->work_started(); 969   4456 sched_->work_started();
HITCBC 970   205877 } 970   205719 }
971   } 971   }
972   972  
973   inline void 973   inline void
HITCBC 974   1521 posix_signal_service::deliver_signal(int signal_number) 974   1521 posix_signal_service::deliver_signal(int signal_number)
975   { 975   {
HITCBC 976   1521 if (signal_number < 0 || signal_number >= max_signal_number) 976   1521 if (signal_number < 0 || signal_number >= max_signal_number)
MISUBC 977   ✗ return; 977   ✗ return;
978   978  
979   posix_signal_detail::signal_state* state = 979   posix_signal_detail::signal_state* state =
HITCBC 980   1521 posix_signal_detail::get_signal_state(); 980   1521 posix_signal_detail::get_signal_state();
HITCBC 981   1521 std::lock_guard lock(state->mutex); 981   1521 std::lock_guard lock(state->mutex);
982   982  
HITCBC 983   1521 posix_signal_service* service = state->service_list; 983   1521 posix_signal_service* service = state->service_list;
HITCBC 984   3842 while (service) 984   3842 while (service)
985   { 985   {
HITCBC 986   2321 std::lock_guard svc_lock(service->mutex_); 986   2321 std::lock_guard svc_lock(service->mutex_);
987   987  
HITCBC 988   2321 signal_registration* reg = service->registrations_[signal_number]; 988   2321 signal_registration* reg = service->registrations_[signal_number];
HITCBC 989   4646 while (reg) 989   4646 while (reg)
990   { 990   {
HITCBC 991   2325 posix_signal* impl = static_cast<posix_signal*>(reg->owner); 991   2325 posix_signal* impl = static_cast<posix_signal*>(reg->owner);
992   992  
HITCBC 993   2325 if (impl->waiting_) 993   2325 if (impl->waiting_)
994   { 994   {
995   // Reached through the registry, not a held reference. 995   // Reached through the registry, not a held reference.
996   // A set whose count reached zero is being destroyed, 996   // A set whose count reached zero is being destroyed,
997   // and that destruction completes its wait itself. 997   // and that destruction completes its wait itself.
HITCBC 998   751 auto keep = detail::object_ref::try_from(impl); 998   738 auto keep = detail::object_ref::try_from(impl);
HITCBC 999   751 if (keep) 999   738 if (keep)
1000   { 1000   {
HITCBC 1001   751 impl->waiting_ = false; 1001   738 impl->waiting_ = false;
HITCBC 1002   751 impl->pending_op_.signal_number = signal_number; 1002   738 impl->pending_op_.signal_number = signal_number;
HITCBC 1003   751 impl->pending_op_.keep = std::move(keep); 1003   738 impl->pending_op_.keep = std::move(keep);
HITCBC 1004   751 service->post(&impl->pending_op_); 1004   738 service->post(&impl->pending_op_);
1005   } 1005   }
HITCBC 1006   751 } 1006   738 }
1007   else 1007   else
1008   { 1008   {
HITCBC 1009   1574 ++reg->undelivered; 1009   1587 ++reg->undelivered;
1010   } 1010   }
1011   1011  
HITCBC 1012   2325 reg = reg->next_in_table; 1012   2325 reg = reg->next_in_table;
1013   } 1013   }
1014   1014  
HITCBC 1015   2321 service = service->next_; 1015   2321 service = service->next_;
HITCBC 1016   2321 } 1016   2321 }
HITCBC 1017   1521 } 1017   1521 }
1018   1018  
1019   inline void 1019   inline void
1020   posix_signal_service::work_started() noexcept 1020   posix_signal_service::work_started() noexcept
1021   { 1021   {
1022   sched_->work_started(); 1022   sched_->work_started();
1023   } 1023   }
1024   1024  
1025   inline void 1025   inline void
HITCBC 1026   751 posix_signal_service::work_finished() noexcept 1026   738 posix_signal_service::work_finished() noexcept
1027   { 1027   {
HITCBC 1028   751 sched_->work_finished(); 1028   738 sched_->work_finished();
HITCBC 1029   751 } 1029   738 }
1030   1030  
1031   inline void 1031   inline void
HITCBC 1032   751 posix_signal_service::post(signal_op* op) 1032   738 posix_signal_service::post(signal_op* op)
1033   { 1033   {
HITCBC 1034   751 sched_->post(op); 1034   738 sched_->post(op);
HITCBC 1035   751 } 1035   738 }
1036   1036  
1037   inline void 1037   inline void
HITCBC 1038   583 posix_signal_service::add_service(posix_signal_service* service) 1038   585 posix_signal_service::add_service(posix_signal_service* service)
1039   { 1039   {
1040   posix_signal_detail::signal_state* state = 1040   posix_signal_detail::signal_state* state =
HITCBC 1041   583 posix_signal_detail::get_signal_state(); 1041   585 posix_signal_detail::get_signal_state();
HITCBC 1042   583 std::lock_guard lock(state->mutex); 1042   585 std::lock_guard lock(state->mutex);
1043   1043  
HITCBC 1044   583 service->next_ = state->service_list; 1044   585 service->next_ = state->service_list;
HITCBC 1045   583 service->prev_ = nullptr; 1045   585 service->prev_ = nullptr;
HITCBC 1046   583 if (state->service_list) 1046   585 if (state->service_list)
HITCBC 1047   10 state->service_list->prev_ = service; 1047   10 state->service_list->prev_ = service;
HITCBC 1048   583 state->service_list = service; 1048   585 state->service_list = service;
HITCBC 1049   583 } 1049   585 }
1050   1050  
1051   inline void 1051   inline void
HITCBC 1052   583 posix_signal_service::remove_service(posix_signal_service* service) 1052   585 posix_signal_service::remove_service(posix_signal_service* service)
1053   { 1053   {
1054   posix_signal_detail::signal_state* state = 1054   posix_signal_detail::signal_state* state =
HITCBC 1055   583 posix_signal_detail::get_signal_state(); 1055   585 posix_signal_detail::get_signal_state();
HITCBC 1056   583 std::lock_guard lock(state->mutex); 1056   585 std::lock_guard lock(state->mutex);
1057   1057  
HITCBC 1058   583 if (service->next_ || service->prev_ || state->service_list == service) 1058   585 if (service->next_ || service->prev_ || state->service_list == service)
1059   { 1059   {
HITCBC 1060   583 if (state->service_list == service) 1060   585 if (state->service_list == service)
HITCBC 1061   581 state->service_list = service->next_; 1061   583 state->service_list = service->next_;
HITCBC 1062   583 if (service->prev_) 1062   585 if (service->prev_)
HITCBC 1063   2 service->prev_->next_ = service->next_; 1063   2 service->prev_->next_ = service->next_;
HITCBC 1064   583 if (service->next_) 1064   585 if (service->next_)
HITCBC 1065   8 service->next_->prev_ = service->prev_; 1065   8 service->next_->prev_ = service->prev_;
HITCBC 1066   583 service->next_ = nullptr; 1066   585 service->next_ = nullptr;
HITCBC 1067   583 service->prev_ = nullptr; 1067   585 service->prev_ = nullptr;
1068   } 1068   }
HITCBC 1069   583 } 1069   585 }
1070   1070  
1071   } // namespace detail 1071   } // namespace detail
1072   } // namespace boost::corosio 1072   } // namespace boost::corosio
1073   1073  
1074   #endif // BOOST_COROSIO_POSIX 1074   #endif // BOOST_COROSIO_POSIX
1075   1075  
1076   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 1076   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP