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