TLA 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 : #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 HIT 90 : void destroy(io_object::implementation* p) override
162 : {
163 90 : auto& impl = static_cast<posix_signal&>(*p);
164 90 : [[maybe_unused]] auto n = impl.clear();
165 90 : impl.cancel();
166 90 : destroy_impl(impl);
167 90 : }
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 96 : 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 96 : return true;
254 : }
255 :
256 : // Map abstract flags to sigaction() flags.
257 : // Caller must ensure flags_supported() returns true first.
258 : inline int
259 78 : to_sigaction_flags(signal_set::flags_t flags)
260 : {
261 78 : int sa_flags = 0;
262 78 : if (flags & signal_set::restart)
263 18 : sa_flags |= SA_RESTART;
264 78 : if (flags & signal_set::no_child_stop)
265 MIS 0 : sa_flags |= SA_NOCLDSTOP;
266 : #ifdef SA_NOCLDWAIT
267 HIT 78 : if (flags & signal_set::no_child_wait)
268 MIS 0 : sa_flags |= SA_NOCLDWAIT;
269 : #endif
270 HIT 78 : if (flags & signal_set::no_defer)
271 2 : sa_flags |= SA_NODEFER;
272 78 : if (flags & signal_set::reset_handler)
273 MIS 0 : sa_flags |= SA_RESETHAND;
274 HIT 78 : return sa_flags;
275 : }
276 :
277 : // Check if two flag values are compatible
278 : inline bool
279 18 : flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
280 : {
281 : // dont_care is always compatible
282 34 : if ((existing & signal_set::dont_care) ||
283 16 : (requested & signal_set::dont_care))
284 6 : return true;
285 :
286 : // Mask out dont_care bit for comparison
287 12 : constexpr auto mask = ~signal_set::dont_care;
288 12 : return (existing & mask) == (requested & mask);
289 : }
290 :
291 : // C signal handler - must be async-signal-safe
292 : inline void
293 20 : corosio_posix_signal_handler(int signal_number)
294 : {
295 20 : 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 20 : }
299 :
300 : } // namespace posix_signal_detail
301 :
302 : // signal_op implementation
303 :
304 : inline void
305 22 : signal_op::operator()()
306 : {
307 22 : if (ec_out)
308 22 : *ec_out = {};
309 22 : if (signal_out)
310 22 : *signal_out = signal_number;
311 :
312 : // Capture svc before resuming (coro may destroy us)
313 22 : auto* service = svc;
314 22 : svc = nullptr;
315 :
316 22 : cont_op.cont.h = h;
317 22 : d.post(cont_op.cont);
318 :
319 : // Balance the work_started() from start_wait
320 22 : if (service)
321 12 : service->work_finished();
322 22 : }
323 :
324 : inline void
325 MIS 0 : signal_op::destroy()
326 : {
327 : // No-op: signal_op is embedded in posix_signal
328 0 : }
329 :
330 : // posix_signal implementation
331 :
332 HIT 90 : inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
333 90 : : svc_(svc)
334 : {
335 90 : }
336 :
337 : inline std::coroutine_handle<>
338 28 : 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 28 : pending_op_.h = h;
346 28 : pending_op_.d = d;
347 28 : pending_op_.ec_out = ec;
348 28 : pending_op_.signal_out = signal_out;
349 28 : pending_op_.signal_number = 0;
350 :
351 28 : if (token.stop_requested())
352 : {
353 MIS 0 : if (ec)
354 0 : *ec = make_error_code(capy::error::canceled);
355 0 : if (signal_out)
356 0 : *signal_out = 0;
357 0 : pending_op_.cont_op.cont.h = h;
358 0 : d.post(pending_op_.cont_op.cont);
359 : // completion is always posted to scheduler queue, never inline.
360 0 : return std::noop_coroutine();
361 : }
362 :
363 HIT 28 : svc_.start_wait(*this, &pending_op_);
364 : // completion is always posted to scheduler queue, never inline.
365 28 : return std::noop_coroutine();
366 : }
367 :
368 : inline std::error_code
369 98 : posix_signal::add(int signal_number, signal_set::flags_t flags)
370 : {
371 98 : return svc_.add_signal(*this, signal_number, flags);
372 : }
373 :
374 : inline std::error_code
375 4 : posix_signal::remove(int signal_number)
376 : {
377 4 : return svc_.remove_signal(*this, signal_number);
378 : }
379 :
380 : inline std::error_code
381 94 : posix_signal::clear()
382 : {
383 94 : return svc_.clear_signals(*this);
384 : }
385 :
386 : inline void
387 104 : posix_signal::cancel()
388 : {
389 104 : svc_.cancel_wait(*this);
390 104 : }
391 :
392 : // posix_signal_service implementation
393 :
394 541 : inline posix_signal_service::posix_signal_service(
395 541 : capy::execution_context&, scheduler& sched)
396 541 : : sched_(&sched)
397 : {
398 35165 : for (int i = 0; i < max_signal_number; ++i)
399 : {
400 34624 : registrations_[i] = nullptr;
401 34624 : registration_count_[i] = 0;
402 : }
403 541 : add_service(this);
404 541 : }
405 :
406 1082 : inline posix_signal_service::~posix_signal_service()
407 : {
408 541 : remove_service(this);
409 1082 : }
410 :
411 : inline void
412 541 : posix_signal_service::shutdown()
413 : {
414 541 : std::lock_guard lock(mutex_);
415 :
416 541 : for (auto* impl = impl_list_.pop_front(); impl != nullptr;
417 MIS 0 : impl = impl_list_.pop_front())
418 : {
419 0 : while (auto* reg = impl->signals_)
420 : {
421 0 : impl->signals_ = reg->next_in_set;
422 0 : delete reg;
423 0 : }
424 0 : delete impl;
425 : }
426 HIT 541 : }
427 :
428 : inline io_object::implementation*
429 90 : posix_signal_service::construct()
430 : {
431 90 : auto* impl = new posix_signal(*this);
432 :
433 : {
434 90 : std::lock_guard lock(mutex_);
435 90 : impl_list_.push_back(impl);
436 90 : }
437 :
438 90 : return impl;
439 : }
440 :
441 : inline void
442 90 : posix_signal_service::destroy_impl(posix_signal& impl)
443 : {
444 : {
445 90 : std::lock_guard lock(mutex_);
446 90 : impl_list_.remove(&impl);
447 90 : }
448 :
449 90 : delete &impl;
450 90 : }
451 :
452 : inline std::error_code
453 98 : posix_signal_service::add_signal(
454 : posix_signal& impl, int signal_number, signal_set::flags_t flags)
455 : {
456 98 : if (signal_number < 0 || signal_number >= max_signal_number)
457 2 : 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 96 : if (!posix_signal_detail::flags_supported(flags))
462 MIS 0 : return make_error_code(std::errc::operation_not_supported);
463 :
464 : posix_signal_detail::signal_state* state =
465 HIT 96 : posix_signal_detail::get_signal_state();
466 96 : std::lock_guard state_lock(state->mutex);
467 96 : std::lock_guard lock(mutex_);
468 :
469 : // Find insertion point (list is sorted by signal number)
470 96 : signal_registration** insertion_point = &impl.signals_;
471 96 : signal_registration* reg = impl.signals_;
472 106 : while (reg && reg->signal_number < signal_number)
473 : {
474 10 : insertion_point = ®->next_in_set;
475 10 : 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 96 : if (reg && reg->signal_number == signal_number)
481 : {
482 10 : if (!posix_signal_detail::flags_compatible(reg->flags, flags))
483 2 : return make_error_code(std::errc::invalid_argument);
484 8 : return {};
485 : }
486 :
487 : // Check flag compatibility with global registration
488 : // (different signal_set already registered this signal with different flags)
489 86 : if (state->registration_count[signal_number] > 0)
490 : {
491 8 : if (!posix_signal_detail::flags_compatible(
492 : state->registered_flags[signal_number], flags))
493 2 : return make_error_code(std::errc::invalid_argument);
494 : }
495 :
496 84 : auto* new_reg = new signal_registration;
497 84 : new_reg->signal_number = signal_number;
498 84 : new_reg->flags = flags;
499 84 : new_reg->owner = &impl;
500 84 : new_reg->undelivered = 0;
501 :
502 : // Install signal handler on first global registration
503 84 : if (state->registration_count[signal_number] == 0)
504 : {
505 78 : struct sigaction sa = {};
506 78 : sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler;
507 78 : sigemptyset(&sa.sa_mask);
508 78 : sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
509 :
510 78 : if (::sigaction(signal_number, &sa, nullptr) < 0)
511 : {
512 MIS 0 : delete new_reg;
513 0 : return make_error_code(std::errc::invalid_argument);
514 : }
515 :
516 : // Store the flags used for first registration
517 HIT 78 : state->registered_flags[signal_number] = flags;
518 : }
519 :
520 84 : new_reg->next_in_set = reg;
521 84 : *insertion_point = new_reg;
522 :
523 84 : new_reg->next_in_table = registrations_[signal_number];
524 84 : new_reg->prev_in_table = nullptr;
525 84 : if (registrations_[signal_number])
526 6 : registrations_[signal_number]->prev_in_table = new_reg;
527 84 : registrations_[signal_number] = new_reg;
528 :
529 84 : ++state->registration_count[signal_number];
530 84 : ++registration_count_[signal_number];
531 :
532 84 : return {};
533 96 : }
534 :
535 : inline std::error_code
536 4 : posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
537 : {
538 4 : if (signal_number < 0 || signal_number >= max_signal_number)
539 MIS 0 : return make_error_code(std::errc::invalid_argument);
540 :
541 : posix_signal_detail::signal_state* state =
542 HIT 4 : posix_signal_detail::get_signal_state();
543 4 : std::lock_guard state_lock(state->mutex);
544 4 : std::lock_guard lock(mutex_);
545 :
546 4 : signal_registration** deletion_point = &impl.signals_;
547 4 : signal_registration* reg = impl.signals_;
548 4 : while (reg && reg->signal_number < signal_number)
549 : {
550 MIS 0 : deletion_point = ®->next_in_set;
551 0 : reg = reg->next_in_set;
552 : }
553 :
554 HIT 4 : if (!reg || reg->signal_number != signal_number)
555 2 : return {};
556 :
557 : // Restore default handler on last global unregistration
558 2 : if (state->registration_count[signal_number] == 1)
559 : {
560 2 : struct sigaction sa = {};
561 2 : sa.sa_handler = SIG_DFL;
562 2 : sigemptyset(&sa.sa_mask);
563 2 : sa.sa_flags = 0;
564 :
565 2 : if (::sigaction(signal_number, &sa, nullptr) < 0)
566 MIS 0 : return make_error_code(std::errc::invalid_argument);
567 :
568 : // Clear stored flags
569 HIT 2 : state->registered_flags[signal_number] = signal_set::none;
570 : }
571 :
572 2 : *deletion_point = reg->next_in_set;
573 :
574 2 : if (registrations_[signal_number] == reg)
575 2 : registrations_[signal_number] = reg->next_in_table;
576 2 : if (reg->prev_in_table)
577 MIS 0 : reg->prev_in_table->next_in_table = reg->next_in_table;
578 HIT 2 : if (reg->next_in_table)
579 MIS 0 : reg->next_in_table->prev_in_table = reg->prev_in_table;
580 :
581 HIT 2 : --state->registration_count[signal_number];
582 2 : --registration_count_[signal_number];
583 :
584 2 : delete reg;
585 2 : return {};
586 4 : }
587 :
588 : inline std::error_code
589 94 : posix_signal_service::clear_signals(posix_signal& impl)
590 : {
591 : posix_signal_detail::signal_state* state =
592 94 : posix_signal_detail::get_signal_state();
593 94 : std::lock_guard state_lock(state->mutex);
594 94 : std::lock_guard lock(mutex_);
595 :
596 94 : std::error_code first_error;
597 :
598 176 : while (signal_registration* reg = impl.signals_)
599 : {
600 82 : int signal_number = reg->signal_number;
601 :
602 82 : if (state->registration_count[signal_number] == 1)
603 : {
604 76 : struct sigaction sa = {};
605 76 : sa.sa_handler = SIG_DFL;
606 76 : sigemptyset(&sa.sa_mask);
607 76 : sa.sa_flags = 0;
608 :
609 76 : if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
610 MIS 0 : first_error = make_error_code(std::errc::invalid_argument);
611 :
612 : // Clear stored flags
613 HIT 76 : state->registered_flags[signal_number] = signal_set::none;
614 : }
615 :
616 82 : impl.signals_ = reg->next_in_set;
617 :
618 82 : if (registrations_[signal_number] == reg)
619 82 : registrations_[signal_number] = reg->next_in_table;
620 82 : if (reg->prev_in_table)
621 MIS 0 : reg->prev_in_table->next_in_table = reg->next_in_table;
622 HIT 82 : if (reg->next_in_table)
623 6 : reg->next_in_table->prev_in_table = reg->prev_in_table;
624 :
625 82 : --state->registration_count[signal_number];
626 82 : --registration_count_[signal_number];
627 :
628 82 : delete reg;
629 82 : }
630 :
631 94 : if (first_error)
632 MIS 0 : return first_error;
633 HIT 94 : return {};
634 94 : }
635 :
636 : inline void
637 104 : posix_signal_service::cancel_wait(posix_signal& impl)
638 : {
639 104 : bool was_waiting = false;
640 104 : signal_op* op = nullptr;
641 :
642 : {
643 104 : std::lock_guard lock(mutex_);
644 104 : impl.cancelled_ = true;
645 104 : if (impl.waiting_)
646 : {
647 4 : was_waiting = true;
648 4 : impl.waiting_ = false;
649 4 : op = &impl.pending_op_;
650 : }
651 104 : }
652 :
653 104 : if (was_waiting)
654 : {
655 4 : if (op->ec_out)
656 4 : *op->ec_out = make_error_code(capy::error::canceled);
657 4 : if (op->signal_out)
658 4 : *op->signal_out = 0;
659 4 : op->cont_op.cont.h = op->h;
660 4 : op->d.post(op->cont_op.cont);
661 4 : sched_->work_finished();
662 : }
663 104 : }
664 :
665 : inline void
666 28 : posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
667 : {
668 : {
669 28 : std::lock_guard lock(mutex_);
670 :
671 : // Check if cancel() was called before this wait started
672 28 : if (impl.cancelled_)
673 : {
674 2 : impl.cancelled_ = false;
675 2 : if (op->ec_out)
676 2 : *op->ec_out = make_error_code(capy::error::canceled);
677 2 : if (op->signal_out)
678 2 : *op->signal_out = 0;
679 2 : op->cont_op.cont.h = op->h;
680 2 : op->d.post(op->cont_op.cont);
681 2 : return;
682 : }
683 :
684 : // Check for queued signals first (signal arrived before wait started)
685 26 : signal_registration* reg = impl.signals_;
686 44 : while (reg)
687 : {
688 28 : if (reg->undelivered > 0)
689 : {
690 10 : --reg->undelivered;
691 10 : op->signal_number = reg->signal_number;
692 : // svc=nullptr: no work_finished needed since we never called work_started
693 10 : op->svc = nullptr;
694 10 : sched_->post(op);
695 10 : return;
696 : }
697 18 : reg = reg->next_in_set;
698 : }
699 :
700 : // No queued signals - wait for delivery
701 16 : impl.waiting_ = true;
702 : // svc=this: signal_op::operator() will call work_finished() to balance this
703 16 : op->svc = this;
704 16 : sched_->work_started();
705 28 : }
706 : }
707 :
708 : inline void
709 20 : posix_signal_service::deliver_signal(int signal_number)
710 : {
711 20 : if (signal_number < 0 || signal_number >= max_signal_number)
712 MIS 0 : return;
713 :
714 : posix_signal_detail::signal_state* state =
715 HIT 20 : posix_signal_detail::get_signal_state();
716 20 : std::lock_guard lock(state->mutex);
717 :
718 20 : posix_signal_service* service = state->service_list;
719 40 : while (service)
720 : {
721 20 : std::lock_guard svc_lock(service->mutex_);
722 :
723 20 : signal_registration* reg = service->registrations_[signal_number];
724 42 : while (reg)
725 : {
726 22 : posix_signal* impl = static_cast<posix_signal*>(reg->owner);
727 :
728 22 : if (impl->waiting_)
729 : {
730 12 : impl->waiting_ = false;
731 12 : impl->pending_op_.signal_number = signal_number;
732 12 : service->post(&impl->pending_op_);
733 : }
734 : else
735 : {
736 10 : ++reg->undelivered;
737 : }
738 :
739 22 : reg = reg->next_in_table;
740 : }
741 :
742 20 : service = service->next_;
743 20 : }
744 20 : }
745 :
746 : inline void
747 : posix_signal_service::work_started() noexcept
748 : {
749 : sched_->work_started();
750 : }
751 :
752 : inline void
753 12 : posix_signal_service::work_finished() noexcept
754 : {
755 12 : sched_->work_finished();
756 12 : }
757 :
758 : inline void
759 12 : posix_signal_service::post(signal_op* op)
760 : {
761 12 : sched_->post(op);
762 12 : }
763 :
764 : inline void
765 541 : posix_signal_service::add_service(posix_signal_service* service)
766 : {
767 : posix_signal_detail::signal_state* state =
768 541 : posix_signal_detail::get_signal_state();
769 541 : std::lock_guard lock(state->mutex);
770 :
771 541 : service->next_ = state->service_list;
772 541 : service->prev_ = nullptr;
773 541 : if (state->service_list)
774 5 : state->service_list->prev_ = service;
775 541 : state->service_list = service;
776 541 : }
777 :
778 : inline void
779 541 : posix_signal_service::remove_service(posix_signal_service* service)
780 : {
781 : posix_signal_detail::signal_state* state =
782 541 : posix_signal_detail::get_signal_state();
783 541 : std::lock_guard lock(state->mutex);
784 :
785 541 : if (service->next_ || service->prev_ || state->service_list == service)
786 : {
787 541 : if (state->service_list == service)
788 541 : state->service_list = service->next_;
789 541 : if (service->prev_)
790 MIS 0 : service->prev_->next_ = service->next_;
791 HIT 541 : if (service->next_)
792 5 : service->next_->prev_ = service->prev_;
793 541 : service->next_ = nullptr;
794 541 : service->prev_ = nullptr;
795 : }
796 541 : }
797 :
798 : // get_signal_service - factory function
799 :
800 : inline posix_signal_service&
801 541 : get_signal_service(capy::execution_context& ctx, scheduler& sched)
802 : {
803 541 : 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
|