LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_signal_service.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 89.4 % 293 262 31
Test Date: 2026-02-17 21:42:07 Functions: 96.7 % 30 29 1

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

Generated by: LCOV version 2.3