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 by 40 + scheduler. One instance per execution_context, created on first use
41 - get_signal_service(). 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 - posix_signal_service(capy::execution_context& ctx, scheduler& sched); 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   184 void destroy(io_object::implementation* p) override 168   186 void destroy(io_object::implementation* p) override
169   { 169   {
HITCBC 170   184 auto& impl = static_cast<posix_signal&>(*p); 170   186 auto& impl = static_cast<posix_signal&>(*p);
HITCBC 171   184 [[maybe_unused]] auto n = impl.clear(); 171   186 [[maybe_unused]] auto n = impl.clear();
HITCBC 172   184 impl.disarm_stop(); 172   186 impl.disarm_stop();
HITCBC 173   184 impl.cancel(); 173   186 impl.cancel();
HITCBC 174   184 destroy_impl(impl); 174   186 destroy_impl(impl);
HITCBC 175   184 } 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   1024 void reset_token_cancel(posix_signal& impl) noexcept 210   1051 void reset_token_cancel(posix_signal& impl) noexcept
211   { 211   {
HITCBC 212   1024 std::lock_guard lock(mutex_); 212   1051 std::lock_guard lock(mutex_);
HITCBC 213   1024 impl.token_cancelled_ = false; 213   1051 impl.token_cancelled_ = false;
HITCBC 214   1024 } 214   1051 }
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 - /** Get or create the signal service for the given context.  
249 -  
250 - This function is called by the concrete scheduler during initialization  
251 - to create the signal service with a reference to itself.  
252 -  
253 - @param ctx Reference to the owning execution_context.  
254 - @param sched Reference to the scheduler for posting completions.  
255 - @return Reference to the signal service.  
256 - */  
257 - posix_signal_service&  
258 - get_signal_service(capy::execution_context& ctx, scheduler& sched);  
259 -  
260   248  
261   } // namespace detail 249   } // namespace detail
262   250  
263   } // namespace boost::corosio 251   } // namespace boost::corosio
264   252  
265   // --------------------------------------------------------------------------- 253   // ---------------------------------------------------------------------------
266   // Inline implementation 254   // Inline implementation
267   // --------------------------------------------------------------------------- 255   // ---------------------------------------------------------------------------
268   256  
269   namespace boost::corosio { 257   namespace boost::corosio {
270   258  
271   namespace detail { 259   namespace detail {
272   260  
273   namespace posix_signal_detail { 261   namespace posix_signal_detail {
274   262  
275   struct signal_state 263   struct signal_state
276   { 264   {
277   std::mutex mutex; 265   std::mutex mutex;
278   posix_signal_service* service_list = nullptr; 266   posix_signal_service* service_list = nullptr;
279   std::size_t registration_count[max_signal_number] = {}; 267   std::size_t registration_count[max_signal_number] = {};
280   signal_set::flags_t registered_flags[max_signal_number] = {}; 268   signal_set::flags_t registered_flags[max_signal_number] = {};
281   269  
282   // Self-pipe used to defer signal delivery out of handler context. 270   // Self-pipe used to defer signal delivery out of handler context.
283   // 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-
284   // 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()
285   // in normal context. Created once (on the first signal registration) and 273   // in normal context. Created once (on the first signal registration) and
286   // kept for the process lifetime. Each posix_signal_service registers the 274   // kept for the process lifetime. Each posix_signal_service registers the
287   // 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
288   // 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
289   // each signal is a fixed sizeof(int) record read atomically. 277   // each signal is a fixed sizeof(int) record read atomically.
290   int read_fd = -1; 278   int read_fd = -1;
291   int write_fd = -1; 279   int write_fd = -1;
292   }; 280   };
293   281  
294   BOOST_COROSIO_DECL signal_state* get_signal_state(); 282   BOOST_COROSIO_DECL signal_state* get_signal_state();
295   283  
296   // Check if requested flags are supported on this platform. 284   // Check if requested flags are supported on this platform.
297   // Returns true if all flags are supported, false otherwise. 285   // Returns true if all flags are supported, false otherwise.
298   inline bool 286   inline bool
HITCBC 299   207 flags_supported([[maybe_unused]] signal_set::flags_t flags) 287   207 flags_supported([[maybe_unused]] signal_set::flags_t flags)
300   { 288   {
301   #ifndef SA_NOCLDWAIT 289   #ifndef SA_NOCLDWAIT
302   if (flags & signal_set::no_child_wait) 290   if (flags & signal_set::no_child_wait)
303   return false; 291   return false;
304   #endif 292   #endif
HITCBC 305   207 return true; 293   207 return true;
306   } 294   }
307   295  
308   // Map abstract flags to sigaction() flags. 296   // Map abstract flags to sigaction() flags.
309   // Caller must ensure flags_supported() returns true first. 297   // Caller must ensure flags_supported() returns true first.
310   inline int 298   inline int
HITCBC 311   159 to_sigaction_flags(signal_set::flags_t flags) 299   159 to_sigaction_flags(signal_set::flags_t flags)
312   { 300   {
HITCBC 313   159 int sa_flags = 0; 301   159 int sa_flags = 0;
HITCBC 314   159 if (flags & signal_set::restart) 302   159 if (flags & signal_set::restart)
HITCBC 315   23 sa_flags |= SA_RESTART; 303   23 sa_flags |= SA_RESTART;
HITCBC 316   159 if (flags & signal_set::no_child_stop) 304   159 if (flags & signal_set::no_child_stop)
HITCBC 317   3 sa_flags |= SA_NOCLDSTOP; 305   3 sa_flags |= SA_NOCLDSTOP;
318   #ifdef SA_NOCLDWAIT 306   #ifdef SA_NOCLDWAIT
HITCBC 319   159 if (flags & signal_set::no_child_wait) 307   159 if (flags & signal_set::no_child_wait)
HITCBC 320   2 sa_flags |= SA_NOCLDWAIT; 308   2 sa_flags |= SA_NOCLDWAIT;
321   #endif 309   #endif
HITCBC 322   159 if (flags & signal_set::no_defer) 310   159 if (flags & signal_set::no_defer)
HITCBC 323   4 sa_flags |= SA_NODEFER; 311   4 sa_flags |= SA_NODEFER;
HITCBC 324   159 if (flags & signal_set::reset_handler) 312   159 if (flags & signal_set::reset_handler)
HITCBC 325   2 sa_flags |= SA_RESETHAND; 313   2 sa_flags |= SA_RESETHAND;
HITCBC 326   159 return sa_flags; 314   159 return sa_flags;
327   } 315   }
328   316  
329   // Check if two flag values are compatible 317   // Check if two flag values are compatible
330   inline bool 318   inline bool
HITCBC 331   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)
332   { 320   {
333   // dont_care is always compatible 321   // dont_care is always compatible
HITCBC 334   76 if ((existing & signal_set::dont_care) || 322   76 if ((existing & signal_set::dont_care) ||
HITCBC 335   37 (requested & signal_set::dont_care)) 323   37 (requested & signal_set::dont_care))
HITCBC 336   7 return true; 324   7 return true;
337   325  
338   // Mask out dont_care bit for comparison 326   // Mask out dont_care bit for comparison
HITCBC 339   32 constexpr auto mask = ~signal_set::dont_care; 327   32 constexpr auto mask = ~signal_set::dont_care;
HITCBC 340   32 return (existing & mask) == (requested & mask); 328   32 return (existing & mask) == (requested & mask);
341   } 329   }
342   330  
343   // Lazily create the global signal self-pipe. Idempotent; call under 331   // Lazily create the global signal self-pipe. Idempotent; call under
344   // 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
345   // 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
346   // 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).
347   // 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
348   // fails: an exhausted descriptor table and a rejected fcntl are different 336   // fails: an exhausted descriptor table and a rejected fcntl are different
349   // problems to the caller of add(). 337   // problems to the caller of add().
350   [[nodiscard]] inline std::error_code 338   [[nodiscard]] inline std::error_code
HITCBC 351   207 open_signal_pipe(signal_state* state) 339   207 open_signal_pipe(signal_state* state)
352   { 340   {
HITCBC 353   207 if (state->read_fd >= 0) 341   207 if (state->read_fd >= 0)
HITCBC 354   193 return {}; 342   193 return {};
355   343  
356   int fds[2]; 344   int fds[2];
HITCBC 357   14 if (::pipe(fds) < 0) 345   14 if (::pipe(fds) < 0)
HITCBC 358   1 return make_err(errno); 346   1 return make_err(errno);
359   347  
HITCBC 360   30 for (int i = 0; i < 2; ++i) 348   30 for (int i = 0; i < 2; ++i)
361   { 349   {
HITCBC 362   23 int fl = ::fcntl(fds[i], F_GETFL, 0); 350   23 int fl = ::fcntl(fds[i], F_GETFL, 0);
HITCBC 363   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 364   19 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) 352   19 ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1)
365   { 353   {
HITCBC 366   6 auto ec = make_err(errno); 354   6 auto ec = make_err(errno);
HITCBC 367   6 ::close(fds[0]); 355   6 ::close(fds[0]);
HITCBC 368   6 ::close(fds[1]); 356   6 ::close(fds[1]);
HITCBC 369   6 return ec; 357   6 return ec;
370   } 358   }
371   } 359   }
372   360  
HITCBC 373   7 state->read_fd = fds[0]; 361   7 state->read_fd = fds[0];
HITCBC 374   7 state->write_fd = fds[1]; 362   7 state->write_fd = fds[1];
HITCBC 375   7 return {}; 363   7 return {};
376   } 364   }
377   365  
378   // 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
379   // 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(),
380   // 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
381   // interrupted foreground syscall is unaffected. A full pipe (write returns 369   // interrupted foreground syscall is unaffected. A full pipe (write returns
382   // EAGAIN) or a short write is intentionally dropped — the reactor still 370   // EAGAIN) or a short write is intentionally dropped — the reactor still
383   // coalesces because deliver_signal reports the signal to every waiting set. 371   // coalesces because deliver_signal reports the signal to every waiting set.
384   inline void 372   inline void
HITCBC 385   317 corosio_posix_signal_handler(int signal_number) 373   317 corosio_posix_signal_handler(int signal_number)
386   { 374   {
HITCBC 387   317 int saved_errno = errno; 375   317 int saved_errno = errno;
HITCBC 388   317 signal_state* state = get_signal_state(); 376   317 signal_state* state = get_signal_state();
389   [[maybe_unused]] ssize_t r = 377   [[maybe_unused]] ssize_t r =
HITCBC 390   317 ::write(state->write_fd, &signal_number, sizeof(int)); 378   317 ::write(state->write_fd, &signal_number, sizeof(int));
HITCBC 391   317 errno = saved_errno; 379   317 errno = saved_errno;
392   // With sigaction(), the handler persists automatically (unlike some 380   // With sigaction(), the handler persists automatically (unlike some
393   // signal() implementations that reset to SIG_DFL). 381   // signal() implementations that reset to SIG_DFL).
HITCBC 394   317 } 382   317 }
395   383  
396   // 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
397   // 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
398   // locking and scheduler post are safe here. Reads until EAGAIN (edge- 386   // locking and scheduler post are safe here. Reads until EAGAIN (edge-
399   // triggered backends require a full drain per readiness event). 387   // triggered backends require a full drain per readiness event).
400   inline void 388   inline void
HITCBC 401   317 drain_signal_pipe() 389   317 drain_signal_pipe()
402   { 390   {
HITCBC 403   317 signal_state* state = get_signal_state(); 391   317 signal_state* state = get_signal_state();
404   int signal_number; 392   int signal_number;
HITCBC 405   634 while (::read(state->read_fd, &signal_number, sizeof(int)) == 393   634 while (::read(state->read_fd, &signal_number, sizeof(int)) ==
406   static_cast<ssize_t>(sizeof(int))) 394   static_cast<ssize_t>(sizeof(int)))
407   { 395   {
HITCBC 408   317 posix_signal_service::deliver_signal(signal_number); 396   317 posix_signal_service::deliver_signal(signal_number);
409   } 397   }
HITCBC 410   317 } 398   317 }
411   399  
412   } // namespace posix_signal_detail 400   } // namespace posix_signal_detail
413   401  
414   // signal_op implementation 402   // signal_op implementation
415   403  
416   inline void 404   inline void
HITCBC 417   321 signal_op::operator()() 405   321 signal_op::operator()()
418   { 406   {
HITCBC 419   321 if (ec_out) 407   321 if (ec_out)
HITCBC 420   321 *ec_out = {}; 408   321 *ec_out = {};
HITCBC 421   321 if (signal_out) 409   321 if (signal_out)
HITCBC 422   321 *signal_out = signal_number; 410   321 *signal_out = signal_number;
423   411  
424   // Capture svc before resuming (coro may destroy us) 412   // Capture svc before resuming (coro may destroy us)
HITCBC 425   321 auto* service = svc; 413   321 auto* service = svc;
HITCBC 426   321 svc = nullptr; 414   321 svc = nullptr;
427   415  
HITCBC 428   321 cont.h = h; 416   321 cont.h = h;
HITCBC 429   321 d.post(cont); 417   321 d.post(cont);
430   418  
431   // Balance the work_started() from start_wait 419   // Balance the work_started() from start_wait
HITCBC 432   321 if (service) 420   321 if (service)
HITCBC 433   319 service->work_finished(); 421   319 service->work_finished();
HITCBC 434   321 } 422   321 }
435   423  
436   inline void 424   inline void
MISUBC 437   ✗ signal_op::destroy() 425   ✗ signal_op::destroy()
438   { 426   {
439   // No-op: signal_op is embedded in posix_signal 427   // No-op: signal_op is embedded in posix_signal
MISUBC 440   ✗ } 428   ✗ }
441   429  
442   // posix_signal implementation 430   // posix_signal implementation
443   431  
HITCBC 444   190 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept 432   192 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
HITCBC 445   190 : svc_(svc) 433   192 : svc_(svc)
446   { 434   {
HITCBC 447   190 } 435   192 }
448   436  
449   inline std::coroutine_handle<> 437   inline std::coroutine_handle<>
HITCBC 450   1107 posix_signal::wait( 438   1121 posix_signal::wait(
451   std::coroutine_handle<> h, 439   std::coroutine_handle<> h,
452   capy::executor_ref d, 440   capy::executor_ref d,
453   std::stop_token token, 441   std::stop_token token,
454   std::error_code* ec, 442   std::error_code* ec,
455   int* signal_out) 443   int* signal_out)
456   { 444   {
HITCBC 457   1107 pending_op_.h = h; 445   1121 pending_op_.h = h;
HITCBC 458   1107 pending_op_.d = d; 446   1121 pending_op_.d = d;
HITCBC 459   1107 pending_op_.ec_out = ec; 447   1121 pending_op_.ec_out = ec;
HITCBC 460   1107 pending_op_.signal_out = signal_out; 448   1121 pending_op_.signal_out = signal_out;
HITCBC 461   1107 pending_op_.signal_number = 0; 449   1121 pending_op_.signal_number = 0;
462   450  
463   // Disarm any callback left over from a previous wait before doing 451   // Disarm any callback left over from a previous wait before doing
464   // anything else, including the early return below: otherwise that 452   // anything else, including the early return below: otherwise that
465   // path leaves this object owning a callback it no longer uses. 453   // path leaves this object owning a callback it no longer uses.
466   // 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
467   // concurrently running callback returns, and that callback takes 455   // concurrently running callback returns, and that callback takes
468   // posix_signal_service::mutex_. 456   // posix_signal_service::mutex_.
HITCBC 469   1107 stop_cb_.reset(); 457   1121 stop_cb_.reset();
470   458  
HITCBC 471   1107 if (token.stop_requested()) 459   1121 if (token.stop_requested())
472   { 460   {
HITCBC 473   83 if (ec) 461   70 if (ec)
HITCBC 474   83 *ec = make_error_code(capy::error::canceled); 462   70 *ec = make_error_code(capy::error::canceled);
HITCBC 475   83 if (signal_out) 463   70 if (signal_out)
HITCBC 476   83 *signal_out = 0; 464   70 *signal_out = 0;
HITCBC 477   83 pending_op_.cont.h = h; 465   70 pending_op_.cont.h = h;
HITCBC 478   83 d.post(pending_op_.cont); 466   70 d.post(pending_op_.cont);
479   // completion is always posted to scheduler queue, never inline. 467   // completion is always posted to scheduler queue, never inline.
HITCBC 480   83 return std::noop_coroutine(); 468   70 return std::noop_coroutine();
481   } 469   }
482   470  
483   // Clearing the flag before arming is load-bearing: reset_token_cancel 471   // Clearing the flag before arming is load-bearing: reset_token_cancel
484   // must run immediately before emplace, not before the early return 472   // must run immediately before emplace, not before the early return
485   // above. 473   // above.
HITCBC 486   1024 svc_.reset_token_cancel(*this); 474   1051 svc_.reset_token_cancel(*this);
HITCBC 487   1024 if (token.stop_possible()) 475   1051 if (token.stop_possible())
HITCBC 488   694 stop_cb_.emplace(token, token_canceller{this}); 476   721 stop_cb_.emplace(token, token_canceller{this});
489   477  
HITCBC 490   1024 svc_.start_wait(*this, &pending_op_); 478   1051 svc_.start_wait(*this, &pending_op_);
491   // completion is always posted to scheduler queue, never inline. 479   // completion is always posted to scheduler queue, never inline.
HITCBC 492   1024 return std::noop_coroutine(); 480   1051 return std::noop_coroutine();
493   } 481   }
494   482  
495   inline std::error_code 483   inline std::error_code
HITCBC 496   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)
497   { 485   {
HITCBC 498   211 return svc_.add_signal(*this, signal_number, flags); 486   211 return svc_.add_signal(*this, signal_number, flags);
499   } 487   }
500   488  
501   inline std::error_code 489   inline std::error_code
HITCBC 502   26 posix_signal::remove(int signal_number) 490   26 posix_signal::remove(int signal_number)
503   { 491   {
HITCBC 504   26 return svc_.remove_signal(*this, signal_number); 492   26 return svc_.remove_signal(*this, signal_number);
505   } 493   }
506   494  
507   inline std::error_code 495   inline std::error_code
HITCBC 508   198 posix_signal::clear() 496   200 posix_signal::clear()
509   { 497   {
HITCBC 510   198 return svc_.clear_signals(*this); 498   200 return svc_.clear_signals(*this);
511   } 499   }
512   500  
513   inline void 501   inline void
HITCBC 514   201 posix_signal::cancel() noexcept 502   203 posix_signal::cancel() noexcept
515   { 503   {
HITCBC 516   201 svc_.cancel_wait(*this); 504   203 svc_.cancel_wait(*this);
HITCBC 517   201 } 505   203 }
518   506  
519   // posix_signal_service implementation 507   // posix_signal_service implementation
520   508  
HITCBC 521   2241 inline posix_signal_service::posix_signal_service( 509   165 inline posix_signal_service::posix_signal_service(
HITCBC 522 - 2241 capy::execution_context&, scheduler& sched) 510 + 165 capy::execution_context& ctx)
HITCBC 523 - 2241 : sched_(&sched) 511 + 165 : sched_(&get_scheduler(ctx))
524   { 512   {
HITCBC 525   145665 for (int i = 0; i < max_signal_number; ++i) 513   10725 for (int i = 0; i < max_signal_number; ++i)
526   { 514   {
HITCBC 527   143424 registrations_[i] = nullptr; 515   10560 registrations_[i] = nullptr;
HITCBC 528   143424 registration_count_[i] = 0; 516   10560 registration_count_[i] = 0;
529   } 517   }
HITCBC 530   2241 add_service(this); 518   165 add_service(this);
HITCBC 531   2241 } 519   165 }
532   520  
HITCBC 533   4482 inline posix_signal_service::~posix_signal_service() 521   330 inline posix_signal_service::~posix_signal_service()
534   { 522   {
HITCBC 535   2241 remove_service(this); 523   165 remove_service(this);
HITCBC 536   4482 } 524   330 }
537   525  
538   inline void 526   inline void
HITCBC 539   2241 posix_signal_service::shutdown() 527   165 posix_signal_service::shutdown()
540   { 528   {
541   // Collected under the locks below and deleted after they are released: 529   // Collected under the locks below and deleted after they are released:
542   // ~posix_signal destroys an armed stop_cb_, and ~stop_callback blocks 530   // ~posix_signal destroys an armed stop_cb_, and ~stop_callback blocks
543   // until a concurrently running token_canceller returns -- which takes 531   // until a concurrently running token_canceller returns -- which takes
544   // mutex_. Deleting while still holding mutex_ would self-deadlock the 532   // mutex_. Deleting while still holding mutex_ would self-deadlock the
545   // same way disarm_stop() would if called inside the locked loop. 533   // same way disarm_stop() would if called inside the locked loop.
HITCBC 546   2241 intrusive_list<posix_signal> doomed; 534   165 intrusive_list<posix_signal> doomed;
547   535  
548   { 536   {
549   posix_signal_detail::signal_state* state = 537   posix_signal_detail::signal_state* state =
HITCBC 550   2241 posix_signal_detail::get_signal_state(); 538   165 posix_signal_detail::get_signal_state();
HITCBC 551   2241 std::lock_guard state_lock(state->mutex); 539   165 std::lock_guard state_lock(state->mutex);
HITCBC 552   2241 std::lock_guard lock(mutex_); 540   165 std::lock_guard lock(mutex_);
553   541  
HITCBC 554   2247 for (auto* impl = impl_list_.pop_front(); impl != nullptr; 542   171 for (auto* impl = impl_list_.pop_front(); impl != nullptr;
HITCBC 555   6 impl = impl_list_.pop_front()) 543   6 impl = impl_list_.pop_front())
556   { 544   {
HITCBC 557   12 while (auto* reg = impl->signals_) 545   12 while (auto* reg = impl->signals_)
558   { 546   {
HITCBC 559   6 int const signal_number = reg->signal_number; 547   6 int const signal_number = reg->signal_number;
560   548  
561   // The registration table outlives every io_context, so a set 549   // The registration table outlives every io_context, so a set
562   // still registered here has to give its count and disposition 550   // still registered here has to give its count and disposition
563   // back the way clear() would: otherwise the signal stays 551   // back the way clear() would: otherwise the signal stays
564   // installed with these flags and the next add() of it is 552   // installed with these flags and the next add() of it is
565   // refused. The per-node table unlink clear() also does is 553   // refused. The per-node table unlink clear() also does is
566   // skipped in favour of the wholesale null-out below. 554   // skipped in favour of the wholesale null-out below.
HITCBC 567   6 if (state->registration_count[signal_number] == 1) 555   6 if (state->registration_count[signal_number] == 1)
568   { 556   {
HITCBC 569   4 struct sigaction sa = {}; 557   4 struct sigaction sa = {};
HITCBC 570   4 sa.sa_handler = SIG_DFL; 558   4 sa.sa_handler = SIG_DFL;
HITCBC 571   4 sigemptyset(&sa.sa_mask); 559   4 sigemptyset(&sa.sa_mask);
HITCBC 572   4 sa.sa_flags = 0; 560   4 sa.sa_flags = 0;
HITCBC 573   4 std::ignore = ::sigaction(signal_number, &sa, nullptr); 561   4 std::ignore = ::sigaction(signal_number, &sa, nullptr);
HITCBC 574   4 state->registered_flags[signal_number] = signal_set::none; 562   4 state->registered_flags[signal_number] = signal_set::none;
575   } 563   }
576   564  
HITCBC 577   6 --state->registration_count[signal_number]; 565   6 --state->registration_count[signal_number];
HITCBC 578   6 --registration_count_[signal_number]; 566   6 --registration_count_[signal_number];
579   567  
HITCBC 580   6 impl->signals_ = reg->next_in_set; 568   6 impl->signals_ = reg->next_in_set;
HITCBC 581   6 delete reg; 569   6 delete reg;
HITCBC 582   6 } 570   6 }
HITCBC 583   6 doomed.push_back(impl); 571   6 doomed.push_back(impl);
584   } 572   }
585   573  
586   // Every live registration hung off an implementation in impl_list_, 574   // Every live registration hung off an implementation in impl_list_,
587   // 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
588   // 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()
589   // walks this service until the destructor unlinks it from the global 577   // walks this service until the destructor unlinks it from the global
590   // list. 578   // list.
HITCBC 591   145665 for (int i = 0; i < max_signal_number; ++i) 579   10725 for (int i = 0; i < max_signal_number; ++i)
HITCBC 592   143424 registrations_[i] = nullptr; 580   10560 registrations_[i] = nullptr;
HITCBC 593   2241 } 581   165 }
594   582  
HITCBC 595   2247 for (auto* impl = doomed.pop_front(); impl != nullptr; 583   171 for (auto* impl = doomed.pop_front(); impl != nullptr;
HITCBC 596   6 impl = doomed.pop_front()) 584   6 impl = doomed.pop_front())
597   { 585   {
HITCBC 598   6 delete impl; 586   6 delete impl;
599   } 587   }
HITCBC 600   2241 } 588   165 }
601   589  
602   inline io_object::implementation* 590   inline io_object::implementation*
HITCBC 603   190 posix_signal_service::construct() 591   192 posix_signal_service::construct()
604   { 592   {
HITCBC 605   190 auto* impl = new posix_signal(*this); 593   192 auto* impl = new posix_signal(*this);
606   594  
607   { 595   {
HITCBC 608   190 std::lock_guard lock(mutex_); 596   192 std::lock_guard lock(mutex_);
HITCBC 609   190 impl_list_.push_back(impl); 597   192 impl_list_.push_back(impl);
HITCBC 610   190 } 598   192 }
611   599  
HITCBC 612   190 return impl; 600   192 return impl;
613   } 601   }
614   602  
615   inline void 603   inline void
HITCBC 616   184 posix_signal_service::destroy_impl(posix_signal& impl) 604   186 posix_signal_service::destroy_impl(posix_signal& impl)
617   { 605   {
618   { 606   {
HITCBC 619   184 std::lock_guard lock(mutex_); 607   186 std::lock_guard lock(mutex_);
HITCBC 620   184 impl_list_.remove(&impl); 608   186 impl_list_.remove(&impl);
HITCBC 621   184 } 609   186 }
622   610  
HITCBC 623   184 delete &impl; 611   186 delete &impl;
HITCBC 624   184 } 612   186 }
625   613  
626   inline std::error_code 614   inline std::error_code
HITCBC 627   211 posix_signal_service::add_signal( 615   211 posix_signal_service::add_signal(
628   posix_signal& impl, int signal_number, signal_set::flags_t flags) 616   posix_signal& impl, int signal_number, signal_set::flags_t flags)
629   { 617   {
HITCBC 630   211 if (signal_number < 0 || signal_number >= max_signal_number) 618   211 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 631   4 return make_error_code(std::errc::invalid_argument); 619   4 return make_error_code(std::errc::invalid_argument);
632   620  
633   // Validate that requested flags are supported on this platform 621   // Validate that requested flags are supported on this platform
634   // (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 635   207 if (!posix_signal_detail::flags_supported(flags)) 623   207 if (!posix_signal_detail::flags_supported(flags))
MISUBC 636   ✗ return make_error_code(std::errc::operation_not_supported); 624   ✗ return make_error_code(std::errc::operation_not_supported);
637   625  
638   posix_signal_detail::signal_state* state = 626   posix_signal_detail::signal_state* state =
HITCBC 639   207 posix_signal_detail::get_signal_state(); 627   207 posix_signal_detail::get_signal_state();
640   628  
641   // 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
642   // watching its read end, BEFORE taking the registration locks. The 630   // watching its read end, BEFORE taking the registration locks. The
643   // 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
644   // and service mutexes; register_signal_reader locks the descriptor mutex 632   // and service mutexes; register_signal_reader locks the descriptor mutex
645   // (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
646   // 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
647   // 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
648   // this context race add() from different threads. 636   // this context race add() from different threads.
649   { 637   {
HITCBC 650   207 std::lock_guard state_lock(state->mutex); 638   207 std::lock_guard state_lock(state->mutex);
HITCBC 651   207 if (auto ec = posix_signal_detail::open_signal_pipe(state)) 639   207 if (auto ec = posix_signal_detail::open_signal_pipe(state))
HITCBC 652   7 return ec; 640   7 return ec;
HITCBC 653   207 } 641   207 }
654   { 642   {
655   // Success-latched so a failed environmental registration 643   // Success-latched so a failed environmental registration
656   // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add() 644   // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add()
657   // instead of being lost; the code travels the return channel. 645   // instead of being lost; the code travels the return channel.
HITCBC 658   200 std::lock_guard reg_lock(reader_mutex_); 646   200 std::lock_guard reg_lock(reader_mutex_);
HITCBC 659   200 if (!reader_registered_) 647   200 if (!reader_registered_)
660   { 648   {
HITCBC 661   137 if (auto ec = sched_->register_signal_reader(state->read_fd)) 649   137 if (auto ec = sched_->register_signal_reader(state->read_fd))
HITCBC 662   2 return ec; 650   2 return ec;
HITCBC 663   135 reader_registered_ = true; 651   135 reader_registered_ = true;
664   } 652   }
HITCBC 665   200 } 653   200 }
666   654  
HITCBC 667   198 std::lock_guard state_lock(state->mutex); 655   198 std::lock_guard state_lock(state->mutex);
HITCBC 668   198 std::lock_guard lock(mutex_); 656   198 std::lock_guard lock(mutex_);
669   657  
670   // Find insertion point (list is sorted by signal number) 658   // Find insertion point (list is sorted by signal number)
HITCBC 671   198 signal_registration** insertion_point = &impl.signals_; 659   198 signal_registration** insertion_point = &impl.signals_;
HITCBC 672   198 signal_registration* reg = impl.signals_; 660   198 signal_registration* reg = impl.signals_;
HITCBC 673   221 while (reg && reg->signal_number < signal_number) 661   221 while (reg && reg->signal_number < signal_number)
674   { 662   {
HITCBC 675   23 insertion_point = &reg->next_in_set; 663   23 insertion_point = &reg->next_in_set;
HITCBC 676   23 reg = reg->next_in_set; 664   23 reg = reg->next_in_set;
677   } 665   }
678   666  
679   // Already registered in this set - check flag compatibility 667   // Already registered in this set - check flag compatibility
680   // (same signal_set adding same signal twice with different flags) 668   // (same signal_set adding same signal twice with different flags)
HITCBC 681   198 if (reg && reg->signal_number == signal_number) 669   198 if (reg && reg->signal_number == signal_number)
682   { 670   {
HITCBC 683   13 if (!posix_signal_detail::flags_compatible(reg->flags, flags)) 671   13 if (!posix_signal_detail::flags_compatible(reg->flags, flags))
HITCBC 684   4 return make_error_code(std::errc::invalid_argument); 672   4 return make_error_code(std::errc::invalid_argument);
HITCBC 685   9 return {}; 673   9 return {};
686   } 674   }
687   675  
688   // Check flag compatibility with global registration 676   // Check flag compatibility with global registration
689   // (different signal_set already registered this signal with different flags) 677   // (different signal_set already registered this signal with different flags)
HITCBC 690   185 if (state->registration_count[signal_number] > 0) 678   185 if (state->registration_count[signal_number] > 0)
691   { 679   {
HITCBC 692   26 if (!posix_signal_detail::flags_compatible( 680   26 if (!posix_signal_detail::flags_compatible(
693   state->registered_flags[signal_number], flags)) 681   state->registered_flags[signal_number], flags))
HITCBC 694   2 return make_error_code(std::errc::invalid_argument); 682   2 return make_error_code(std::errc::invalid_argument);
695   } 683   }
696   684  
HITCBC 697   183 auto* new_reg = new signal_registration; 685   183 auto* new_reg = new signal_registration;
HITCBC 698   183 new_reg->signal_number = signal_number; 686   183 new_reg->signal_number = signal_number;
HITCBC 699   183 new_reg->flags = flags; 687   183 new_reg->flags = flags;
HITCBC 700   183 new_reg->owner = &impl; 688   183 new_reg->owner = &impl;
HITCBC 701   183 new_reg->undelivered = 0; 689   183 new_reg->undelivered = 0;
702   690  
703   // Install signal handler on first global registration 691   // Install signal handler on first global registration
HITCBC 704   183 if (state->registration_count[signal_number] == 0) 692   183 if (state->registration_count[signal_number] == 0)
705   { 693   {
HITCBC 706   159 struct sigaction sa = {}; 694   159 struct sigaction sa = {};
HITCBC 707   159 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; 695   159 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
HITCBC 708   159 sigemptyset(&sa.sa_mask); 696   159 sigemptyset(&sa.sa_mask);
HITCBC 709   159 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); 697   159 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
710   698  
HITCBC 711   159 if (::sigaction(signal_number, &sa, nullptr) < 0) 699   159 if (::sigaction(signal_number, &sa, nullptr) < 0)
712   { 700   {
HITCBC 713   1 delete new_reg; 701   1 delete new_reg;
HITCBC 714   1 return make_error_code(std::errc::invalid_argument); 702   1 return make_error_code(std::errc::invalid_argument);
715   } 703   }
716   704  
717   // Store the flags used for first registration 705   // Store the flags used for first registration
HITCBC 718   158 state->registered_flags[signal_number] = flags; 706   158 state->registered_flags[signal_number] = flags;
719   } 707   }
720   708  
HITCBC 721   182 new_reg->next_in_set = reg; 709   182 new_reg->next_in_set = reg;
HITCBC 722   182 *insertion_point = new_reg; 710   182 *insertion_point = new_reg;
723   711  
HITCBC 724   182 new_reg->next_in_table = registrations_[signal_number]; 712   182 new_reg->next_in_table = registrations_[signal_number];
HITCBC 725   182 new_reg->prev_in_table = nullptr; 713   182 new_reg->prev_in_table = nullptr;
HITCBC 726   182 if (registrations_[signal_number]) 714   182 if (registrations_[signal_number])
HITCBC 727   18 registrations_[signal_number]->prev_in_table = new_reg; 715   18 registrations_[signal_number]->prev_in_table = new_reg;
HITCBC 728   182 registrations_[signal_number] = new_reg; 716   182 registrations_[signal_number] = new_reg;
729   717  
HITCBC 730   182 ++state->registration_count[signal_number]; 718   182 ++state->registration_count[signal_number];
HITCBC 731   182 ++registration_count_[signal_number]; 719   182 ++registration_count_[signal_number];
732   720  
HITCBC 733   182 return {}; 721   182 return {};
HITCBC 734   198 } 722   198 }
735   723  
736   inline std::error_code 724   inline std::error_code
HITCBC 737   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)
738   { 726   {
HITCBC 739   26 if (signal_number < 0 || signal_number >= max_signal_number) 727   26 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 740   2 return make_error_code(std::errc::invalid_argument); 728   2 return make_error_code(std::errc::invalid_argument);
741   729  
742   posix_signal_detail::signal_state* state = 730   posix_signal_detail::signal_state* state =
HITCBC 743   24 posix_signal_detail::get_signal_state(); 731   24 posix_signal_detail::get_signal_state();
HITCBC 744   24 std::lock_guard state_lock(state->mutex); 732   24 std::lock_guard state_lock(state->mutex);
HITCBC 745   24 std::lock_guard lock(mutex_); 733   24 std::lock_guard lock(mutex_);
746   734  
HITCBC 747   24 signal_registration** deletion_point = &impl.signals_; 735   24 signal_registration** deletion_point = &impl.signals_;
HITCBC 748   24 signal_registration* reg = impl.signals_; 736   24 signal_registration* reg = impl.signals_;
HITCBC 749   26 while (reg && reg->signal_number < signal_number) 737   26 while (reg && reg->signal_number < signal_number)
750   { 738   {
HITCBC 751   2 deletion_point = &reg->next_in_set; 739   2 deletion_point = &reg->next_in_set;
HITCBC 752   2 reg = reg->next_in_set; 740   2 reg = reg->next_in_set;
753   } 741   }
754   742  
HITCBC 755   24 if (!reg || reg->signal_number != signal_number) 743   24 if (!reg || reg->signal_number != signal_number)
HITCBC 756   3 return {}; 744   3 return {};
757   745  
758   // Restore default handler on last global unregistration 746   // Restore default handler on last global unregistration
HITCBC 759   21 if (state->registration_count[signal_number] == 1) 747   21 if (state->registration_count[signal_number] == 1)
760   { 748   {
HITCBC 761   17 struct sigaction sa = {}; 749   17 struct sigaction sa = {};
HITCBC 762   17 sa.sa_handler = SIG_DFL; 750   17 sa.sa_handler = SIG_DFL;
HITCBC 763   17 sigemptyset(&sa.sa_mask); 751   17 sigemptyset(&sa.sa_mask);
HITCBC 764   17 sa.sa_flags = 0; 752   17 sa.sa_flags = 0;
765   753  
HITCBC 766   17 if (::sigaction(signal_number, &sa, nullptr) < 0) 754   17 if (::sigaction(signal_number, &sa, nullptr) < 0)
HITCBC 767   1 return make_error_code(std::errc::invalid_argument); 755   1 return make_error_code(std::errc::invalid_argument);
768   756  
769   // Clear stored flags 757   // Clear stored flags
HITCBC 770   16 state->registered_flags[signal_number] = signal_set::none; 758   16 state->registered_flags[signal_number] = signal_set::none;
771   } 759   }
772   760  
HITCBC 773   20 *deletion_point = reg->next_in_set; 761   20 *deletion_point = reg->next_in_set;
774   762  
HITCBC 775   20 if (registrations_[signal_number] == reg) 763   20 if (registrations_[signal_number] == reg)
HITCBC 776   18 registrations_[signal_number] = reg->next_in_table; 764   18 registrations_[signal_number] = reg->next_in_table;
HITCBC 777   20 if (reg->prev_in_table) 765   20 if (reg->prev_in_table)
HITCBC 778   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 779   20 if (reg->next_in_table) 767   20 if (reg->next_in_table)
HITCBC 780   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;
781   769  
HITCBC 782   20 --state->registration_count[signal_number]; 770   20 --state->registration_count[signal_number];
HITCBC 783   20 --registration_count_[signal_number]; 771   20 --registration_count_[signal_number];
784   772  
HITCBC 785   20 delete reg; 773   20 delete reg;
HITCBC 786   20 return {}; 774   20 return {};
HITCBC 787   24 } 775   24 }
788   776  
789   inline std::error_code 777   inline std::error_code
HITCBC 790   198 posix_signal_service::clear_signals(posix_signal& impl) 778   200 posix_signal_service::clear_signals(posix_signal& impl)
791   { 779   {
792   posix_signal_detail::signal_state* state = 780   posix_signal_detail::signal_state* state =
HITCBC 793   198 posix_signal_detail::get_signal_state(); 781   200 posix_signal_detail::get_signal_state();
HITCBC 794   198 std::lock_guard state_lock(state->mutex); 782   200 std::lock_guard state_lock(state->mutex);
HITCBC 795   198 std::lock_guard lock(mutex_); 783   200 std::lock_guard lock(mutex_);
796   784  
HITCBC 797   198 std::error_code first_error; 785   200 std::error_code first_error;
798   786  
HITCBC 799   354 while (signal_registration* reg = impl.signals_) 787   356 while (signal_registration* reg = impl.signals_)
800   { 788   {
HITCBC 801   156 int signal_number = reg->signal_number; 789   156 int signal_number = reg->signal_number;
802   790  
HITCBC 803   156 if (state->registration_count[signal_number] == 1) 791   156 if (state->registration_count[signal_number] == 1)
804   { 792   {
HITCBC 805   138 struct sigaction sa = {}; 793   138 struct sigaction sa = {};
HITCBC 806   138 sa.sa_handler = SIG_DFL; 794   138 sa.sa_handler = SIG_DFL;
HITCBC 807   138 sigemptyset(&sa.sa_mask); 795   138 sigemptyset(&sa.sa_mask);
HITCBC 808   138 sa.sa_flags = 0; 796   138 sa.sa_flags = 0;
809   797  
HITCBC 810   138 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) 798   138 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
HITCBC 811   1 first_error = make_error_code(std::errc::invalid_argument); 799   1 first_error = make_error_code(std::errc::invalid_argument);
812   800  
813   // Clear stored flags 801   // Clear stored flags
HITCBC 814   138 state->registered_flags[signal_number] = signal_set::none; 802   138 state->registered_flags[signal_number] = signal_set::none;
815   } 803   }
816   804  
HITCBC 817   156 impl.signals_ = reg->next_in_set; 805   156 impl.signals_ = reg->next_in_set;
818   806  
HITCBC 819   156 if (registrations_[signal_number] == reg) 807   156 if (registrations_[signal_number] == reg)
HITCBC 820   154 registrations_[signal_number] = reg->next_in_table; 808   154 registrations_[signal_number] = reg->next_in_table;
HITCBC 821   156 if (reg->prev_in_table) 809   156 if (reg->prev_in_table)
HITCBC 822   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 823   156 if (reg->next_in_table) 811   156 if (reg->next_in_table)
HITCBC 824   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;
825   813  
HITCBC 826   156 --state->registration_count[signal_number]; 814   156 --state->registration_count[signal_number];
HITCBC 827   156 --registration_count_[signal_number]; 815   156 --registration_count_[signal_number];
828   816  
HITCBC 829   156 delete reg; 817   156 delete reg;
HITCBC 830   156 } 818   156 }
831   819  
HITCBC 832   198 if (first_error) 820   200 if (first_error)
HITCBC 833   1 return first_error; 821   1 return first_error;
HITCBC 834   197 return {}; 822   199 return {};
HITCBC 835   198 } 823   200 }
836   824  
837   inline void 825   inline void
HITCBC 838   201 posix_signal_service::cancel_wait(posix_signal& impl) 826   203 posix_signal_service::cancel_wait(posix_signal& impl)
839   { 827   {
HITCBC 840   201 bool was_waiting = false; 828   203 bool was_waiting = false;
HITCBC 841   201 signal_op* op = nullptr; 829   203 signal_op* op = nullptr;
842   830  
843   { 831   {
HITCBC 844   201 std::lock_guard lock(mutex_); 832   203 std::lock_guard lock(mutex_);
HITCBC 845   201 impl.cancelled_ = true; 833   203 impl.cancelled_ = true;
HITCBC 846   201 if (impl.waiting_) 834   203 if (impl.waiting_)
847   { 835   {
HITCBC 848   7 was_waiting = true; 836   7 was_waiting = true;
HITCBC 849   7 impl.waiting_ = false; 837   7 impl.waiting_ = false;
HITCBC 850   7 op = &impl.pending_op_; 838   7 op = &impl.pending_op_;
851   } 839   }
HITCBC 852   201 } 840   203 }
853   841  
HITCBC 854   201 if (was_waiting) 842   203 if (was_waiting)
855   { 843   {
HITCBC 856   7 if (op->ec_out) 844   7 if (op->ec_out)
HITCBC 857   7 *op->ec_out = make_error_code(capy::error::canceled); 845   7 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 858   7 if (op->signal_out) 846   7 if (op->signal_out)
HITCBC 859   7 *op->signal_out = 0; 847   7 *op->signal_out = 0;
HITCBC 860   7 op->cont.h = op->h; 848   7 op->cont.h = op->h;
HITCBC 861   7 op->d.post(op->cont); 849   7 op->d.post(op->cont);
HITCBC 862   7 sched_->work_finished(); 850   7 sched_->work_finished();
863   } 851   }
HITCBC 864   201 } 852   203 }
865   853  
866   inline void 854   inline void
HITCBC 867   690 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept 855   717 posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept
868   { 856   {
HITCBC 869   690 bool was_waiting = false; 857   717 bool was_waiting = false;
HITCBC 870   690 signal_op* op = nullptr; 858   717 signal_op* op = nullptr;
871   859  
872   { 860   {
HITCBC 873   690 std::lock_guard lock(mutex_); 861   717 std::lock_guard lock(mutex_);
874   // Persist the request even when no wait is parked yet: wait() 862   // Persist the request even when no wait is parked yet: wait()
875   // arms the callback before start_wait takes this lock, and 863   // arms the callback before start_wait takes this lock, and
876   // start_wait consumes this flag. 864   // start_wait consumes this flag.
HITCBC 877   690 impl.token_cancelled_ = true; 865   717 impl.token_cancelled_ = true;
HITCBC 878   690 if (impl.waiting_) 866   717 if (impl.waiting_)
879   { 867   {
HITCBC 880   610 was_waiting = true; 868   650 was_waiting = true;
HITCBC 881   610 impl.waiting_ = false; 869   650 impl.waiting_ = false;
HITCBC 882   610 op = &impl.pending_op_; 870   650 op = &impl.pending_op_;
883   } 871   }
HITCBC 884   690 } 872   717 }
885   873  
HITCBC 886   690 if (was_waiting) 874   717 if (was_waiting)
887   { 875   {
HITCBC 888   610 if (op->ec_out) 876   650 if (op->ec_out)
HITCBC 889   610 *op->ec_out = make_error_code(capy::error::canceled); 877   650 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 890   610 if (op->signal_out) 878   650 if (op->signal_out)
HITCBC 891   610 *op->signal_out = 0; 879   650 *op->signal_out = 0;
HITCBC 892   610 op->cont.h = op->h; 880   650 op->cont.h = op->h;
HITCBC 893   610 op->d.post(op->cont); 881   650 op->d.post(op->cont);
HITCBC 894   610 sched_->work_finished(); 882   650 sched_->work_finished();
895   } 883   }
HITCBC 896   690 } 884   717 }
897   885  
898   inline void 886   inline void
HITCBC 899   690 posix_signal::token_canceller::operator()() const noexcept 887   717 posix_signal::token_canceller::operator()() const noexcept
900   { 888   {
HITCBC 901   690 self->svc_.cancel_wait_token(*self); 889   717 self->svc_.cancel_wait_token(*self);
HITCBC 902   690 } 890   717 }
903   891  
904   inline void 892   inline void
HITCBC 905   1024 posix_signal_service::start_wait(posix_signal& impl, signal_op* op) 893   1051 posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
906   { 894   {
907   { 895   {
HITCBC 908   1024 std::lock_guard lock(mutex_); 896   1051 std::lock_guard lock(mutex_);
909   897  
910   // Check if cancel() was called before this wait started 898   // Check if cancel() was called before this wait started
HITCBC 911   1024 if (impl.cancelled_) 899   1051 if (impl.cancelled_)
912   { 900   {
HITCBC 913   2 impl.cancelled_ = false; 901   2 impl.cancelled_ = false;
HITCBC 914   2 if (op->ec_out) 902   2 if (op->ec_out)
HITCBC 915   2 *op->ec_out = make_error_code(capy::error::canceled); 903   2 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 916   2 if (op->signal_out) 904   2 if (op->signal_out)
HITCBC 917   2 *op->signal_out = 0; 905   2 *op->signal_out = 0;
HITCBC 918   2 op->cont.h = op->h; 906   2 op->cont.h = op->h;
HITCBC 919   2 op->d.post(op->cont); 907   2 op->d.post(op->cont);
HITCBC 920   2 return; 908   2 return;
921   } 909   }
922   910  
923   // A stop request that arrived between wait() arming the callback 911   // A stop request that arrived between wait() arming the callback
924   // and this lock: complete now rather than parking forever. 912   // and this lock: complete now rather than parking forever.
HITCBC 925   1022 if (impl.token_cancelled_) 913   1049 if (impl.token_cancelled_)
926   { 914   {
HITCBC 927   78 impl.token_cancelled_ = false; 915   65 impl.token_cancelled_ = false;
HITCBC 928   78 if (op->ec_out) 916   65 if (op->ec_out)
HITCBC 929   78 *op->ec_out = make_error_code(capy::error::canceled); 917   65 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 930   78 if (op->signal_out) 918   65 if (op->signal_out)
HITCBC 931   78 *op->signal_out = 0; 919   65 *op->signal_out = 0;
HITCBC 932   78 op->cont.h = op->h; 920   65 op->cont.h = op->h;
HITCBC 933   78 op->d.post(op->cont); 921   65 op->d.post(op->cont);
HITCBC 934   78 return; 922   65 return;
935   } 923   }
936   924  
937   // Check for queued signals first (signal arrived before wait started) 925   // Check for queued signals first (signal arrived before wait started)
HITCBC 938   944 signal_registration* reg = impl.signals_; 926   984 signal_registration* reg = impl.signals_;
HITCBC 939   1890 while (reg) 927   1970 while (reg)
940   { 928   {
HITCBC 941   948 if (reg->undelivered > 0) 929   988 if (reg->undelivered > 0)
942   { 930   {
HITCBC 943   2 --reg->undelivered; 931   2 --reg->undelivered;
HITCBC 944   2 op->signal_number = reg->signal_number; 932   2 op->signal_number = reg->signal_number;
945   // 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 946   2 op->svc = nullptr; 934   2 op->svc = nullptr;
HITCBC 947   2 sched_->post(op); 935   2 sched_->post(op);
HITCBC 948   2 return; 936   2 return;
949   } 937   }
HITCBC 950   946 reg = reg->next_in_set; 938   986 reg = reg->next_in_set;
951   } 939   }
952   940  
953   // No queued signals - wait for delivery 941   // No queued signals - wait for delivery
HITCBC 954   942 impl.waiting_ = true; 942   982 impl.waiting_ = true;
955   // 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 956   942 op->svc = this; 944   982 op->svc = this;
HITCBC 957   942 sched_->work_started(); 945   982 sched_->work_started();
HITCBC 958   1024 } 946   1051 }
959   } 947   }
960   948  
961   inline void 949   inline void
HITCBC 962   317 posix_signal_service::deliver_signal(int signal_number) 950   317 posix_signal_service::deliver_signal(int signal_number)
963   { 951   {
HITCBC 964   317 if (signal_number < 0 || signal_number >= max_signal_number) 952   317 if (signal_number < 0 || signal_number >= max_signal_number)
MISUBC 965   ✗ return; 953   ✗ return;
966   954  
967   posix_signal_detail::signal_state* state = 955   posix_signal_detail::signal_state* state =
HITCBC 968   317 posix_signal_detail::get_signal_state(); 956   317 posix_signal_detail::get_signal_state();
HITCBC 969   317 std::lock_guard lock(state->mutex); 957   317 std::lock_guard lock(state->mutex);
970   958  
HITCBC 971   317 posix_signal_service* service = state->service_list; 959   317 posix_signal_service* service = state->service_list;
HITCBC 972   634 while (service) 960   634 while (service)
973   { 961   {
HITCBC 974   317 std::lock_guard svc_lock(service->mutex_); 962   317 std::lock_guard svc_lock(service->mutex_);
975   963  
HITCBC 976   317 signal_registration* reg = service->registrations_[signal_number]; 964   317 signal_registration* reg = service->registrations_[signal_number];
HITCBC 977   638 while (reg) 965   638 while (reg)
978   { 966   {
HITCBC 979   321 posix_signal* impl = static_cast<posix_signal*>(reg->owner); 967   321 posix_signal* impl = static_cast<posix_signal*>(reg->owner);
980   968  
HITCBC 981   321 if (impl->waiting_) 969   321 if (impl->waiting_)
982   { 970   {
HITCBC 983   319 impl->waiting_ = false; 971   319 impl->waiting_ = false;
HITCBC 984   319 impl->pending_op_.signal_number = signal_number; 972   319 impl->pending_op_.signal_number = signal_number;
HITCBC 985   319 service->post(&impl->pending_op_); 973   319 service->post(&impl->pending_op_);
986   } 974   }
987   else 975   else
988   { 976   {
HITCBC 989   2 ++reg->undelivered; 977   2 ++reg->undelivered;
990   } 978   }
991   979  
HITCBC 992   321 reg = reg->next_in_table; 980   321 reg = reg->next_in_table;
993   } 981   }
994   982  
HITCBC 995   317 service = service->next_; 983   317 service = service->next_;
HITCBC 996   317 } 984   317 }
HITCBC 997   317 } 985   317 }
998   986  
999   inline void 987   inline void
1000   posix_signal_service::work_started() noexcept 988   posix_signal_service::work_started() noexcept
1001   { 989   {
1002   sched_->work_started(); 990   sched_->work_started();
1003   } 991   }
1004   992  
1005   inline void 993   inline void
HITCBC 1006   319 posix_signal_service::work_finished() noexcept 994   319 posix_signal_service::work_finished() noexcept
1007   { 995   {
HITCBC 1008   319 sched_->work_finished(); 996   319 sched_->work_finished();
HITCBC 1009   319 } 997   319 }
1010   998  
1011   inline void 999   inline void
HITCBC 1012   319 posix_signal_service::post(signal_op* op) 1000   319 posix_signal_service::post(signal_op* op)
1013   { 1001   {
HITCBC 1014   319 sched_->post(op); 1002   319 sched_->post(op);
HITCBC 1015   319 } 1003   319 }
1016   1004  
1017   inline void 1005   inline void
HITCBC 1018   2241 posix_signal_service::add_service(posix_signal_service* service) 1006   165 posix_signal_service::add_service(posix_signal_service* service)
1019   { 1007   {
1020   posix_signal_detail::signal_state* state = 1008   posix_signal_detail::signal_state* state =
HITCBC 1021   2241 posix_signal_detail::get_signal_state(); 1009   165 posix_signal_detail::get_signal_state();
HITCBC 1022   2241 std::lock_guard lock(state->mutex); 1010   165 std::lock_guard lock(state->mutex);
1023   1011  
HITCBC 1024   2241 service->next_ = state->service_list; 1012   165 service->next_ = state->service_list;
HITCBC 1025   2241 service->prev_ = nullptr; 1013   165 service->prev_ = nullptr;
HITCBC 1026   2241 if (state->service_list) 1014   165 if (state->service_list)
HITCBC 1027   11 state->service_list->prev_ = service; 1015   8 state->service_list->prev_ = service;
HITCBC 1028   2241 state->service_list = service; 1016   165 state->service_list = service;
HITCBC 1029   2241 } 1017   165 }
1030   1018  
1031   inline void 1019   inline void
HITCBC 1032   2241 posix_signal_service::remove_service(posix_signal_service* service) 1020   165 posix_signal_service::remove_service(posix_signal_service* service)
1033   { 1021   {
1034   posix_signal_detail::signal_state* state = 1022   posix_signal_detail::signal_state* state =
HITCBC 1035   2241 posix_signal_detail::get_signal_state(); 1023   165 posix_signal_detail::get_signal_state();
HITCBC 1036   2241 std::lock_guard lock(state->mutex); 1024   165 std::lock_guard lock(state->mutex);
1037   1025  
HITCBC 1038   2241 if (service->next_ || service->prev_ || state->service_list == service) 1026   165 if (service->next_ || service->prev_ || state->service_list == service)
1039   { 1027   {
HITCBC 1040   2241 if (state->service_list == service) 1028   165 if (state->service_list == service)
HITCBC 1041   2239 state->service_list = service->next_; 1029   163 state->service_list = service->next_;
HITCBC 1042   2241 if (service->prev_) 1030   165 if (service->prev_)
HITCBC 1043   2 service->prev_->next_ = service->next_; 1031   2 service->prev_->next_ = service->next_;
HITCBC 1044   2241 if (service->next_) 1032   165 if (service->next_)
HITCBC 1045   9 service->next_->prev_ = service->prev_; 1033   6 service->next_->prev_ = service->prev_;
HITCBC 1046   2241 service->next_ = nullptr; 1034   165 service->next_ = nullptr;
HITCBC 1047   2241 service->prev_ = nullptr; 1035   165 service->prev_ = nullptr;
1048 - }  
DCB 1049 - 2241  
1050 - // get_signal_service - factory function  
1051 -  
1052 - inline posix_signal_service&  
1053 - get_signal_service(capy::execution_context& ctx, scheduler& sched)  
DCB 1054 - 2241 {  
1055 - return ctx.make_service<posix_signal_service>(sched);  
ECB 1056   2241 } 1036   }
HITGIC 1057   } 1037   165 }
1058   1038  
1059   } // namespace detail 1039   } // namespace detail
1060   } // namespace boost::corosio 1040   } // namespace boost::corosio
1061   1041  
1062   #endif // BOOST_COROSIO_POSIX 1042   #endif // BOOST_COROSIO_POSIX
1063   1043  
1064   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 1044   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP