100.00% Lines (3/3) 100.00% Functions (1/1)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP 11   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP 12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP
13   13  
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   15  
16   #if BOOST_COROSIO_POSIX 16   #if BOOST_COROSIO_POSIX
17   17  
18   #include <boost/corosio/detail/config.hpp> 18   #include <boost/corosio/detail/config.hpp>
19   #include <boost/corosio/signal_set.hpp> 19   #include <boost/corosio/signal_set.hpp>
20   #include <boost/corosio/detail/intrusive.hpp> 20   #include <boost/corosio/detail/intrusive.hpp>
21   #include <boost/corosio/detail/scheduler_op.hpp> 21   #include <boost/corosio/detail/scheduler_op.hpp>
22   #include <boost/capy/continuation.hpp> 22   #include <boost/capy/continuation.hpp>
23   #include <boost/capy/ex/executor_ref.hpp> 23   #include <boost/capy/ex/executor_ref.hpp>
24   24  
25   #include <coroutine> 25   #include <coroutine>
26   #include <cstddef> 26   #include <cstddef>
27   #include <optional> 27   #include <optional>
28   #include <stop_token> 28   #include <stop_token>
29   #include <system_error> 29   #include <system_error>
30   30  
31   namespace boost::corosio { 31   namespace boost::corosio {
32   32  
33   namespace detail { 33   namespace detail {
34   34  
35   // Forward declarations 35   // Forward declarations
36   class posix_signal_service; 36   class posix_signal_service;
37   37  
38   // Maximum signal number supported (NSIG is typically 64 on Linux) 38   // Maximum signal number supported (NSIG is typically 64 on Linux)
39   enum 39   enum
40   { 40   {
41   max_signal_number = 64 41   max_signal_number = 64
42   }; 42   };
43   43  
44   // signal_op - pending wait operation 44   // signal_op - pending wait operation
45   45  
46   struct signal_op : scheduler_op 46   struct signal_op : scheduler_op
47   { 47   {
48   std::coroutine_handle<> h; 48   std::coroutine_handle<> h;
49   capy::continuation cont; 49   capy::continuation cont;
50   capy::executor_ref d; 50   capy::executor_ref d;
51   std::error_code* ec_out = nullptr; 51   std::error_code* ec_out = nullptr;
52   int* signal_out = nullptr; 52   int* signal_out = nullptr;
53   int signal_number = 0; 53   int signal_number = 0;
54   posix_signal_service* svc = nullptr; // For work_finished callback 54   posix_signal_service* svc = nullptr; // For work_finished callback
55   55  
56   void operator()() override; 56   void operator()() override;
57   void destroy() override; 57   void destroy() override;
58   }; 58   };
59   59  
60   // signal_registration - per-signal registration tracking 60   // signal_registration - per-signal registration tracking
61   61  
62   struct signal_registration 62   struct signal_registration
63   { 63   {
64   int signal_number = 0; 64   int signal_number = 0;
65   signal_set::flags_t flags = signal_set::none; 65   signal_set::flags_t flags = signal_set::none;
66   signal_set::implementation* owner = nullptr; 66   signal_set::implementation* owner = nullptr;
67   std::size_t undelivered = 0; 67   std::size_t undelivered = 0;
68   signal_registration* next_in_table = nullptr; 68   signal_registration* next_in_table = nullptr;
69   signal_registration* prev_in_table = nullptr; 69   signal_registration* prev_in_table = nullptr;
70   signal_registration* next_in_set = nullptr; 70   signal_registration* next_in_set = nullptr;
71   }; 71   };
72   72  
73   // posix_signal - per-signal_set implementation 73   // posix_signal - per-signal_set implementation
74   74  
75   class posix_signal final 75   class posix_signal final
76   : public signal_set::implementation 76   : public signal_set::implementation
77   , public intrusive_list<posix_signal>::node 77   , public intrusive_list<posix_signal>::node
78   { 78   {
79   friend class posix_signal_service; 79   friend class posix_signal_service;
80   80  
81   posix_signal_service& svc_; 81   posix_signal_service& svc_;
82   signal_registration* signals_ = nullptr; 82   signal_registration* signals_ = nullptr;
83   signal_op pending_op_; 83   signal_op pending_op_;
84   bool waiting_ = false; 84   bool waiting_ = false;
85   bool cancelled_ = false; 85   bool cancelled_ = false;
86   86  
87   /** Routes a stop request into the service's per-operation cancel. 87   /** Routes a stop request into the service's per-operation cancel.
88   88  
89   Deliberately NOT `cancel_wait`: that sets the sticky `cancelled_` 89   Deliberately NOT `cancel_wait`: that sets the sticky `cancelled_`
90   latch, which belongs to `cancel()` and scopes to the whole set. A 90   latch, which belongs to `cancel()` and scopes to the whole set. A
91   stop token scopes to one wait, so a late fire must be a no-op 91   stop token scopes to one wait, so a late fire must be a no-op
92   rather than poisoning the next wait. 92   rather than poisoning the next wait.
93   */ 93   */
94   struct token_canceller 94   struct token_canceller
95   { 95   {
96   posix_signal* self; 96   posix_signal* self;
97   void operator()() const noexcept; 97   void operator()() const noexcept;
98   }; 98   };
99   99  
100   /** Armed for the duration of one wait; see wait(). 100   /** Armed for the duration of one wait; see wait().
101   101  
102   Never reset while `posix_signal_service::mutex_` is held: 102   Never reset while `posix_signal_service::mutex_` is held:
103   `~stop_callback` blocks until a concurrently running callback 103   `~stop_callback` blocks until a concurrently running callback
104   returns, and that callback takes the same mutex. 104   returns, and that callback takes the same mutex.
105   */ 105   */
106   std::optional<std::stop_callback<token_canceller>> stop_cb_; 106   std::optional<std::stop_callback<token_canceller>> stop_cb_;
107   107  
108   /** Set when a stop request arrives for the current wait. 108   /** Set when a stop request arrives for the current wait.
109   109  
110   Closes a lost-wakeup race. The callback is armed before 110   Closes a lost-wakeup race. The callback is armed before
111   `start_wait` takes the lock, so a request landing in that window 111   `start_wait` takes the lock, so a request landing in that window
112   finds `waiting_ == false` and would otherwise return having done 112   finds `waiting_ == false` and would otherwise return having done
113   nothing, leaving `start_wait` to park the wait forever. Every 113   nothing, leaving `start_wait` to park the wait forever. Every
114   other op survives this because `coro_op::on_cancel()` defaults to 114   other op survives this because `coro_op::on_cancel()` defaults to
115   `request_cancel()`, which sets a persistent flag the completion 115   `request_cancel()`, which sets a persistent flag the completion
116   decode reads later; this is that flag for the signal path. 116   decode reads later; this is that flag for the signal path.
117   117  
118   Distinct from `cancelled_` on purpose: `cancelled_` is the sticky 118   Distinct from `cancelled_` on purpose: `cancelled_` is the sticky
119   per-set latch `cancel()` owns, this is per-operation. 119   per-set latch `cancel()` owns, this is per-operation.
120   */ 120   */
121   bool token_cancelled_ = false; 121   bool token_cancelled_ = false;
122   122  
123   public: 123   public:
124   explicit posix_signal(posix_signal_service& svc) noexcept; 124   explicit posix_signal(posix_signal_service& svc) noexcept;
125   125  
126   std::coroutine_handle<> wait( 126   std::coroutine_handle<> wait(
127   std::coroutine_handle<>, 127   std::coroutine_handle<>,
128   capy::executor_ref, 128   capy::executor_ref,
129   std::stop_token, 129   std::stop_token,
130   std::error_code*, 130   std::error_code*,
131   int*) override; 131   int*) override;
132   132  
133   std::error_code add(int signal_number, signal_set::flags_t flags) override; 133   std::error_code add(int signal_number, signal_set::flags_t flags) override;
134   std::error_code remove(int signal_number) override; 134   std::error_code remove(int signal_number) override;
135   std::error_code clear() override; 135   std::error_code clear() override;
136   void cancel() noexcept override; 136   void cancel() noexcept override;
137   137  
138   /// Disarm the wait's stop callback. Called before teardown. 138   /// Disarm the wait's stop callback. Called before teardown.
HITCBC 139   184 void disarm_stop() noexcept 139   184 void disarm_stop() noexcept
140   { 140   {
HITCBC 141   184 stop_cb_.reset(); 141   184 stop_cb_.reset();
HITCBC 142   184 } 142   184 }
143   }; 143   };
144   144  
145   } // namespace detail 145   } // namespace detail
146   146  
147   } // namespace boost::corosio 147   } // namespace boost::corosio
148   148  
149   #endif // BOOST_COROSIO_POSIX 149   #endif // BOOST_COROSIO_POSIX
150   150  
151   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP 151   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_HPP