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   3476 void destroy(io_object::implementation* p) override
178   { 178   {
HITCBC 179   3476 auto& impl = static_cast<posix_signal&>(*p); 179   3476 auto& impl = static_cast<posix_signal&>(*p);
HITCBC 180   3476 [[maybe_unused]] auto n = impl.clear(); 180   3476 [[maybe_unused]] auto n = impl.clear();
HITCBC 181   3476 impl.disarm_stop(); 181   3476 impl.disarm_stop();
HITCBC 182   3476 impl.cancel(); 182   3476 impl.cancel();
HITCBC 183   3476 release(&impl); 183   3476 release(&impl);
HITCBC 184   3476 } 184   3476 }
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   205742 void reset_token_cancel(posix_signal& impl) noexcept 219   205897 void reset_token_cancel(posix_signal& impl) noexcept
220   { 220   {
HITCBC 221   205742 std::lock_guard lock(mutex_); 221   205897 std::lock_guard lock(mutex_);
HITCBC 222   205742 impl.token_cancelled_ = false; 222   205897 impl.token_cancelled_ = false;
HITCBC 223   205742 } 223   205897 }
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   3491 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   3491 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   641 to_sigaction_flags(signal_set::flags_t flags)
309   { 309   {
HITCBC 310   641 int sa_flags = 0; 310   641 int sa_flags = 0;
HITCBC 311   641 if (flags & signal_set::restart) 311   641 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   641 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   641 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   641 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   641 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   641 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   3491 open_signal_pipe(signal_state* state)
349   { 349   {
HITCBC 350   3491 if (state->read_fd >= 0) 350   3491 if (state->read_fd >= 0)
HITCBC 351   3475 return {}; 351   3475 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   964 drain_signal_pipe() 398   941 drain_signal_pipe()
399   { 399   {
HITCBC 400   964 signal_state* state = get_signal_state(); 400   941 signal_state* state = get_signal_state();
401   int signal_number; 401   int signal_number;
HITCBC 402   2485 while (::read(state->read_fd, &signal_number, sizeof(int)) == 402   2462 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   964 } 407   941 }
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   1791 signal_op::operator()() noexcept 414   1982 signal_op::operator()() noexcept
415   { 415   {
HITCBC 416   1791 if (ec_out) 416   1982 if (ec_out)
HITCBC 417   1791 *ec_out = {}; 417   1982 *ec_out = {};
HITCBC 418   1791 if (signal_out) 418   1982 if (signal_out)
HITCBC 419   1791 *signal_out = signal_number; 419   1982 *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   1791 auto* service = svc; 423   1982 auto* service = svc;
HITCBC 424   1791 svc = nullptr; 424   1982 svc = nullptr;
HITCBC 425   1791 auto* c = cont; 425   1982 auto* c = cont;
HITCBC 426   1791 auto ex = d; 426   1982 auto ex = d;
HITCBC 427   1791 auto k = std::move(keep); 427   1982 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   1791 ex.post(*c); 431   1982 ex.post(*c);
432   432  
433   // Balance the work_started() from start_wait 433   // Balance the work_started() from start_wait
HITCBC 434   1791 if (service) 434   1982 if (service)
HITCBC 435   738 service->work_finished(); 435   792 service->work_finished();
HITCBC 436   1791 } 436   1982 }
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   3404 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
HITCBC 452   3404 : svc_(svc) 452   3404 : svc_(svc)
453   { 453   {
HITCBC 454   3404 } 454   3404 }
455   455  
456   inline std::coroutine_handle<> 456   inline std::coroutine_handle<>
HITCBC 457   205800 posix_signal::wait( 457   205987 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   205800 pending_op_.cont = &cont; 464   205987 pending_op_.cont = &cont;
HITCBC 465   205800 pending_op_.d = d; 465   205987 pending_op_.d = d;
HITCBC 466   205800 pending_op_.ec_out = ec; 466   205987 pending_op_.ec_out = ec;
HITCBC 467   205800 pending_op_.signal_out = signal_out; 467   205987 pending_op_.signal_out = signal_out;
HITCBC 468   205800 pending_op_.signal_number = 0; 468   205987 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   205800 stop_cb_.reset(); 476   205987 stop_cb_.reset();
477   477  
HITCBC 478   205800 if (token.stop_requested()) 478   205987 if (token.stop_requested())
479   { 479   {
HITCBC 480   58 if (ec) 480   90 if (ec)
HITCBC 481   58 *ec = make_error_code(capy::error::canceled); 481   90 *ec = make_error_code(capy::error::canceled);
HITCBC 482   58 if (signal_out) 482   90 if (signal_out)
HITCBC 483   58 *signal_out = 0; 483   90 *signal_out = 0;
HITCBC 484   58 d.post(*pending_op_.cont); 484   90 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   58 return std::noop_coroutine(); 486   90 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   205742 svc_.reset_token_cancel(*this); 492   205897 svc_.reset_token_cancel(*this);
HITCBC 493   205742 if (token.stop_possible()) 493   205897 if (token.stop_possible())
HITCBC 494   5000 stop_cb_.emplace(token, token_canceller{this}); 494   5155 stop_cb_.emplace(token, token_canceller{this});
495   495  
HITCBC 496   205742 svc_.start_wait(*this, &pending_op_); 496   205897 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   205742 return std::noop_coroutine(); 498   205897 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   3495 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   3495 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   3490 posix_signal::clear()
515   { 515   {
HITCBC 516   3490 return svc_.clear_signals(*this); 516   3490 return svc_.clear_signals(*this);
517   } 517   }
518   518  
519   inline void 519   inline void
HITCBC 520   203497 posix_signal::cancel() noexcept 520   203497 posix_signal::cancel() noexcept
521   { 521   {
HITCBC 522   203497 svc_.cancel_wait(*this); 522   203497 svc_.cancel_wait(*this);
HITCBC 523   203497 } 523   203497 }
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   583 inline posix_signal_service::posix_signal_service(
HITCBC 528   583 capy::execution_context& ctx) 528   583 capy::execution_context& ctx)
HITCBC 529   583 : sched_(&get_scheduler(ctx)) 529   583 : sched_(&get_scheduler(ctx))
530   { 530   {
HITCBC 531   37895 for (int i = 0; i < max_signal_number; ++i) 531   37895 for (int i = 0; i < max_signal_number; ++i)
532   { 532   {
HITCBC 533   37312 registrations_[i] = nullptr; 533   37312 registrations_[i] = nullptr;
HITCBC 534   37312 registration_count_[i] = 0; 534   37312 registration_count_[i] = 0;
535   } 535   }
HITCBC 536   583 add_service(this); 536   583 add_service(this);
HITCBC 537   583 } 537   583 }
538   538  
HITCBC 539   1166 inline posix_signal_service::~posix_signal_service() 539   1166 inline posix_signal_service::~posix_signal_service()
540   { 540   {
HITCBC 541   583 remove_service(this); 541   583 remove_service(this);
HITCBC 542   1166 } 542   1166 }
543   543  
544   inline void 544   inline void
HITCBC 545   583 posix_signal_service::shutdown() 545   583 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   583 std::vector<posix_signal*> doomed;
HITCBC 554   583 std::vector<capy::continuation> parked; 554   583 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   583 posix_signal_detail::get_signal_state();
HITCBC 559   583 std::lock_guard state_lock(state->mutex); 559   583 std::lock_guard state_lock(state->mutex);
HITCBC 560   583 std::lock_guard lock(mutex_); 560   583 std::lock_guard lock(mutex_);
561   561  
HITCBC 562   583 pool_.shutdown( 562   583 pool_.shutdown(
HITCBC 563   583 [&](posix_signal* impl) 563   583 [&](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   3793 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   37895 for (int i = 0; i < max_signal_number; ++i)
HITCBC 615   37312 registrations_[i] = nullptr; 615   37312 registrations_[i] = nullptr;
HITCBC 616   583 } 616   583 }
617   617  
HITCBC 618   3793 for (auto& c : parked) 618   3793 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   3793 for (auto* impl : doomed)
HITCBC 622   3210 release(impl); 622   3210 release(impl);
HITCBC 623   583 } 623   583 }
624   624  
625   inline io_object::implementation* 625   inline io_object::implementation*
HITCBC 626   3476 posix_signal_service::construct() 626   3476 posix_signal_service::construct()
627   { 627   {
HITCBC 628   3476 return pool_.acquire(*this); 628   3476 return pool_.acquire(*this);
629   } 629   }
630   630  
631   inline void 631   inline void
HITCBC 632   3476 posix_signal::retire() noexcept 632   3476 posix_signal::retire() noexcept
633   { 633   {
HITCBC 634   3476 svc_.pool_.recycle(this); 634   3476 svc_.pool_.recycle(this);
HITCBC 635   3476 } 635   3476 }
636   636  
637   inline std::error_code 637   inline std::error_code
HITCBC 638   3495 posix_signal_service::add_signal( 638   3495 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   3495 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   3491 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   3491 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   3491 std::lock_guard state_lock(state->mutex);
HITCBC 661   3491 if (auto ec = posix_signal_detail::open_signal_pipe(state)) 661   3491 if (auto ec = posix_signal_detail::open_signal_pipe(state))
HITCBC 662   7 return ec; 662   7 return ec;
HITCBC 663   3491 } 663   3491 }
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   3484 std::lock_guard reg_lock(reader_mutex_);
HITCBC 669   3484 if (!reader_registered_) 669   3484 if (!reader_registered_)
670   { 670   {
HITCBC 671   555 if (auto ec = sched_->register_signal_reader(state->read_fd)) 671   555 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   553 reader_registered_ = true;
674   } 674   }
HITCBC 675   3484 } 675   3484 }
676   676  
HITCBC 677   3482 std::lock_guard state_lock(state->mutex); 677   3482 std::lock_guard state_lock(state->mutex);
HITCBC 678   3482 std::lock_guard lock(mutex_); 678   3482 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   3482 signal_registration** insertion_point = &impl.signals_;
HITCBC 682   3482 signal_registration* reg = impl.signals_; 682   3482 signal_registration* reg = impl.signals_;
HITCBC 683   3505 while (reg && reg->signal_number < signal_number) 683   3505 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   3482 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   3469 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   3467 auto* new_reg = new signal_registration;
HITCBC 708   3467 new_reg->signal_number = signal_number; 708   3467 new_reg->signal_number = signal_number;
HITCBC 709   3467 new_reg->flags = flags; 709   3467 new_reg->flags = flags;
HITCBC 710   3467 new_reg->owner = &impl; 710   3467 new_reg->owner = &impl;
HITCBC 711   3467 new_reg->undelivered = 0; 711   3467 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   3467 if (state->registration_count[signal_number] == 0)
715   { 715   {
HITCBC 716   641 struct sigaction sa = {}; 716   641 struct sigaction sa = {};
HITCBC 717   641 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; 717   641 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
HITCBC 718   641 sigemptyset(&sa.sa_mask); 718   641 sigemptyset(&sa.sa_mask);
HITCBC 719   641 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); 719   641 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
720   720  
HITCBC 721   641 if (::sigaction(signal_number, &sa, nullptr) < 0) 721   641 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   640 state->registered_flags[signal_number] = flags;
729   } 729   }
730   730  
HITCBC 731   3466 new_reg->next_in_set = reg; 731   3466 new_reg->next_in_set = reg;
HITCBC 732   3466 *insertion_point = new_reg; 732   3466 *insertion_point = new_reg;
733   733  
HITCBC 734   3466 new_reg->next_in_table = registrations_[signal_number]; 734   3466 new_reg->next_in_table = registrations_[signal_number];
HITCBC 735   3466 new_reg->prev_in_table = nullptr; 735   3466 new_reg->prev_in_table = nullptr;
HITCBC 736   3466 if (registrations_[signal_number]) 736   3466 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   3466 registrations_[signal_number] = new_reg;
739   739  
HITCBC 740   3466 ++state->registration_count[signal_number]; 740   3466 ++state->registration_count[signal_number];
HITCBC 741   3466 ++registration_count_[signal_number]; 741   3466 ++registration_count_[signal_number];
742   742  
HITCBC 743   3466 return {}; 743   3466 return {};
HITCBC 744   3482 } 744   3482 }
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   3490 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   3490 posix_signal_detail::get_signal_state();
HITCBC 804   3490 std::lock_guard state_lock(state->mutex); 804   3490 std::lock_guard state_lock(state->mutex);
HITCBC 805   3490 std::lock_guard lock(mutex_); 805   3490 std::lock_guard lock(mutex_);
806   806  
HITCBC 807   3490 std::error_code first_error; 807   3490 std::error_code first_error;
808   808  
HITCBC 809   3726 while (signal_registration* reg = impl.signals_) 809   3726 while (signal_registration* reg = impl.signals_)
810   { 810   {
HITCBC 811   236 int signal_number = reg->signal_number; 811   236 int signal_number = reg->signal_number;
812   812  
HITCBC 813   236 if (state->registration_count[signal_number] == 1) 813   236 if (state->registration_count[signal_number] == 1)
814   { 814   {
HITCBC 815   216 struct sigaction sa = {}; 815   216 struct sigaction sa = {};
HITCBC 816   216 sa.sa_handler = SIG_DFL; 816   216 sa.sa_handler = SIG_DFL;
HITCBC 817   216 sigemptyset(&sa.sa_mask); 817   216 sigemptyset(&sa.sa_mask);
HITCBC 818   216 sa.sa_flags = 0; 818   216 sa.sa_flags = 0;
819   819  
HITCBC 820   216 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) 820   216 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   216 state->registered_flags[signal_number] = signal_set::none;
825   } 825   }
826   826  
HITCBC 827   236 impl.signals_ = reg->next_in_set; 827   236 impl.signals_ = reg->next_in_set;
828   828  
HITCBC 829   236 if (registrations_[signal_number] == reg) 829   236 if (registrations_[signal_number] == reg)
HITCBC 830   234 registrations_[signal_number] = reg->next_in_table; 830   234 registrations_[signal_number] = reg->next_in_table;
HITCBC 831   236 if (reg->prev_in_table) 831   236 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   236 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   236 --state->registration_count[signal_number];
HITCBC 837   236 --registration_count_[signal_number]; 837   236 --registration_count_[signal_number];
838   838  
HITCBC 839   236 delete reg; 839   236 delete reg;
HITCBC 840   236 } 840   236 }
841   841  
HITCBC 842   3490 if (first_error) 842   3490 if (first_error)
HITCBC 843   1 return first_error; 843   1 return first_error;
HITCBC 844   3489 return {}; 844   3489 return {};
HITCBC 845   3490 } 845   3490 }
846   846  
847   inline void 847   inline void
HITCBC 848   203497 posix_signal_service::cancel_wait(posix_signal& impl) noexcept 848   203497 posix_signal_service::cancel_wait(posix_signal& impl) noexcept
849   { 849   {
HITCBC 850   203497 bool was_waiting = false; 850   203497 bool was_waiting = false;
HITCBC 851   203497 signal_op* op = nullptr; 851   203497 signal_op* op = nullptr;
852   852  
853   { 853   {
HITCBC 854   203497 std::lock_guard lock(mutex_); 854   203497 std::lock_guard lock(mutex_);
HITCBC 855   203497 impl.cancelled_ = true; 855   203497 impl.cancelled_ = true;
HITCBC 856   203497 if (impl.waiting_) 856   203497 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   203497 }
864   864  
HITCBC 865   203497 if (was_waiting) 865   203497 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   203497 }
876   876  
877   inline void 877   inline void
HITCBC 878   1014 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept 878   961 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept
879   { 879   {
HITCBC 880   1014 bool was_waiting = false; 880   961 bool was_waiting = false;
HITCBC 881   1014 signal_op* op = nullptr; 881   961 signal_op* op = nullptr;
882   882  
883   { 883   {
HITCBC 884   1014 std::lock_guard lock(mutex_); 884   961 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   1014 impl.token_cancelled_ = true; 888   961 impl.token_cancelled_ = true;
HITCBC 889   1014 if (impl.waiting_) 889   961 if (impl.waiting_)
890   { 890   {
HITCBC 891   665 was_waiting = true; 891   610 was_waiting = true;
HITCBC 892   665 impl.waiting_ = false; 892   610 impl.waiting_ = false;
HITCBC 893   665 op = &impl.pending_op_; 893   610 op = &impl.pending_op_;
HITCBC 894   665 sched_->cancel_posting_begin(); 894   610 sched_->cancel_posting_begin();
895   } 895   }
HITCBC 896   1014 } 896   961 }
897   897  
HITCBC 898   1014 if (was_waiting) 898   961 if (was_waiting)
899   { 899   {
HITCBC 900   665 if (op->ec_out) 900   610 if (op->ec_out)
HITCBC 901   665 *op->ec_out = make_error_code(capy::error::canceled); 901   610 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 902   665 if (op->signal_out) 902   610 if (op->signal_out)
HITCBC 903   665 *op->signal_out = 0; 903   610 *op->signal_out = 0;
HITCBC 904   665 op->d.post(*op->cont); 904   610 op->d.post(*op->cont);
HITCBC 905   665 sched_->work_finished(); 905   610 sched_->work_finished();
HITCBC 906   665 sched_->cancel_posting_end(); 906   610 sched_->cancel_posting_end();
907   } 907   }
HITCBC 908   1014 } 908   961 }
909   909  
910   inline void 910   inline void
HITCBC 911   1014 posix_signal::token_canceller::operator()() const noexcept 911   961 posix_signal::token_canceller::operator()() const noexcept
912   { 912   {
HITCBC 913   1014 self->svc_.cancel_wait_token(*self); 913   961 self->svc_.cancel_wait_token(*self);
HITCBC 914   1014 } 914   961 }
915   915  
916   inline void 916   inline void
HITCBC 917   205742 posix_signal_service::start_wait(posix_signal& impl, signal_op* op) 917   205897 posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
918   { 918   {
919   { 919   {
HITCBC 920   205742 std::lock_guard lock(mutex_); 920   205897 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   205742 if (impl.cancelled_) 923   205897 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   5740 if (impl.token_cancelled_) 936   5895 if (impl.token_cancelled_)
937   { 937   {
HITCBC 938   63 impl.token_cancelled_ = false; 938   82 impl.token_cancelled_ = false;
HITCBC 939   63 if (op->ec_out) 939   82 if (op->ec_out)
HITCBC 940   63 *op->ec_out = make_error_code(capy::error::canceled); 940   82 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 941   63 if (op->signal_out) 941   82 if (op->signal_out)
HITCBC 942   63 *op->signal_out = 0; 942   82 *op->signal_out = 0;
HITCBC 943   63 op->d.post(*op->cont); 943   82 op->d.post(*op->cont);
HITCBC 944   63 return; 944   82 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   5677 signal_registration* reg = impl.signals_; 948   5813 signal_registration* reg = impl.signals_;
HITCBC 949   10305 while (reg) 949   10440 while (reg)
950   { 950   {
HITCBC 951   5681 if (reg->undelivered > 0) 951   5817 if (reg->undelivered > 0)
952   { 952   {
HITCBC 953   1053 --reg->undelivered; 953   1190 --reg->undelivered;
HITCBC 954   1053 op->signal_number = reg->signal_number; 954   1190 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   1053 op->svc = nullptr; 956   1190 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   1053 op->keep = detail::object_ref(&impl); 958   1190 op->keep = detail::object_ref(&impl);
HITCBC 959   1053 sched_->post(op); 959   1190 sched_->post(op);
HITCBC 960   1053 return; 960   1190 return;
961   } 961   }
HITCBC 962   4628 reg = reg->next_in_set; 962   4627 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   4624 impl.waiting_ = true; 966   4623 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   4624 op->svc = this; 968   4623 op->svc = this;
HITCBC 969   4624 sched_->work_started(); 969   4623 sched_->work_started();
HITCBC 970   205742 } 970   205897 }
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   738 auto keep = detail::object_ref::try_from(impl); 998   792 auto keep = detail::object_ref::try_from(impl);
HITCBC 999   738 if (keep) 999   792 if (keep)
1000   { 1000   {
HITCBC 1001   738 impl->waiting_ = false; 1001   792 impl->waiting_ = false;
HITCBC 1002   738 impl->pending_op_.signal_number = signal_number; 1002   792 impl->pending_op_.signal_number = signal_number;
HITCBC 1003   738 impl->pending_op_.keep = std::move(keep); 1003   792 impl->pending_op_.keep = std::move(keep);
HITCBC 1004   738 service->post(&impl->pending_op_); 1004   792 service->post(&impl->pending_op_);
1005   } 1005   }
HITCBC 1006   738 } 1006   792 }
1007   else 1007   else
1008   { 1008   {
HITCBC 1009   1587 ++reg->undelivered; 1009   1533 ++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   738 posix_signal_service::work_finished() noexcept 1026   792 posix_signal_service::work_finished() noexcept
1027   { 1027   {
HITCBC 1028   738 sched_->work_finished(); 1028   792 sched_->work_finished();
HITCBC 1029   738 } 1029   792 }
1030   1030  
1031   inline void 1031   inline void
HITCBC 1032   738 posix_signal_service::post(signal_op* op) 1032   792 posix_signal_service::post(signal_op* op)
1033   { 1033   {
HITCBC 1034   738 sched_->post(op); 1034   792 sched_->post(op);
HITCBC 1035   738 } 1035   792 }
1036   1036  
1037   inline void 1037   inline void
HITCBC 1038   583 posix_signal_service::add_service(posix_signal_service* service) 1038   583 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   583 posix_signal_detail::get_signal_state();
HITCBC 1042   583 std::lock_guard lock(state->mutex); 1042   583 std::lock_guard lock(state->mutex);
1043   1043  
HITCBC 1044   583 service->next_ = state->service_list; 1044   583 service->next_ = state->service_list;
HITCBC 1045   583 service->prev_ = nullptr; 1045   583 service->prev_ = nullptr;
HITCBC 1046   583 if (state->service_list) 1046   583 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   583 state->service_list = service;
HITCBC 1049   583 } 1049   583 }
1050   1050  
1051   inline void 1051   inline void
HITCBC 1052   583 posix_signal_service::remove_service(posix_signal_service* service) 1052   583 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   583 posix_signal_detail::get_signal_state();
HITCBC 1056   583 std::lock_guard lock(state->mutex); 1056   583 std::lock_guard lock(state->mutex);
1057   1057  
HITCBC 1058   583 if (service->next_ || service->prev_ || state->service_list == service) 1058   583 if (service->next_ || service->prev_ || state->service_list == service)
1059   { 1059   {
HITCBC 1060   583 if (state->service_list == service) 1060   583 if (state->service_list == service)
HITCBC 1061   581 state->service_list = service->next_; 1061   581 state->service_list = service->next_;
HITCBC 1062   583 if (service->prev_) 1062   583 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   583 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   583 service->next_ = nullptr;
HITCBC 1067   583 service->prev_ = nullptr; 1067   583 service->prev_ = nullptr;
1068   } 1068   }
HITCBC 1069   583 } 1069   583 }
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