LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_signal.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 10 10
Test Date: 2026-10-17 04:01:32 Functions: 100.0 % 2 2

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/corosio
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
      12                 : #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
      13                 : 
      14                 : #include <boost/corosio/detail/platform.hpp>
      15                 : 
      16                 : #if BOOST_COROSIO_POSIX
      17                 : 
      18                 : #include <boost/corosio/detail/config.hpp>
      19                 : #include <boost/corosio/signal_set.hpp>
      20                 : #include <boost/corosio/detail/intrusive.hpp>
      21                 : #include <boost/corosio/detail/object_ref.hpp>
      22                 : #include <boost/corosio/detail/scheduler_op.hpp>
      23                 : #include <boost/capy/continuation.hpp>
      24                 : #include <boost/capy/ex/executor_ref.hpp>
      25                 : 
      26                 : #include <coroutine>
      27                 : #include <cstddef>
      28                 : #include <optional>
      29                 : #include <stop_token>
      30                 : #include <system_error>
      31                 : 
      32                 : namespace boost::corosio {
      33                 : 
      34                 : namespace detail {
      35                 : 
      36                 : // Forward declarations
      37                 : class posix_signal_service;
      38                 : 
      39                 : // Maximum signal number supported (NSIG is typically 64 on Linux)
      40                 : enum
      41                 : {
      42                 :     max_signal_number = 64
      43                 : };
      44                 : 
      45                 : // signal_op - pending wait operation
      46                 : 
      47                 : struct signal_op : scheduler_op
      48                 : {
      49                 :     /// The awaitable's continuation; see coro_op::cont.
      50                 :     capy::continuation* cont = nullptr;
      51                 :     capy::executor_ref d;
      52                 :     /// Holds the owning set while this op sits in the scheduler queue.
      53                 :     detail::object_ref keep;
      54                 :     std::error_code* ec_out   = nullptr;
      55                 :     int* signal_out           = nullptr;
      56                 :     int signal_number         = 0;
      57                 :     posix_signal_service* svc = nullptr; // For work_finished callback
      58                 : 
      59                 :     void operator()() noexcept override;
      60                 :     void destroy() override;
      61                 : };
      62                 : 
      63                 : // signal_registration - per-signal registration tracking
      64                 : 
      65                 : struct signal_registration
      66                 : {
      67                 :     int signal_number                  = 0;
      68                 :     signal_set::flags_t flags          = signal_set::none;
      69                 :     signal_set::implementation* owner  = nullptr;
      70                 :     std::size_t undelivered            = 0;
      71                 :     signal_registration* next_in_table = nullptr;
      72                 :     signal_registration* prev_in_table = nullptr;
      73                 :     signal_registration* next_in_set   = nullptr;
      74                 : };
      75                 : 
      76                 : // posix_signal - per-signal_set implementation
      77                 : 
      78                 : class posix_signal final
      79                 :     : public signal_set::implementation
      80                 :     , public intrusive_list<posix_signal>::node
      81                 : {
      82                 :     friend class posix_signal_service;
      83                 : 
      84                 :     posix_signal_service& svc_;
      85                 :     signal_registration* signals_ = nullptr;
      86                 :     signal_op pending_op_;
      87                 :     bool waiting_   = false;
      88                 :     bool cancelled_ = false;
      89                 : 
      90                 :     /** Routes a stop request into the service's per-operation cancel.
      91                 : 
      92                 :         Deliberately NOT `cancel_wait`: that sets the sticky `cancelled_`
      93                 :         latch, which belongs to `cancel()` and scopes to the whole set. A
      94                 :         stop token scopes to one wait, so a late fire must be a no-op
      95                 :         rather than poisoning the next wait.
      96                 :     */
      97                 :     struct token_canceller
      98                 :     {
      99                 :         posix_signal* self;
     100                 :         void operator()() const noexcept;
     101                 :     };
     102                 : 
     103                 :     /** Armed for the duration of one wait; see wait().
     104                 : 
     105                 :         Never reset while `posix_signal_service::mutex_` is held:
     106                 :         `~stop_callback` blocks until a concurrently running callback
     107                 :         returns, and that callback takes the same mutex.
     108                 :     */
     109                 :     std::optional<std::stop_callback<token_canceller>> stop_cb_;
     110                 : 
     111                 :     /** Set when a stop request arrives for the current wait.
     112                 : 
     113                 :         Closes a lost-wakeup race. The callback is armed before
     114                 :         `start_wait` takes the lock, so a request landing in that window
     115                 :         finds `waiting_ == false` and would otherwise return having done
     116                 :         nothing, leaving `start_wait` to park the wait forever. Every
     117                 :         other op survives this because `coro_op::on_cancel()` defaults to
     118                 :         `request_cancel()`, which sets a persistent flag the completion
     119                 :         decode reads later; this is that flag for the signal path.
     120                 : 
     121                 :         Distinct from `cancelled_` on purpose: `cancelled_` is the sticky
     122                 :         per-set latch `cancel()` owns, this is per-operation.
     123                 :     */
     124                 :     bool token_cancelled_ = false;
     125                 : 
     126                 : public:
     127                 :     explicit posix_signal(posix_signal_service& svc) noexcept;
     128                 : 
     129                 :     /// Recycle into the owning service's pool. Defined out-of-line
     130                 :     /// after posix_signal_service for its complete type.
     131                 :     void retire() noexcept override;
     132                 : 
     133                 :     /** Reset for recycling.
     134                 : 
     135                 :         `destroy()` already calls `clear()` (empties the registration
     136                 :         list) and `disarm_stop()` before the refcount reaches zero, so
     137                 :         those are asserted rather than re-cleared. `destroy()` also
     138                 :         calls `cancel()`, which deliberately latches `cancelled_` to
     139                 :         abort any wait still unwinding — that latch is this object's
     140                 :         to clear for its next, unrelated use, not something reset()
     141                 :         can skip by asserting it already false.
     142                 : 
     143                 :         @pre refs_ == 0, registration list empty, no wait in flight.
     144                 :     */
     145 HIT          72 :     void reuse() noexcept
     146                 :     {
     147              72 :         BOOST_COROSIO_ASSERT(signals_ == nullptr);
     148              72 :         BOOST_COROSIO_ASSERT(!waiting_);
     149              72 :         BOOST_COROSIO_ASSERT(!stop_cb_);
     150              72 :         cancelled_       = false;
     151              72 :         token_cancelled_ = false;
     152              72 :     }
     153                 : 
     154                 :     std::coroutine_handle<> wait(
     155                 :         capy::continuation&,
     156                 :         capy::executor_ref,
     157                 :         std::stop_token,
     158                 :         std::error_code*,
     159                 :         int*) override;
     160                 : 
     161                 :     std::error_code add(int signal_number, signal_set::flags_t flags) override;
     162                 :     std::error_code remove(int signal_number) override;
     163                 :     std::error_code clear() override;
     164                 :     void cancel() noexcept override;
     165                 : 
     166                 :     /// Disarm the wait's stop callback. Called before teardown.
     167            3476 :     void disarm_stop() noexcept
     168                 :     {
     169            3476 :         stop_cb_.reset();
     170            3476 :     }
     171                 : };
     172                 : 
     173                 : } // namespace detail
     174                 : 
     175                 : } // namespace boost::corosio
     176                 : 
     177                 : #endif // BOOST_COROSIO_POSIX
     178                 : 
     179                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
        

Generated by: LCOV version 2.3