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