LCOV - code coverage report
Current view: top level - src/detail/posix - signals.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 89.6 % 335 300
Test Date: 2026-02-04 19:19:32 Functions: 92.7 % 41 38

            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 = &reg->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 = &reg->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
        

Generated by: LCOV version 2.3