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