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