include/boost/corosio/native/detail/posix/posix_signal_service.hpp

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