89.54% Lines (274/306) 96.55% Functions (28/29)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
11   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 11   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
12   12  
13   #include <boost/corosio/detail/platform.hpp> 13   #include <boost/corosio/detail/platform.hpp>
14   14  
15   #if BOOST_COROSIO_POSIX 15   #if BOOST_COROSIO_POSIX
16   16  
17   #include <boost/corosio/native/detail/posix/posix_signal.hpp> 17   #include <boost/corosio/native/detail/posix/posix_signal.hpp>
18   18  
19   #include <boost/corosio/detail/config.hpp> 19   #include <boost/corosio/detail/config.hpp>
20   #include <boost/capy/ex/execution_context.hpp> 20   #include <boost/capy/ex/execution_context.hpp>
21   #include <boost/corosio/detail/scheduler.hpp> 21   #include <boost/corosio/detail/scheduler.hpp>
22   #include <boost/capy/error.hpp> 22   #include <boost/capy/error.hpp>
23   23  
24   #include <mutex> 24   #include <mutex>
25   25  
26   #include <signal.h> 26   #include <signal.h>
27   27  
28   /* 28   /*
29   POSIX Signal Service 29   POSIX Signal Service
30   ==================== 30   ====================
31   31  
32   Concrete signal service implementation for POSIX backends. Manages signal 32   Concrete signal service implementation for POSIX backends. Manages signal
33   registrations via sigaction() and dispatches completions through the 33   registrations via sigaction() and dispatches completions through the
34   scheduler. One instance per execution_context, created by 34   scheduler. One instance per execution_context, created by
35   get_signal_service(). 35   get_signal_service().
36   36  
37   See the block comment further down for the full architecture overview. 37   See the block comment further down for the full architecture overview.
38   */ 38   */
39   39  
40   /* 40   /*
41   POSIX Signal Implementation 41   POSIX Signal Implementation
42   =========================== 42   ===========================
43   43  
44   This file implements signal handling for POSIX systems using sigaction(). 44   This file implements signal handling for POSIX systems using sigaction().
45   The implementation supports signal flags (SA_RESTART, etc.) and integrates 45   The implementation supports signal flags (SA_RESTART, etc.) and integrates
46   with any POSIX-compatible scheduler via the abstract scheduler interface. 46   with any POSIX-compatible scheduler via the abstract scheduler interface.
47   47  
48   Architecture Overview 48   Architecture Overview
49   --------------------- 49   ---------------------
50   50  
51   Three layers manage signal registrations: 51   Three layers manage signal registrations:
52   52  
53   1. signal_state (global singleton) 53   1. signal_state (global singleton)
54   - Tracks the global service list and per-signal registration counts 54   - Tracks the global service list and per-signal registration counts
55   - Stores the flags used for first registration of each signal (for 55   - Stores the flags used for first registration of each signal (for
56   conflict detection when multiple signal_sets register same signal) 56   conflict detection when multiple signal_sets register same signal)
57   - Owns the mutex that protects signal handler installation/removal 57   - Owns the mutex that protects signal handler installation/removal
58   58  
59   2. posix_signal_service (one per execution_context) 59   2. posix_signal_service (one per execution_context)
60   - Maintains registrations_[] table indexed by signal number 60   - Maintains registrations_[] table indexed by signal number
61   - Each slot is a doubly-linked list of signal_registrations for that signal 61   - Each slot is a doubly-linked list of signal_registrations for that signal
62   - Also maintains impl_list_ of all posix_signal objects it owns 62   - Also maintains impl_list_ of all posix_signal objects it owns
63   63  
64   3. posix_signal (one per signal_set) 64   3. posix_signal (one per signal_set)
65   - Owns a singly-linked list (sorted by signal number) of signal_registrations 65   - Owns a singly-linked list (sorted by signal number) of signal_registrations
66   - Contains the pending_op_ used for wait operations 66   - Contains the pending_op_ used for wait operations
67   67  
68   Signal Delivery Flow 68   Signal Delivery Flow
69   -------------------- 69   --------------------
70   70  
71   1. Signal arrives -> corosio_posix_signal_handler() (must be async-signal-safe) 71   1. Signal arrives -> corosio_posix_signal_handler() (must be async-signal-safe)
72   -> deliver_signal() 72   -> deliver_signal()
73   73  
74   2. deliver_signal() iterates all posix_signal_service services: 74   2. deliver_signal() iterates all posix_signal_service services:
75   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op 75   - If a signal_set is waiting (impl->waiting_ == true), post the signal_op
76   to the scheduler for immediate completion 76   to the scheduler for immediate completion
77   - Otherwise, increment reg->undelivered to queue the signal 77   - Otherwise, increment reg->undelivered to queue the signal
78   78  
79   3. When wait() is called via start_wait(): 79   3. When wait() is called via start_wait():
80   - First check for queued signals (undelivered > 0); if found, post 80   - First check for queued signals (undelivered > 0); if found, post
81   immediate completion without blocking 81   immediate completion without blocking
82   - Otherwise, set waiting_ = true and call work_started() to keep 82   - Otherwise, set waiting_ = true and call work_started() to keep
83   the io_context alive 83   the io_context alive
84   84  
85   Locking Protocol 85   Locking Protocol
86   ---------------- 86   ----------------
87   87  
88   Two mutex levels exist (MUST acquire in this order to avoid deadlock): 88   Two mutex levels exist (MUST acquire in this order to avoid deadlock):
89   1. signal_state::mutex - protects handler registration and service list 89   1. signal_state::mutex - protects handler registration and service list
90   2. posix_signal_service::mutex_ - protects per-service registration tables 90   2. posix_signal_service::mutex_ - protects per-service registration tables
91   91  
92   Async-Signal-Safety Limitation 92   Async-Signal-Safety Limitation
93   ------------------------------ 93   ------------------------------
94   94  
95   IMPORTANT: deliver_signal() is called from signal handler context and 95   IMPORTANT: deliver_signal() is called from signal handler context and
96   acquires mutexes. This is NOT strictly async-signal-safe per POSIX. 96   acquires mutexes. This is NOT strictly async-signal-safe per POSIX.
97   The limitation: 97   The limitation:
98   - If a signal arrives while another thread holds state->mutex or 98   - If a signal arrives while another thread holds state->mutex or
99   service->mutex_, and that same thread receives the signal, a 99   service->mutex_, and that same thread receives the signal, a
100   deadlock can occur (self-deadlock on non-recursive mutex). 100   deadlock can occur (self-deadlock on non-recursive mutex).
101   101  
102   This design trades strict async-signal-safety for implementation simplicity. 102   This design trades strict async-signal-safety for implementation simplicity.
103   In practice, deadlocks are rare because: 103   In practice, deadlocks are rare because:
104   - Mutexes are held only briefly during registration changes 104   - Mutexes are held only briefly during registration changes
105   - Most programs don't modify signal sets while signals are expected 105   - Most programs don't modify signal sets while signals are expected
106   - The window for signal arrival during mutex hold is small 106   - The window for signal arrival during mutex hold is small
107   107  
108   A fully async-signal-safe implementation would require lock-free data 108   A fully async-signal-safe implementation would require lock-free data
109   structures and atomic operations throughout, significantly increasing 109   structures and atomic operations throughout, significantly increasing
110   complexity. 110   complexity.
111   111  
112   Flag Handling 112   Flag Handling
113   ------------- 113   -------------
114   114  
115   - Flags are abstract values in the public API (signal_set::flags_t) 115   - Flags are abstract values in the public API (signal_set::flags_t)
116   - flags_supported() validates that requested flags are available on 116   - flags_supported() validates that requested flags are available on
117   this platform; returns false if SA_NOCLDWAIT is unavailable and 117   this platform; returns false if SA_NOCLDWAIT is unavailable and
118   no_child_wait is requested 118   no_child_wait is requested
119   - to_sigaction_flags() maps validated flags to actual SA_* constants 119   - to_sigaction_flags() maps validated flags to actual SA_* constants
120   - First registration of a signal establishes the flags; subsequent 120   - First registration of a signal establishes the flags; subsequent
121   registrations must be compatible (same flags or dont_care) 121   registrations must be compatible (same flags or dont_care)
122   - Requesting unavailable flags returns operation_not_supported 122   - Requesting unavailable flags returns operation_not_supported
123   123  
124   Work Tracking 124   Work Tracking
125   ------------- 125   -------------
126   126  
127   When waiting for a signal: 127   When waiting for a signal:
128   - start_wait() calls sched_->work_started() to prevent io_context::run() 128   - start_wait() calls sched_->work_started() to prevent io_context::run()
129   from returning while we wait 129   from returning while we wait
130   - signal_op::svc is set to point to the service 130   - signal_op::svc is set to point to the service
131   - signal_op::operator()() calls work_finished() after resuming the coroutine 131   - signal_op::operator()() calls work_finished() after resuming the coroutine
132   132  
133   If a signal was already queued (undelivered > 0), no work tracking is needed 133   If a signal was already queued (undelivered > 0), no work tracking is needed
134   because completion is posted immediately. 134   because completion is posted immediately.
135   */ 135   */
136   136  
137   namespace boost::corosio { 137   namespace boost::corosio {
138   138  
139   namespace detail { 139   namespace detail {
140   140  
141   /** Signal service for POSIX backends. 141   /** Signal service for POSIX backends.
142   142  
143   Manages signal registrations via sigaction() and dispatches signal 143   Manages signal registrations via sigaction() and dispatches signal
144   completions through the scheduler. One instance per execution_context. 144   completions through the scheduler. One instance per execution_context.
145   */ 145   */
146   class BOOST_COROSIO_DECL posix_signal_service final 146   class BOOST_COROSIO_DECL posix_signal_service final
147   : public capy::execution_context::service 147   : public capy::execution_context::service
148   , public io_object::io_service 148   , public io_object::io_service
149   { 149   {
150   public: 150   public:
151   using key_type = posix_signal_service; 151   using key_type = posix_signal_service;
152   152  
153   posix_signal_service(capy::execution_context& ctx, scheduler& sched); 153   posix_signal_service(capy::execution_context& ctx, scheduler& sched);
154   ~posix_signal_service() override; 154   ~posix_signal_service() override;
155   155  
156   posix_signal_service(posix_signal_service const&) = delete; 156   posix_signal_service(posix_signal_service const&) = delete;
157   posix_signal_service& operator=(posix_signal_service const&) = delete; 157   posix_signal_service& operator=(posix_signal_service const&) = delete;
158   158  
159   io_object::implementation* construct() override; 159   io_object::implementation* construct() override;
160   160  
HITCBC 161   90 void destroy(io_object::implementation* p) override 161   90 void destroy(io_object::implementation* p) override
162   { 162   {
HITCBC 163   90 auto& impl = static_cast<posix_signal&>(*p); 163   90 auto& impl = static_cast<posix_signal&>(*p);
HITCBC 164   90 [[maybe_unused]] auto n = impl.clear(); 164   90 [[maybe_unused]] auto n = impl.clear();
HITCBC 165   90 impl.cancel(); 165   90 impl.cancel();
HITCBC 166   90 destroy_impl(impl); 166   90 destroy_impl(impl);
HITCBC 167   90 } 167   90 }
168   168  
169   void shutdown() override; 169   void shutdown() override;
170   170  
171   void destroy_impl(posix_signal& impl); 171   void destroy_impl(posix_signal& impl);
172   172  
173   std::error_code add_signal( 173   std::error_code add_signal(
174   posix_signal& impl, int signal_number, signal_set::flags_t flags); 174   posix_signal& impl, int signal_number, signal_set::flags_t flags);
175   175  
176   std::error_code remove_signal(posix_signal& impl, int signal_number); 176   std::error_code remove_signal(posix_signal& impl, int signal_number);
177   177  
178   std::error_code clear_signals(posix_signal& impl); 178   std::error_code clear_signals(posix_signal& impl);
179   179  
180   void cancel_wait(posix_signal& impl); 180   void cancel_wait(posix_signal& impl);
181   void start_wait(posix_signal& impl, signal_op* op); 181   void start_wait(posix_signal& impl, signal_op* op);
182   182  
183   static void deliver_signal(int signal_number); 183   static void deliver_signal(int signal_number);
184   184  
185   void work_started() noexcept; 185   void work_started() noexcept;
186   void work_finished() noexcept; 186   void work_finished() noexcept;
187   void post(signal_op* op); 187   void post(signal_op* op);
188   188  
189   private: 189   private:
190   static void add_service(posix_signal_service* service); 190   static void add_service(posix_signal_service* service);
191   static void remove_service(posix_signal_service* service); 191   static void remove_service(posix_signal_service* service);
192   192  
193   scheduler* sched_; 193   scheduler* sched_;
194   std::mutex mutex_; 194   std::mutex mutex_;
195   intrusive_list<posix_signal> impl_list_; 195   intrusive_list<posix_signal> impl_list_;
196   196  
197   // Per-signal registration table 197   // Per-signal registration table
198   signal_registration* registrations_[max_signal_number]; 198   signal_registration* registrations_[max_signal_number];
199   199  
200   // Registration counts for each signal 200   // Registration counts for each signal
201   std::size_t registration_count_[max_signal_number]; 201   std::size_t registration_count_[max_signal_number];
202   202  
203   // Linked list of all posix_signal_service services for signal delivery 203   // Linked list of all posix_signal_service services for signal delivery
204   posix_signal_service* next_ = nullptr; 204   posix_signal_service* next_ = nullptr;
205   posix_signal_service* prev_ = nullptr; 205   posix_signal_service* prev_ = nullptr;
206   }; 206   };
207   207  
208   /** Get or create the signal service for the given context. 208   /** Get or create the signal service for the given context.
209   209  
210   This function is called by the concrete scheduler during initialization 210   This function is called by the concrete scheduler during initialization
211   to create the signal service with a reference to itself. 211   to create the signal service with a reference to itself.
212   212  
213   @param ctx Reference to the owning execution_context. 213   @param ctx Reference to the owning execution_context.
214   @param sched Reference to the scheduler for posting completions. 214   @param sched Reference to the scheduler for posting completions.
215   @return Reference to the signal service. 215   @return Reference to the signal service.
216   */ 216   */
217   posix_signal_service& 217   posix_signal_service&
218   get_signal_service(capy::execution_context& ctx, scheduler& sched); 218   get_signal_service(capy::execution_context& ctx, scheduler& sched);
219   219  
220   } // namespace detail 220   } // namespace detail
221   221  
222   } // namespace boost::corosio 222   } // namespace boost::corosio
223   223  
224   // --------------------------------------------------------------------------- 224   // ---------------------------------------------------------------------------
225   // Inline implementation 225   // Inline implementation
226   // --------------------------------------------------------------------------- 226   // ---------------------------------------------------------------------------
227   227  
228   namespace boost::corosio { 228   namespace boost::corosio {
229   229  
230   namespace detail { 230   namespace detail {
231   231  
232   namespace posix_signal_detail { 232   namespace posix_signal_detail {
233   233  
234   struct signal_state 234   struct signal_state
235   { 235   {
236   std::mutex mutex; 236   std::mutex mutex;
237   posix_signal_service* service_list = nullptr; 237   posix_signal_service* service_list = nullptr;
238   std::size_t registration_count[max_signal_number] = {}; 238   std::size_t registration_count[max_signal_number] = {};
239   signal_set::flags_t registered_flags[max_signal_number] = {}; 239   signal_set::flags_t registered_flags[max_signal_number] = {};
240   }; 240   };
241   241  
242   BOOST_COROSIO_DECL signal_state* get_signal_state(); 242   BOOST_COROSIO_DECL signal_state* get_signal_state();
243   243  
244   // Check if requested flags are supported on this platform. 244   // Check if requested flags are supported on this platform.
245   // Returns true if all flags are supported, false otherwise. 245   // Returns true if all flags are supported, false otherwise.
246   inline bool 246   inline bool
HITCBC 247   96 flags_supported([[maybe_unused]] signal_set::flags_t flags) 247   96 flags_supported([[maybe_unused]] signal_set::flags_t flags)
248   { 248   {
249   #ifndef SA_NOCLDWAIT 249   #ifndef SA_NOCLDWAIT
250   if (flags & signal_set::no_child_wait) 250   if (flags & signal_set::no_child_wait)
251   return false; 251   return false;
252   #endif 252   #endif
HITCBC 253   96 return true; 253   96 return true;
254   } 254   }
255   255  
256   // Map abstract flags to sigaction() flags. 256   // Map abstract flags to sigaction() flags.
257   // Caller must ensure flags_supported() returns true first. 257   // Caller must ensure flags_supported() returns true first.
258   inline int 258   inline int
HITCBC 259   78 to_sigaction_flags(signal_set::flags_t flags) 259   78 to_sigaction_flags(signal_set::flags_t flags)
260   { 260   {
HITCBC 261   78 int sa_flags = 0; 261   78 int sa_flags = 0;
HITCBC 262   78 if (flags & signal_set::restart) 262   78 if (flags & signal_set::restart)
HITCBC 263   18 sa_flags |= SA_RESTART; 263   18 sa_flags |= SA_RESTART;
HITCBC 264   78 if (flags & signal_set::no_child_stop) 264   78 if (flags & signal_set::no_child_stop)
MISUBC 265   sa_flags |= SA_NOCLDSTOP; 265   sa_flags |= SA_NOCLDSTOP;
266   #ifdef SA_NOCLDWAIT 266   #ifdef SA_NOCLDWAIT
HITCBC 267   78 if (flags & signal_set::no_child_wait) 267   78 if (flags & signal_set::no_child_wait)
MISUBC 268   sa_flags |= SA_NOCLDWAIT; 268   sa_flags |= SA_NOCLDWAIT;
269   #endif 269   #endif
HITCBC 270   78 if (flags & signal_set::no_defer) 270   78 if (flags & signal_set::no_defer)
HITCBC 271   2 sa_flags |= SA_NODEFER; 271   2 sa_flags |= SA_NODEFER;
HITCBC 272   78 if (flags & signal_set::reset_handler) 272   78 if (flags & signal_set::reset_handler)
MISUBC 273   sa_flags |= SA_RESETHAND; 273   sa_flags |= SA_RESETHAND;
HITCBC 274   78 return sa_flags; 274   78 return sa_flags;
275   } 275   }
276   276  
277   // Check if two flag values are compatible 277   // Check if two flag values are compatible
278   inline bool 278   inline bool
HITCBC 279   18 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested) 279   18 flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
280   { 280   {
281   // dont_care is always compatible 281   // dont_care is always compatible
HITCBC 282   34 if ((existing & signal_set::dont_care) || 282   34 if ((existing & signal_set::dont_care) ||
HITCBC 283   16 (requested & signal_set::dont_care)) 283   16 (requested & signal_set::dont_care))
HITCBC 284   6 return true; 284   6 return true;
285   285  
286   // Mask out dont_care bit for comparison 286   // Mask out dont_care bit for comparison
HITCBC 287   12 constexpr auto mask = ~signal_set::dont_care; 287   12 constexpr auto mask = ~signal_set::dont_care;
HITCBC 288   12 return (existing & mask) == (requested & mask); 288   12 return (existing & mask) == (requested & mask);
289   } 289   }
290   290  
291   // C signal handler - must be async-signal-safe 291   // C signal handler - must be async-signal-safe
292   inline void 292   inline void
HITCBC 293   20 corosio_posix_signal_handler(int signal_number) 293   20 corosio_posix_signal_handler(int signal_number)
294   { 294   {
HITCBC 295   20 posix_signal_service::deliver_signal(signal_number); 295   20 posix_signal_service::deliver_signal(signal_number);
296   // Note: With sigaction(), the handler persists automatically 296   // Note: With sigaction(), the handler persists automatically
297   // (unlike some signal() implementations that reset to SIG_DFL) 297   // (unlike some signal() implementations that reset to SIG_DFL)
HITCBC 298   20 } 298   20 }
299   299  
300   } // namespace posix_signal_detail 300   } // namespace posix_signal_detail
301   301  
302   // signal_op implementation 302   // signal_op implementation
303   303  
304   inline void 304   inline void
HITCBC 305   22 signal_op::operator()() 305   22 signal_op::operator()()
306   { 306   {
HITCBC 307   22 if (ec_out) 307   22 if (ec_out)
HITCBC 308   22 *ec_out = {}; 308   22 *ec_out = {};
HITCBC 309   22 if (signal_out) 309   22 if (signal_out)
HITCBC 310   22 *signal_out = signal_number; 310   22 *signal_out = signal_number;
311   311  
312   // Capture svc before resuming (coro may destroy us) 312   // Capture svc before resuming (coro may destroy us)
HITCBC 313   22 auto* service = svc; 313   22 auto* service = svc;
HITCBC 314   22 svc = nullptr; 314   22 svc = nullptr;
315   315  
HITCBC 316   22 cont_op.cont.h = h; 316   22 cont_op.cont.h = h;
HITCBC 317   22 d.post(cont_op.cont); 317   22 d.post(cont_op.cont);
318   318  
319   // Balance the work_started() from start_wait 319   // Balance the work_started() from start_wait
HITCBC 320   22 if (service) 320   22 if (service)
HITCBC 321   12 service->work_finished(); 321   12 service->work_finished();
HITCBC 322   22 } 322   22 }
323   323  
324   inline void 324   inline void
MISUBC 325   signal_op::destroy() 325   signal_op::destroy()
326   { 326   {
327   // No-op: signal_op is embedded in posix_signal 327   // No-op: signal_op is embedded in posix_signal
MISUBC 328   } 328   }
329   329  
330   // posix_signal implementation 330   // posix_signal implementation
331   331  
HITCBC 332   90 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept 332   90 inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
HITCBC 333   90 : svc_(svc) 333   90 : svc_(svc)
334   { 334   {
HITCBC 335   90 } 335   90 }
336   336  
337   inline std::coroutine_handle<> 337   inline std::coroutine_handle<>
HITCBC 338   28 posix_signal::wait( 338   28 posix_signal::wait(
339   std::coroutine_handle<> h, 339   std::coroutine_handle<> h,
340   capy::executor_ref d, 340   capy::executor_ref d,
341   std::stop_token token, 341   std::stop_token token,
342   std::error_code* ec, 342   std::error_code* ec,
343   int* signal_out) 343   int* signal_out)
344   { 344   {
HITCBC 345   28 pending_op_.h = h; 345   28 pending_op_.h = h;
HITCBC 346   28 pending_op_.d = d; 346   28 pending_op_.d = d;
HITCBC 347   28 pending_op_.ec_out = ec; 347   28 pending_op_.ec_out = ec;
HITCBC 348   28 pending_op_.signal_out = signal_out; 348   28 pending_op_.signal_out = signal_out;
HITCBC 349   28 pending_op_.signal_number = 0; 349   28 pending_op_.signal_number = 0;
350   350  
HITCBC 351   28 if (token.stop_requested()) 351   28 if (token.stop_requested())
352   { 352   {
MISUBC 353   if (ec) 353   if (ec)
MISUBC 354   *ec = make_error_code(capy::error::canceled); 354   *ec = make_error_code(capy::error::canceled);
MISUBC 355   if (signal_out) 355   if (signal_out)
MISUBC 356   *signal_out = 0; 356   *signal_out = 0;
MISUBC 357   pending_op_.cont_op.cont.h = h; 357   pending_op_.cont_op.cont.h = h;
MISUBC 358   d.post(pending_op_.cont_op.cont); 358   d.post(pending_op_.cont_op.cont);
359   // completion is always posted to scheduler queue, never inline. 359   // completion is always posted to scheduler queue, never inline.
MISUBC 360   return std::noop_coroutine(); 360   return std::noop_coroutine();
361   } 361   }
362   362  
HITCBC 363   28 svc_.start_wait(*this, &pending_op_); 363   28 svc_.start_wait(*this, &pending_op_);
364   // completion is always posted to scheduler queue, never inline. 364   // completion is always posted to scheduler queue, never inline.
HITCBC 365   28 return std::noop_coroutine(); 365   28 return std::noop_coroutine();
366   } 366   }
367   367  
368   inline std::error_code 368   inline std::error_code
HITCBC 369   98 posix_signal::add(int signal_number, signal_set::flags_t flags) 369   98 posix_signal::add(int signal_number, signal_set::flags_t flags)
370   { 370   {
HITCBC 371   98 return svc_.add_signal(*this, signal_number, flags); 371   98 return svc_.add_signal(*this, signal_number, flags);
372   } 372   }
373   373  
374   inline std::error_code 374   inline std::error_code
HITCBC 375   4 posix_signal::remove(int signal_number) 375   4 posix_signal::remove(int signal_number)
376   { 376   {
HITCBC 377   4 return svc_.remove_signal(*this, signal_number); 377   4 return svc_.remove_signal(*this, signal_number);
378   } 378   }
379   379  
380   inline std::error_code 380   inline std::error_code
HITCBC 381   94 posix_signal::clear() 381   94 posix_signal::clear()
382   { 382   {
HITCBC 383   94 return svc_.clear_signals(*this); 383   94 return svc_.clear_signals(*this);
384   } 384   }
385   385  
386   inline void 386   inline void
HITCBC 387   104 posix_signal::cancel() 387   104 posix_signal::cancel()
388   { 388   {
HITCBC 389   104 svc_.cancel_wait(*this); 389   104 svc_.cancel_wait(*this);
HITCBC 390   104 } 390   104 }
391   391  
392   // posix_signal_service implementation 392   // posix_signal_service implementation
393   393  
HITCBC 394   605 inline posix_signal_service::posix_signal_service( 394   605 inline posix_signal_service::posix_signal_service(
HITCBC 395   605 capy::execution_context&, scheduler& sched) 395   605 capy::execution_context&, scheduler& sched)
HITCBC 396   605 : sched_(&sched) 396   605 : sched_(&sched)
397   { 397   {
HITCBC 398   39325 for (int i = 0; i < max_signal_number; ++i) 398   39325 for (int i = 0; i < max_signal_number; ++i)
399   { 399   {
HITCBC 400   38720 registrations_[i] = nullptr; 400   38720 registrations_[i] = nullptr;
HITCBC 401   38720 registration_count_[i] = 0; 401   38720 registration_count_[i] = 0;
402   } 402   }
HITCBC 403   605 add_service(this); 403   605 add_service(this);
HITCBC 404   605 } 404   605 }
405   405  
HITCBC 406   1210 inline posix_signal_service::~posix_signal_service() 406   1210 inline posix_signal_service::~posix_signal_service()
407   { 407   {
HITCBC 408   605 remove_service(this); 408   605 remove_service(this);
HITCBC 409   1210 } 409   1210 }
410   410  
411   inline void 411   inline void
HITCBC 412   605 posix_signal_service::shutdown() 412   605 posix_signal_service::shutdown()
413   { 413   {
HITCBC 414   605 std::lock_guard lock(mutex_); 414   605 std::lock_guard lock(mutex_);
415   415  
HITCBC 416   605 for (auto* impl = impl_list_.pop_front(); impl != nullptr; 416   605 for (auto* impl = impl_list_.pop_front(); impl != nullptr;
MISUBC 417   impl = impl_list_.pop_front()) 417   impl = impl_list_.pop_front())
418   { 418   {
MISUBC 419   while (auto* reg = impl->signals_) 419   while (auto* reg = impl->signals_)
420   { 420   {
MISUBC 421   impl->signals_ = reg->next_in_set; 421   impl->signals_ = reg->next_in_set;
MISUBC 422   delete reg; 422   delete reg;
MISUBC 423   } 423   }
MISUBC 424   delete impl; 424   delete impl;
425   } 425   }
HITCBC 426   605 } 426   605 }
427   427  
428   inline io_object::implementation* 428   inline io_object::implementation*
HITCBC 429   90 posix_signal_service::construct() 429   90 posix_signal_service::construct()
430   { 430   {
HITCBC 431   90 auto* impl = new posix_signal(*this); 431   90 auto* impl = new posix_signal(*this);
432   432  
433   { 433   {
HITCBC 434   90 std::lock_guard lock(mutex_); 434   90 std::lock_guard lock(mutex_);
HITCBC 435   90 impl_list_.push_back(impl); 435   90 impl_list_.push_back(impl);
HITCBC 436   90 } 436   90 }
437   437  
HITCBC 438   90 return impl; 438   90 return impl;
439   } 439   }
440   440  
441   inline void 441   inline void
HITCBC 442   90 posix_signal_service::destroy_impl(posix_signal& impl) 442   90 posix_signal_service::destroy_impl(posix_signal& impl)
443   { 443   {
444   { 444   {
HITCBC 445   90 std::lock_guard lock(mutex_); 445   90 std::lock_guard lock(mutex_);
HITCBC 446   90 impl_list_.remove(&impl); 446   90 impl_list_.remove(&impl);
HITCBC 447   90 } 447   90 }
448   448  
HITCBC 449   90 delete &impl; 449   90 delete &impl;
HITCBC 450   90 } 450   90 }
451   451  
452   inline std::error_code 452   inline std::error_code
HITCBC 453   98 posix_signal_service::add_signal( 453   98 posix_signal_service::add_signal(
454   posix_signal& impl, int signal_number, signal_set::flags_t flags) 454   posix_signal& impl, int signal_number, signal_set::flags_t flags)
455   { 455   {
HITCBC 456   98 if (signal_number < 0 || signal_number >= max_signal_number) 456   98 if (signal_number < 0 || signal_number >= max_signal_number)
HITCBC 457   2 return make_error_code(std::errc::invalid_argument); 457   2 return make_error_code(std::errc::invalid_argument);
458   458  
459   // Validate that requested flags are supported on this platform 459   // Validate that requested flags are supported on this platform
460   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems) 460   // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
HITCBC 461   96 if (!posix_signal_detail::flags_supported(flags)) 461   96 if (!posix_signal_detail::flags_supported(flags))
MISUBC 462   return make_error_code(std::errc::operation_not_supported); 462   return make_error_code(std::errc::operation_not_supported);
463   463  
464   posix_signal_detail::signal_state* state = 464   posix_signal_detail::signal_state* state =
HITCBC 465   96 posix_signal_detail::get_signal_state(); 465   96 posix_signal_detail::get_signal_state();
HITCBC 466   96 std::lock_guard state_lock(state->mutex); 466   96 std::lock_guard state_lock(state->mutex);
HITCBC 467   96 std::lock_guard lock(mutex_); 467   96 std::lock_guard lock(mutex_);
468   468  
469   // Find insertion point (list is sorted by signal number) 469   // Find insertion point (list is sorted by signal number)
HITCBC 470   96 signal_registration** insertion_point = &impl.signals_; 470   96 signal_registration** insertion_point = &impl.signals_;
HITCBC 471   96 signal_registration* reg = impl.signals_; 471   96 signal_registration* reg = impl.signals_;
HITCBC 472   106 while (reg && reg->signal_number < signal_number) 472   106 while (reg && reg->signal_number < signal_number)
473   { 473   {
HITCBC 474   10 insertion_point = &reg->next_in_set; 474   10 insertion_point = &reg->next_in_set;
HITCBC 475   10 reg = reg->next_in_set; 475   10 reg = reg->next_in_set;
476   } 476   }
477   477  
478   // Already registered in this set - check flag compatibility 478   // Already registered in this set - check flag compatibility
479   // (same signal_set adding same signal twice with different flags) 479   // (same signal_set adding same signal twice with different flags)
HITCBC 480   96 if (reg && reg->signal_number == signal_number) 480   96 if (reg && reg->signal_number == signal_number)
481   { 481   {
HITCBC 482   10 if (!posix_signal_detail::flags_compatible(reg->flags, flags)) 482   10 if (!posix_signal_detail::flags_compatible(reg->flags, flags))
HITCBC 483   2 return make_error_code(std::errc::invalid_argument); 483   2 return make_error_code(std::errc::invalid_argument);
HITCBC 484   8 return {}; 484   8 return {};
485   } 485   }
486   486  
487   // Check flag compatibility with global registration 487   // Check flag compatibility with global registration
488   // (different signal_set already registered this signal with different flags) 488   // (different signal_set already registered this signal with different flags)
HITCBC 489   86 if (state->registration_count[signal_number] > 0) 489   86 if (state->registration_count[signal_number] > 0)
490   { 490   {
HITCBC 491   8 if (!posix_signal_detail::flags_compatible( 491   8 if (!posix_signal_detail::flags_compatible(
492   state->registered_flags[signal_number], flags)) 492   state->registered_flags[signal_number], flags))
HITCBC 493   2 return make_error_code(std::errc::invalid_argument); 493   2 return make_error_code(std::errc::invalid_argument);
494   } 494   }
495   495  
HITCBC 496   84 auto* new_reg = new signal_registration; 496   84 auto* new_reg = new signal_registration;
HITCBC 497   84 new_reg->signal_number = signal_number; 497   84 new_reg->signal_number = signal_number;
HITCBC 498   84 new_reg->flags = flags; 498   84 new_reg->flags = flags;
HITCBC 499   84 new_reg->owner = &impl; 499   84 new_reg->owner = &impl;
HITCBC 500   84 new_reg->undelivered = 0; 500   84 new_reg->undelivered = 0;
501   501  
502   // Install signal handler on first global registration 502   // Install signal handler on first global registration
HITCBC 503   84 if (state->registration_count[signal_number] == 0) 503   84 if (state->registration_count[signal_number] == 0)
504   { 504   {
HITCBC 505   78 struct sigaction sa = {}; 505   78 struct sigaction sa = {};
HITCBC 506   78 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; 506   78 sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
HITCBC 507   78 sigemptyset(&sa.sa_mask); 507   78 sigemptyset(&sa.sa_mask);
HITCBC 508   78 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); 508   78 sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
509   509  
HITCBC 510   78 if (::sigaction(signal_number, &sa, nullptr) < 0) 510   78 if (::sigaction(signal_number, &sa, nullptr) < 0)
511   { 511   {
MISUBC 512   delete new_reg; 512   delete new_reg;
MISUBC 513   return make_error_code(std::errc::invalid_argument); 513   return make_error_code(std::errc::invalid_argument);
514   } 514   }
515   515  
516   // Store the flags used for first registration 516   // Store the flags used for first registration
HITCBC 517   78 state->registered_flags[signal_number] = flags; 517   78 state->registered_flags[signal_number] = flags;
518   } 518   }
519   519  
HITCBC 520   84 new_reg->next_in_set = reg; 520   84 new_reg->next_in_set = reg;
HITCBC 521   84 *insertion_point = new_reg; 521   84 *insertion_point = new_reg;
522   522  
HITCBC 523   84 new_reg->next_in_table = registrations_[signal_number]; 523   84 new_reg->next_in_table = registrations_[signal_number];
HITCBC 524   84 new_reg->prev_in_table = nullptr; 524   84 new_reg->prev_in_table = nullptr;
HITCBC 525   84 if (registrations_[signal_number]) 525   84 if (registrations_[signal_number])
HITCBC 526   6 registrations_[signal_number]->prev_in_table = new_reg; 526   6 registrations_[signal_number]->prev_in_table = new_reg;
HITCBC 527   84 registrations_[signal_number] = new_reg; 527   84 registrations_[signal_number] = new_reg;
528   528  
HITCBC 529   84 ++state->registration_count[signal_number]; 529   84 ++state->registration_count[signal_number];
HITCBC 530   84 ++registration_count_[signal_number]; 530   84 ++registration_count_[signal_number];
531   531  
HITCBC 532   84 return {}; 532   84 return {};
HITCBC 533   96 } 533   96 }
534   534  
535   inline std::error_code 535   inline std::error_code
HITCBC 536   4 posix_signal_service::remove_signal(posix_signal& impl, int signal_number) 536   4 posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
537   { 537   {
HITCBC 538   4 if (signal_number < 0 || signal_number >= max_signal_number) 538   4 if (signal_number < 0 || signal_number >= max_signal_number)
MISUBC 539   return make_error_code(std::errc::invalid_argument); 539   return make_error_code(std::errc::invalid_argument);
540   540  
541   posix_signal_detail::signal_state* state = 541   posix_signal_detail::signal_state* state =
HITCBC 542   4 posix_signal_detail::get_signal_state(); 542   4 posix_signal_detail::get_signal_state();
HITCBC 543   4 std::lock_guard state_lock(state->mutex); 543   4 std::lock_guard state_lock(state->mutex);
HITCBC 544   4 std::lock_guard lock(mutex_); 544   4 std::lock_guard lock(mutex_);
545   545  
HITCBC 546   4 signal_registration** deletion_point = &impl.signals_; 546   4 signal_registration** deletion_point = &impl.signals_;
HITCBC 547   4 signal_registration* reg = impl.signals_; 547   4 signal_registration* reg = impl.signals_;
HITCBC 548   4 while (reg && reg->signal_number < signal_number) 548   4 while (reg && reg->signal_number < signal_number)
549   { 549   {
MISUBC 550   deletion_point = &reg->next_in_set; 550   deletion_point = &reg->next_in_set;
MISUBC 551   reg = reg->next_in_set; 551   reg = reg->next_in_set;
552   } 552   }
553   553  
HITCBC 554   4 if (!reg || reg->signal_number != signal_number) 554   4 if (!reg || reg->signal_number != signal_number)
HITCBC 555   2 return {}; 555   2 return {};
556   556  
557   // Restore default handler on last global unregistration 557   // Restore default handler on last global unregistration
HITCBC 558   2 if (state->registration_count[signal_number] == 1) 558   2 if (state->registration_count[signal_number] == 1)
559   { 559   {
HITCBC 560   2 struct sigaction sa = {}; 560   2 struct sigaction sa = {};
HITCBC 561   2 sa.sa_handler = SIG_DFL; 561   2 sa.sa_handler = SIG_DFL;
HITCBC 562   2 sigemptyset(&sa.sa_mask); 562   2 sigemptyset(&sa.sa_mask);
HITCBC 563   2 sa.sa_flags = 0; 563   2 sa.sa_flags = 0;
564   564  
HITCBC 565   2 if (::sigaction(signal_number, &sa, nullptr) < 0) 565   2 if (::sigaction(signal_number, &sa, nullptr) < 0)
MISUBC 566   return make_error_code(std::errc::invalid_argument); 566   return make_error_code(std::errc::invalid_argument);
567   567  
568   // Clear stored flags 568   // Clear stored flags
HITCBC 569   2 state->registered_flags[signal_number] = signal_set::none; 569   2 state->registered_flags[signal_number] = signal_set::none;
570   } 570   }
571   571  
HITCBC 572   2 *deletion_point = reg->next_in_set; 572   2 *deletion_point = reg->next_in_set;
573   573  
HITCBC 574   2 if (registrations_[signal_number] == reg) 574   2 if (registrations_[signal_number] == reg)
HITCBC 575   2 registrations_[signal_number] = reg->next_in_table; 575   2 registrations_[signal_number] = reg->next_in_table;
HITCBC 576   2 if (reg->prev_in_table) 576   2 if (reg->prev_in_table)
MISUBC 577   reg->prev_in_table->next_in_table = reg->next_in_table; 577   reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 578   2 if (reg->next_in_table) 578   2 if (reg->next_in_table)
MISUBC 579   reg->next_in_table->prev_in_table = reg->prev_in_table; 579   reg->next_in_table->prev_in_table = reg->prev_in_table;
580   580  
HITCBC 581   2 --state->registration_count[signal_number]; 581   2 --state->registration_count[signal_number];
HITCBC 582   2 --registration_count_[signal_number]; 582   2 --registration_count_[signal_number];
583   583  
HITCBC 584   2 delete reg; 584   2 delete reg;
HITCBC 585   2 return {}; 585   2 return {};
HITCBC 586   4 } 586   4 }
587   587  
588   inline std::error_code 588   inline std::error_code
HITCBC 589   94 posix_signal_service::clear_signals(posix_signal& impl) 589   94 posix_signal_service::clear_signals(posix_signal& impl)
590   { 590   {
591   posix_signal_detail::signal_state* state = 591   posix_signal_detail::signal_state* state =
HITCBC 592   94 posix_signal_detail::get_signal_state(); 592   94 posix_signal_detail::get_signal_state();
HITCBC 593   94 std::lock_guard state_lock(state->mutex); 593   94 std::lock_guard state_lock(state->mutex);
HITCBC 594   94 std::lock_guard lock(mutex_); 594   94 std::lock_guard lock(mutex_);
595   595  
HITCBC 596   94 std::error_code first_error; 596   94 std::error_code first_error;
597   597  
HITCBC 598   176 while (signal_registration* reg = impl.signals_) 598   176 while (signal_registration* reg = impl.signals_)
599   { 599   {
HITCBC 600   82 int signal_number = reg->signal_number; 600   82 int signal_number = reg->signal_number;
601   601  
HITCBC 602   82 if (state->registration_count[signal_number] == 1) 602   82 if (state->registration_count[signal_number] == 1)
603   { 603   {
HITCBC 604   76 struct sigaction sa = {}; 604   76 struct sigaction sa = {};
HITCBC 605   76 sa.sa_handler = SIG_DFL; 605   76 sa.sa_handler = SIG_DFL;
HITCBC 606   76 sigemptyset(&sa.sa_mask); 606   76 sigemptyset(&sa.sa_mask);
HITCBC 607   76 sa.sa_flags = 0; 607   76 sa.sa_flags = 0;
608   608  
HITCBC 609   76 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) 609   76 if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
MISUBC 610   first_error = make_error_code(std::errc::invalid_argument); 610   first_error = make_error_code(std::errc::invalid_argument);
611   611  
612   // Clear stored flags 612   // Clear stored flags
HITCBC 613   76 state->registered_flags[signal_number] = signal_set::none; 613   76 state->registered_flags[signal_number] = signal_set::none;
614   } 614   }
615   615  
HITCBC 616   82 impl.signals_ = reg->next_in_set; 616   82 impl.signals_ = reg->next_in_set;
617   617  
HITCBC 618   82 if (registrations_[signal_number] == reg) 618   82 if (registrations_[signal_number] == reg)
HITCBC 619   82 registrations_[signal_number] = reg->next_in_table; 619   82 registrations_[signal_number] = reg->next_in_table;
HITCBC 620   82 if (reg->prev_in_table) 620   82 if (reg->prev_in_table)
MISUBC 621   reg->prev_in_table->next_in_table = reg->next_in_table; 621   reg->prev_in_table->next_in_table = reg->next_in_table;
HITCBC 622   82 if (reg->next_in_table) 622   82 if (reg->next_in_table)
HITCBC 623   6 reg->next_in_table->prev_in_table = reg->prev_in_table; 623   6 reg->next_in_table->prev_in_table = reg->prev_in_table;
624   624  
HITCBC 625   82 --state->registration_count[signal_number]; 625   82 --state->registration_count[signal_number];
HITCBC 626   82 --registration_count_[signal_number]; 626   82 --registration_count_[signal_number];
627   627  
HITCBC 628   82 delete reg; 628   82 delete reg;
HITCBC 629   82 } 629   82 }
630   630  
HITCBC 631   94 if (first_error) 631   94 if (first_error)
MISUBC 632   return first_error; 632   return first_error;
HITCBC 633   94 return {}; 633   94 return {};
HITCBC 634   94 } 634   94 }
635   635  
636   inline void 636   inline void
HITCBC 637   104 posix_signal_service::cancel_wait(posix_signal& impl) 637   104 posix_signal_service::cancel_wait(posix_signal& impl)
638   { 638   {
HITCBC 639   104 bool was_waiting = false; 639   104 bool was_waiting = false;
HITCBC 640   104 signal_op* op = nullptr; 640   104 signal_op* op = nullptr;
641   641  
642   { 642   {
HITCBC 643   104 std::lock_guard lock(mutex_); 643   104 std::lock_guard lock(mutex_);
HITCBC 644   104 impl.cancelled_ = true; 644   104 impl.cancelled_ = true;
HITCBC 645   104 if (impl.waiting_) 645   104 if (impl.waiting_)
646   { 646   {
HITCBC 647   4 was_waiting = true; 647   4 was_waiting = true;
HITCBC 648   4 impl.waiting_ = false; 648   4 impl.waiting_ = false;
HITCBC 649   4 op = &impl.pending_op_; 649   4 op = &impl.pending_op_;
650   } 650   }
HITCBC 651   104 } 651   104 }
652   652  
HITCBC 653   104 if (was_waiting) 653   104 if (was_waiting)
654   { 654   {
HITCBC 655   4 if (op->ec_out) 655   4 if (op->ec_out)
HITCBC 656   4 *op->ec_out = make_error_code(capy::error::canceled); 656   4 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 657   4 if (op->signal_out) 657   4 if (op->signal_out)
HITCBC 658   4 *op->signal_out = 0; 658   4 *op->signal_out = 0;
HITCBC 659   4 op->cont_op.cont.h = op->h; 659   4 op->cont_op.cont.h = op->h;
HITCBC 660   4 op->d.post(op->cont_op.cont); 660   4 op->d.post(op->cont_op.cont);
HITCBC 661   4 sched_->work_finished(); 661   4 sched_->work_finished();
662   } 662   }
HITCBC 663   104 } 663   104 }
664   664  
665   inline void 665   inline void
HITCBC 666   28 posix_signal_service::start_wait(posix_signal& impl, signal_op* op) 666   28 posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
667   { 667   {
668   { 668   {
HITCBC 669   28 std::lock_guard lock(mutex_); 669   28 std::lock_guard lock(mutex_);
670   670  
671   // Check if cancel() was called before this wait started 671   // Check if cancel() was called before this wait started
HITCBC 672   28 if (impl.cancelled_) 672   28 if (impl.cancelled_)
673   { 673   {
HITCBC 674   2 impl.cancelled_ = false; 674   2 impl.cancelled_ = false;
HITCBC 675   2 if (op->ec_out) 675   2 if (op->ec_out)
HITCBC 676   2 *op->ec_out = make_error_code(capy::error::canceled); 676   2 *op->ec_out = make_error_code(capy::error::canceled);
HITCBC 677   2 if (op->signal_out) 677   2 if (op->signal_out)
HITCBC 678   2 *op->signal_out = 0; 678   2 *op->signal_out = 0;
HITCBC 679   2 op->cont_op.cont.h = op->h; 679   2 op->cont_op.cont.h = op->h;
HITCBC 680   2 op->d.post(op->cont_op.cont); 680   2 op->d.post(op->cont_op.cont);
HITCBC 681   2 return; 681   2 return;
682   } 682   }
683   683  
684   // Check for queued signals first (signal arrived before wait started) 684   // Check for queued signals first (signal arrived before wait started)
HITCBC 685   26 signal_registration* reg = impl.signals_; 685   26 signal_registration* reg = impl.signals_;
HITCBC 686   44 while (reg) 686   44 while (reg)
687   { 687   {
HITCBC 688   28 if (reg->undelivered > 0) 688   28 if (reg->undelivered > 0)
689   { 689   {
HITCBC 690   10 --reg->undelivered; 690   10 --reg->undelivered;
HITCBC 691   10 op->signal_number = reg->signal_number; 691   10 op->signal_number = reg->signal_number;
692   // svc=nullptr: no work_finished needed since we never called work_started 692   // svc=nullptr: no work_finished needed since we never called work_started
HITCBC 693   10 op->svc = nullptr; 693   10 op->svc = nullptr;
HITCBC 694   10 sched_->post(op); 694   10 sched_->post(op);
HITCBC 695   10 return; 695   10 return;
696   } 696   }
HITCBC 697   18 reg = reg->next_in_set; 697   18 reg = reg->next_in_set;
698   } 698   }
699   699  
700   // No queued signals - wait for delivery 700   // No queued signals - wait for delivery
HITCBC 701   16 impl.waiting_ = true; 701   16 impl.waiting_ = true;
702   // svc=this: signal_op::operator() will call work_finished() to balance this 702   // svc=this: signal_op::operator() will call work_finished() to balance this
HITCBC 703   16 op->svc = this; 703   16 op->svc = this;
HITCBC 704   16 sched_->work_started(); 704   16 sched_->work_started();
HITCBC 705   28 } 705   28 }
706   } 706   }
707   707  
708   inline void 708   inline void
HITCBC 709   20 posix_signal_service::deliver_signal(int signal_number) 709   20 posix_signal_service::deliver_signal(int signal_number)
710   { 710   {
HITCBC 711   20 if (signal_number < 0 || signal_number >= max_signal_number) 711   20 if (signal_number < 0 || signal_number >= max_signal_number)
MISUBC 712   return; 712   return;
713   713  
714   posix_signal_detail::signal_state* state = 714   posix_signal_detail::signal_state* state =
HITCBC 715   20 posix_signal_detail::get_signal_state(); 715   20 posix_signal_detail::get_signal_state();
HITCBC 716   20 std::lock_guard lock(state->mutex); 716   20 std::lock_guard lock(state->mutex);
717   717  
HITCBC 718   20 posix_signal_service* service = state->service_list; 718   20 posix_signal_service* service = state->service_list;
HITCBC 719   40 while (service) 719   40 while (service)
720   { 720   {
HITCBC 721   20 std::lock_guard svc_lock(service->mutex_); 721   20 std::lock_guard svc_lock(service->mutex_);
722   722  
HITCBC 723   20 signal_registration* reg = service->registrations_[signal_number]; 723   20 signal_registration* reg = service->registrations_[signal_number];
HITCBC 724   42 while (reg) 724   42 while (reg)
725   { 725   {
HITCBC 726   22 posix_signal* impl = static_cast<posix_signal*>(reg->owner); 726   22 posix_signal* impl = static_cast<posix_signal*>(reg->owner);
727   727  
HITCBC 728   22 if (impl->waiting_) 728   22 if (impl->waiting_)
729   { 729   {
HITCBC 730   12 impl->waiting_ = false; 730   12 impl->waiting_ = false;
HITCBC 731   12 impl->pending_op_.signal_number = signal_number; 731   12 impl->pending_op_.signal_number = signal_number;
HITCBC 732   12 service->post(&impl->pending_op_); 732   12 service->post(&impl->pending_op_);
733   } 733   }
734   else 734   else
735   { 735   {
HITCBC 736   10 ++reg->undelivered; 736   10 ++reg->undelivered;
737   } 737   }
738   738  
HITCBC 739   22 reg = reg->next_in_table; 739   22 reg = reg->next_in_table;
740   } 740   }
741   741  
HITCBC 742   20 service = service->next_; 742   20 service = service->next_;
HITCBC 743   20 } 743   20 }
HITCBC 744   20 } 744   20 }
745   745  
746   inline void 746   inline void
747   posix_signal_service::work_started() noexcept 747   posix_signal_service::work_started() noexcept
748   { 748   {
749   sched_->work_started(); 749   sched_->work_started();
750   } 750   }
751   751  
752   inline void 752   inline void
HITCBC 753   12 posix_signal_service::work_finished() noexcept 753   12 posix_signal_service::work_finished() noexcept
754   { 754   {
HITCBC 755   12 sched_->work_finished(); 755   12 sched_->work_finished();
HITCBC 756   12 } 756   12 }
757   757  
758   inline void 758   inline void
HITCBC 759   12 posix_signal_service::post(signal_op* op) 759   12 posix_signal_service::post(signal_op* op)
760   { 760   {
HITCBC 761   12 sched_->post(op); 761   12 sched_->post(op);
HITCBC 762   12 } 762   12 }
763   763  
764   inline void 764   inline void
HITCBC 765   605 posix_signal_service::add_service(posix_signal_service* service) 765   605 posix_signal_service::add_service(posix_signal_service* service)
766   { 766   {
767   posix_signal_detail::signal_state* state = 767   posix_signal_detail::signal_state* state =
HITCBC 768   605 posix_signal_detail::get_signal_state(); 768   605 posix_signal_detail::get_signal_state();
HITCBC 769   605 std::lock_guard lock(state->mutex); 769   605 std::lock_guard lock(state->mutex);
770   770  
HITCBC 771   605 service->next_ = state->service_list; 771   605 service->next_ = state->service_list;
HITCBC 772   605 service->prev_ = nullptr; 772   605 service->prev_ = nullptr;
HITCBC 773   605 if (state->service_list) 773   605 if (state->service_list)
HITCBC 774   5 state->service_list->prev_ = service; 774   5 state->service_list->prev_ = service;
HITCBC 775   605 state->service_list = service; 775   605 state->service_list = service;
HITCBC 776   605 } 776   605 }
777   777  
778   inline void 778   inline void
HITCBC 779   605 posix_signal_service::remove_service(posix_signal_service* service) 779   605 posix_signal_service::remove_service(posix_signal_service* service)
780   { 780   {
781   posix_signal_detail::signal_state* state = 781   posix_signal_detail::signal_state* state =
HITCBC 782   605 posix_signal_detail::get_signal_state(); 782   605 posix_signal_detail::get_signal_state();
HITCBC 783   605 std::lock_guard lock(state->mutex); 783   605 std::lock_guard lock(state->mutex);
784   784  
HITCBC 785   605 if (service->next_ || service->prev_ || state->service_list == service) 785   605 if (service->next_ || service->prev_ || state->service_list == service)
786   { 786   {
HITCBC 787   605 if (state->service_list == service) 787   605 if (state->service_list == service)
HITCBC 788   605 state->service_list = service->next_; 788   605 state->service_list = service->next_;
HITCBC 789   605 if (service->prev_) 789   605 if (service->prev_)
MISUBC 790   service->prev_->next_ = service->next_; 790   service->prev_->next_ = service->next_;
HITCBC 791   605 if (service->next_) 791   605 if (service->next_)
HITCBC 792   5 service->next_->prev_ = service->prev_; 792   5 service->next_->prev_ = service->prev_;
HITCBC 793   605 service->next_ = nullptr; 793   605 service->next_ = nullptr;
HITCBC 794   605 service->prev_ = nullptr; 794   605 service->prev_ = nullptr;
795   } 795   }
HITCBC 796   605 } 796   605 }
797   797  
798   // get_signal_service - factory function 798   // get_signal_service - factory function
799   799  
800   inline posix_signal_service& 800   inline posix_signal_service&
HITCBC 801   605 get_signal_service(capy::execution_context& ctx, scheduler& sched) 801   605 get_signal_service(capy::execution_context& ctx, scheduler& sched)
802   { 802   {
HITCBC 803   605 return ctx.make_service<posix_signal_service>(sched); 803   605 return ctx.make_service<posix_signal_service>(sched);
804   } 804   }
805   805  
806   } // namespace detail 806   } // namespace detail
807   } // namespace boost::corosio 807   } // namespace boost::corosio
808   808  
809   #endif // BOOST_COROSIO_POSIX 809   #endif // BOOST_COROSIO_POSIX
810   810  
811   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP 811   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP