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