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 : void 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 : void
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 0 : return;
413 : }
414 :
415 26 : svc_.start_wait(*this, &pending_op_);
416 : }
417 :
418 : std::error_code
419 96 : posix_signal_impl::
420 : add(int signal_number, signal_set::flags_t flags)
421 : {
422 96 : return svc_.add_signal(*this, signal_number, flags);
423 : }
424 :
425 : std::error_code
426 4 : posix_signal_impl::
427 : remove(int signal_number)
428 : {
429 4 : return svc_.remove_signal(*this, signal_number);
430 : }
431 :
432 : std::error_code
433 92 : posix_signal_impl::
434 : clear()
435 : {
436 92 : return svc_.clear_signals(*this);
437 : }
438 :
439 : void
440 100 : posix_signal_impl::
441 : cancel()
442 : {
443 100 : svc_.cancel_wait(*this);
444 100 : }
445 :
446 : //------------------------------------------------------------------------------
447 : // posix_signals_impl implementation
448 : //------------------------------------------------------------------------------
449 :
450 309 : posix_signals_impl::
451 309 : posix_signals_impl(capy::execution_context&, scheduler& sched)
452 309 : : sched_(&sched)
453 : {
454 20085 : for (int i = 0; i < max_signal_number; ++i)
455 : {
456 19776 : registrations_[i] = nullptr;
457 19776 : registration_count_[i] = 0;
458 : }
459 309 : add_service(this);
460 309 : }
461 :
462 618 : posix_signals_impl::
463 309 : ~posix_signals_impl()
464 : {
465 309 : remove_service(this);
466 618 : }
467 :
468 : void
469 309 : posix_signals_impl::
470 : shutdown()
471 : {
472 309 : std::lock_guard lock(mutex_);
473 :
474 309 : for (auto* impl = impl_list_.pop_front(); impl != nullptr;
475 0 : impl = impl_list_.pop_front())
476 : {
477 0 : while (auto* reg = impl->signals_)
478 : {
479 0 : impl->signals_ = reg->next_in_set;
480 0 : delete reg;
481 0 : }
482 0 : delete impl;
483 : }
484 309 : }
485 :
486 : signal_set::signal_set_impl&
487 88 : posix_signals_impl::
488 : create_impl()
489 : {
490 88 : auto* impl = new posix_signal_impl(*this);
491 :
492 : {
493 88 : std::lock_guard lock(mutex_);
494 88 : impl_list_.push_back(impl);
495 88 : }
496 :
497 88 : return *impl;
498 : }
499 :
500 : void
501 88 : posix_signals_impl::
502 : destroy_impl(posix_signal_impl& impl)
503 : {
504 : {
505 88 : std::lock_guard lock(mutex_);
506 88 : impl_list_.remove(&impl);
507 88 : }
508 :
509 88 : delete &impl;
510 88 : }
511 :
512 : std::error_code
513 96 : posix_signals_impl::
514 : add_signal(
515 : posix_signal_impl& impl,
516 : int signal_number,
517 : signal_set::flags_t flags)
518 : {
519 96 : if (signal_number < 0 || signal_number >= max_signal_number)
520 2 : return make_error_code(std::errc::invalid_argument);
521 :
522 : // Validate that requested flags are supported on this platform
523 : // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
524 94 : if (!flags_supported(flags))
525 0 : return make_error_code(std::errc::operation_not_supported);
526 :
527 94 : signal_state* state = get_signal_state();
528 94 : std::lock_guard state_lock(state->mutex);
529 94 : std::lock_guard lock(mutex_);
530 :
531 : // Find insertion point (list is sorted by signal number)
532 94 : signal_registration** insertion_point = &impl.signals_;
533 94 : signal_registration* reg = impl.signals_;
534 104 : while (reg && reg->signal_number < signal_number)
535 : {
536 10 : insertion_point = ®->next_in_set;
537 10 : reg = reg->next_in_set;
538 : }
539 :
540 : // Already registered in this set - check flag compatibility
541 : // (same signal_set adding same signal twice with different flags)
542 94 : if (reg && reg->signal_number == signal_number)
543 : {
544 10 : if (!flags_compatible(reg->flags, flags))
545 2 : return make_error_code(std::errc::invalid_argument);
546 8 : return {};
547 : }
548 :
549 : // Check flag compatibility with global registration
550 : // (different signal_set already registered this signal with different flags)
551 84 : if (state->registration_count[signal_number] > 0)
552 : {
553 8 : if (!flags_compatible(state->registered_flags[signal_number], flags))
554 2 : return make_error_code(std::errc::invalid_argument);
555 : }
556 :
557 82 : auto* new_reg = new signal_registration;
558 82 : new_reg->signal_number = signal_number;
559 82 : new_reg->flags = flags;
560 82 : new_reg->owner = &impl;
561 82 : new_reg->undelivered = 0;
562 :
563 : // Install signal handler on first global registration
564 82 : if (state->registration_count[signal_number] == 0)
565 : {
566 76 : struct sigaction sa = {};
567 76 : sa.sa_handler = corosio_posix_signal_handler;
568 76 : sigemptyset(&sa.sa_mask);
569 76 : sa.sa_flags = to_sigaction_flags(flags);
570 :
571 76 : if (::sigaction(signal_number, &sa, nullptr) < 0)
572 : {
573 0 : delete new_reg;
574 0 : return make_error_code(std::errc::invalid_argument);
575 : }
576 :
577 : // Store the flags used for first registration
578 76 : state->registered_flags[signal_number] = flags;
579 : }
580 :
581 82 : new_reg->next_in_set = reg;
582 82 : *insertion_point = new_reg;
583 :
584 82 : new_reg->next_in_table = registrations_[signal_number];
585 82 : new_reg->prev_in_table = nullptr;
586 82 : if (registrations_[signal_number])
587 6 : registrations_[signal_number]->prev_in_table = new_reg;
588 82 : registrations_[signal_number] = new_reg;
589 :
590 82 : ++state->registration_count[signal_number];
591 82 : ++registration_count_[signal_number];
592 :
593 82 : return {};
594 94 : }
595 :
596 : std::error_code
597 4 : posix_signals_impl::
598 : remove_signal(
599 : posix_signal_impl& impl,
600 : int signal_number)
601 : {
602 4 : if (signal_number < 0 || signal_number >= max_signal_number)
603 0 : return make_error_code(std::errc::invalid_argument);
604 :
605 4 : signal_state* state = get_signal_state();
606 4 : std::lock_guard state_lock(state->mutex);
607 4 : std::lock_guard lock(mutex_);
608 :
609 4 : signal_registration** deletion_point = &impl.signals_;
610 4 : signal_registration* reg = impl.signals_;
611 4 : while (reg && reg->signal_number < signal_number)
612 : {
613 0 : deletion_point = ®->next_in_set;
614 0 : reg = reg->next_in_set;
615 : }
616 :
617 4 : if (!reg || reg->signal_number != signal_number)
618 2 : return {};
619 :
620 : // Restore default handler on last global unregistration
621 2 : if (state->registration_count[signal_number] == 1)
622 : {
623 2 : struct sigaction sa = {};
624 2 : sa.sa_handler = SIG_DFL;
625 2 : sigemptyset(&sa.sa_mask);
626 2 : sa.sa_flags = 0;
627 :
628 2 : if (::sigaction(signal_number, &sa, nullptr) < 0)
629 0 : return make_error_code(std::errc::invalid_argument);
630 :
631 : // Clear stored flags
632 2 : state->registered_flags[signal_number] = signal_set::none;
633 : }
634 :
635 2 : *deletion_point = reg->next_in_set;
636 :
637 2 : if (registrations_[signal_number] == reg)
638 2 : registrations_[signal_number] = reg->next_in_table;
639 2 : if (reg->prev_in_table)
640 0 : reg->prev_in_table->next_in_table = reg->next_in_table;
641 2 : if (reg->next_in_table)
642 0 : reg->next_in_table->prev_in_table = reg->prev_in_table;
643 :
644 2 : --state->registration_count[signal_number];
645 2 : --registration_count_[signal_number];
646 :
647 2 : delete reg;
648 2 : return {};
649 4 : }
650 :
651 : std::error_code
652 92 : posix_signals_impl::
653 : clear_signals(posix_signal_impl& impl)
654 : {
655 92 : signal_state* state = get_signal_state();
656 92 : std::lock_guard state_lock(state->mutex);
657 92 : std::lock_guard lock(mutex_);
658 :
659 92 : std::error_code first_error;
660 :
661 172 : while (signal_registration* reg = impl.signals_)
662 : {
663 80 : int signal_number = reg->signal_number;
664 :
665 80 : if (state->registration_count[signal_number] == 1)
666 : {
667 74 : struct sigaction sa = {};
668 74 : sa.sa_handler = SIG_DFL;
669 74 : sigemptyset(&sa.sa_mask);
670 74 : sa.sa_flags = 0;
671 :
672 74 : if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
673 0 : first_error = make_error_code(std::errc::invalid_argument);
674 :
675 : // Clear stored flags
676 74 : state->registered_flags[signal_number] = signal_set::none;
677 : }
678 :
679 80 : impl.signals_ = reg->next_in_set;
680 :
681 80 : if (registrations_[signal_number] == reg)
682 80 : registrations_[signal_number] = reg->next_in_table;
683 80 : if (reg->prev_in_table)
684 0 : reg->prev_in_table->next_in_table = reg->next_in_table;
685 80 : if (reg->next_in_table)
686 6 : reg->next_in_table->prev_in_table = reg->prev_in_table;
687 :
688 80 : --state->registration_count[signal_number];
689 80 : --registration_count_[signal_number];
690 :
691 80 : delete reg;
692 80 : }
693 :
694 92 : if (first_error)
695 0 : return first_error;
696 92 : return {};
697 92 : }
698 :
699 : void
700 100 : posix_signals_impl::
701 : cancel_wait(posix_signal_impl& impl)
702 : {
703 100 : bool was_waiting = false;
704 100 : signal_op* op = nullptr;
705 :
706 : {
707 100 : std::lock_guard lock(mutex_);
708 100 : if (impl.waiting_)
709 : {
710 4 : was_waiting = true;
711 4 : impl.waiting_ = false;
712 4 : op = &impl.pending_op_;
713 : }
714 100 : }
715 :
716 100 : if (was_waiting)
717 : {
718 4 : if (op->ec_out)
719 4 : *op->ec_out = make_error_code(capy::error::canceled);
720 4 : if (op->signal_out)
721 4 : *op->signal_out = 0;
722 4 : op->d.post(op->h);
723 4 : sched_->on_work_finished();
724 : }
725 100 : }
726 :
727 : void
728 26 : posix_signals_impl::
729 : start_wait(posix_signal_impl& impl, signal_op* op)
730 : {
731 : {
732 26 : std::lock_guard lock(mutex_);
733 :
734 : // Check for queued signals first (signal arrived before wait started)
735 26 : signal_registration* reg = impl.signals_;
736 44 : while (reg)
737 : {
738 28 : if (reg->undelivered > 0)
739 : {
740 10 : --reg->undelivered;
741 10 : op->signal_number = reg->signal_number;
742 : // svc=nullptr: no work_finished needed since we never called work_started
743 10 : op->svc = nullptr;
744 10 : sched_->post(op);
745 10 : return;
746 : }
747 18 : reg = reg->next_in_set;
748 : }
749 :
750 : // No queued signals - wait for delivery
751 16 : impl.waiting_ = true;
752 : // svc=this: signal_op::operator() will call work_finished() to balance this
753 16 : op->svc = this;
754 16 : sched_->on_work_started();
755 26 : }
756 : }
757 :
758 : void
759 20 : posix_signals_impl::
760 : deliver_signal(int signal_number)
761 : {
762 20 : if (signal_number < 0 || signal_number >= max_signal_number)
763 0 : return;
764 :
765 20 : signal_state* state = get_signal_state();
766 20 : std::lock_guard lock(state->mutex);
767 :
768 20 : posix_signals_impl* service = state->service_list;
769 40 : while (service)
770 : {
771 20 : std::lock_guard svc_lock(service->mutex_);
772 :
773 20 : signal_registration* reg = service->registrations_[signal_number];
774 42 : while (reg)
775 : {
776 22 : posix_signal_impl* impl = static_cast<posix_signal_impl*>(reg->owner);
777 :
778 22 : if (impl->waiting_)
779 : {
780 12 : impl->waiting_ = false;
781 12 : impl->pending_op_.signal_number = signal_number;
782 12 : service->post(&impl->pending_op_);
783 : }
784 : else
785 : {
786 10 : ++reg->undelivered;
787 : }
788 :
789 22 : reg = reg->next_in_table;
790 : }
791 :
792 20 : service = service->next_;
793 20 : }
794 20 : }
795 :
796 : void
797 0 : posix_signals_impl::
798 : work_started() noexcept
799 : {
800 0 : sched_->work_started();
801 0 : }
802 :
803 : void
804 12 : posix_signals_impl::
805 : work_finished() noexcept
806 : {
807 12 : sched_->work_finished();
808 12 : }
809 :
810 : void
811 12 : posix_signals_impl::
812 : post(signal_op* op)
813 : {
814 12 : sched_->post(op);
815 12 : }
816 :
817 : void
818 309 : posix_signals_impl::
819 : add_service(posix_signals_impl* service)
820 : {
821 309 : signal_state* state = get_signal_state();
822 309 : std::lock_guard lock(state->mutex);
823 :
824 309 : service->next_ = state->service_list;
825 309 : service->prev_ = nullptr;
826 309 : if (state->service_list)
827 5 : state->service_list->prev_ = service;
828 309 : state->service_list = service;
829 309 : }
830 :
831 : void
832 309 : posix_signals_impl::
833 : remove_service(posix_signals_impl* service)
834 : {
835 309 : signal_state* state = get_signal_state();
836 309 : std::lock_guard lock(state->mutex);
837 :
838 309 : if (service->next_ || service->prev_ || state->service_list == service)
839 : {
840 309 : if (state->service_list == service)
841 309 : state->service_list = service->next_;
842 309 : if (service->prev_)
843 0 : service->prev_->next_ = service->next_;
844 309 : if (service->next_)
845 5 : service->next_->prev_ = service->prev_;
846 309 : service->next_ = nullptr;
847 309 : service->prev_ = nullptr;
848 : }
849 309 : }
850 :
851 : //------------------------------------------------------------------------------
852 : // get_signal_service - factory function
853 : //------------------------------------------------------------------------------
854 :
855 : posix_signals&
856 309 : get_signal_service(capy::execution_context& ctx, scheduler& sched)
857 : {
858 309 : return ctx.make_service<posix_signals_impl>(sched);
859 : }
860 :
861 : } // namespace detail
862 :
863 : //------------------------------------------------------------------------------
864 : // signal_set implementation
865 : //------------------------------------------------------------------------------
866 :
867 90 : signal_set::
868 90 : ~signal_set()
869 : {
870 90 : if (impl_)
871 86 : impl_->release();
872 90 : }
873 :
874 88 : signal_set::
875 88 : signal_set(capy::execution_context& ctx)
876 88 : : io_object(ctx)
877 : {
878 88 : auto* svc = ctx.find_service<detail::posix_signals>();
879 88 : if (!svc)
880 0 : detail::throw_logic_error("signal_set: signal service not initialized");
881 88 : impl_ = &svc->create_impl();
882 88 : }
883 :
884 2 : signal_set::
885 2 : signal_set(signal_set&& other) noexcept
886 2 : : io_object(std::move(other))
887 : {
888 2 : impl_ = other.impl_;
889 2 : other.impl_ = nullptr;
890 2 : }
891 :
892 : signal_set&
893 4 : signal_set::
894 : operator=(signal_set&& other)
895 : {
896 4 : if (this != &other)
897 : {
898 4 : if (ctx_ != other.ctx_)
899 2 : detail::throw_logic_error("signal_set::operator=: context mismatch");
900 :
901 2 : if (impl_)
902 2 : impl_->release();
903 :
904 2 : impl_ = other.impl_;
905 2 : other.impl_ = nullptr;
906 : }
907 2 : return *this;
908 : }
909 :
910 : std::error_code
911 96 : signal_set::
912 : add(int signal_number, flags_t flags)
913 : {
914 96 : return get().add(signal_number, flags);
915 : }
916 :
917 : std::error_code
918 4 : signal_set::
919 : remove(int signal_number)
920 : {
921 4 : return get().remove(signal_number);
922 : }
923 :
924 : std::error_code
925 4 : signal_set::
926 : clear()
927 : {
928 4 : return get().clear();
929 : }
930 :
931 : void
932 12 : signal_set::
933 : cancel()
934 : {
935 12 : get().cancel();
936 12 : }
937 :
938 : } // namespace boost::corosio
939 :
940 : #endif // BOOST_COROSIO_POSIX
|