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