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