src/corosio/src/detail/posix/signals.cpp

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