98.04% Lines (401/409)
97.06% Functions (33/34)
| 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_SERVICE_HPP | 11 | #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP | |||||
| 12 | #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP | 12 | #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_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/native/detail/posix/posix_signal.hpp> | 18 | #include <boost/corosio/native/detail/posix/posix_signal.hpp> | |||||
| 19 | 19 | |||||||
| 20 | #include <boost/corosio/detail/config.hpp> | 20 | #include <boost/corosio/detail/config.hpp> | |||||
| 21 | #include <boost/corosio/detail/object_pool.hpp> | 21 | #include <boost/corosio/detail/object_pool.hpp> | |||||
| 22 | #include <boost/corosio/detail/object_ref.hpp> | 22 | #include <boost/corosio/detail/object_ref.hpp> | |||||
| 23 | #include <boost/capy/continuation.hpp> | 23 | #include <boost/capy/continuation.hpp> | |||||
| 24 | #include <boost/capy/ex/abandon.hpp> | 24 | #include <boost/capy/ex/abandon.hpp> | |||||
| 25 | #include <boost/capy/ex/execution_context.hpp> | 25 | #include <boost/capy/ex/execution_context.hpp> | |||||
| 26 | #include <boost/corosio/detail/scheduler.hpp> | 26 | #include <boost/corosio/detail/scheduler.hpp> | |||||
| 27 | #include <boost/corosio/native/detail/make_err.hpp> | 27 | #include <boost/corosio/native/detail/make_err.hpp> | |||||
| 28 | #include <boost/capy/error.hpp> | 28 | #include <boost/capy/error.hpp> | |||||
| 29 | 29 | |||||||
| 30 | #include <coroutine> | 30 | #include <coroutine> | |||||
| 31 | #include <mutex> | 31 | #include <mutex> | |||||
| 32 | #include <tuple> | 32 | #include <tuple> | |||||
| 33 | #include <utility> | 33 | #include <utility> | |||||
| 34 | #include <vector> | 34 | #include <vector> | |||||
| 35 | 35 | |||||||
| 36 | #include <errno.h> | 36 | #include <errno.h> | |||||
| 37 | #include <fcntl.h> | 37 | #include <fcntl.h> | |||||
| 38 | #include <signal.h> | 38 | #include <signal.h> | |||||
| 39 | #include <unistd.h> | 39 | #include <unistd.h> | |||||
| 40 | 40 | |||||||
| 41 | /* | 41 | /* | |||||
| 42 | POSIX Signal Service | 42 | POSIX Signal Service | |||||
| 43 | ==================== | 43 | ==================== | |||||
| 44 | 44 | |||||||
| 45 | Concrete signal service implementation for POSIX backends. Manages signal | 45 | Concrete signal service implementation for POSIX backends. Manages signal | |||||
| 46 | registrations via sigaction() and dispatches completions through the | 46 | registrations via sigaction() and dispatches completions through the | |||||
| 47 | scheduler. One instance per execution_context, created on first use | 47 | scheduler. One instance per execution_context, created on first use | |||||
| 48 | by the public signal_set. | 48 | by the public signal_set. | |||||
| 49 | 49 | |||||||
| 50 | See the block comment further down for the full architecture overview. | 50 | See the block comment further down for the full architecture overview. | |||||
| 51 | */ | 51 | */ | |||||
| 52 | 52 | |||||||
| 53 | /* | 53 | /* | |||||
| 54 | POSIX Signal Implementation | 54 | POSIX Signal Implementation | |||||
| 55 | =========================== | 55 | =========================== | |||||
| 56 | 56 | |||||||
| 57 | This file implements signal handling for POSIX systems using sigaction(). | 57 | This file implements signal handling for POSIX systems using sigaction(). | |||||
| 58 | The implementation supports signal flags (SA_RESTART, etc.) and integrates | 58 | The implementation supports signal flags (SA_RESTART, etc.) and integrates | |||||
| 59 | with any POSIX-compatible scheduler via the abstract scheduler interface. | 59 | with any POSIX-compatible scheduler via the abstract scheduler interface. | |||||
| 60 | 60 | |||||||
| 61 | Architecture Overview | 61 | Architecture Overview | |||||
| 62 | --------------------- | 62 | --------------------- | |||||
| 63 | 63 | |||||||
| 64 | Three layers manage signal registrations: | 64 | Three layers manage signal registrations: | |||||
| 65 | 65 | |||||||
| 66 | 1. signal_state (global singleton) | 66 | 1. signal_state (global singleton) | |||||
| 67 | - Tracks the global service list and per-signal registration counts | 67 | - Tracks the global service list and per-signal registration counts | |||||
| 68 | - Stores the flags used for first registration of each signal (for | 68 | - Stores the flags used for first registration of each signal (for | |||||
| 69 | conflict detection when multiple signal_sets register same signal) | 69 | conflict detection when multiple signal_sets register same signal) | |||||
| 70 | - Owns the mutex that protects signal handler installation/removal | 70 | - Owns the mutex that protects signal handler installation/removal | |||||
| 71 | 71 | |||||||
| 72 | 2. posix_signal_service (one per execution_context) | 72 | 2. posix_signal_service (one per execution_context) | |||||
| 73 | - Maintains registrations_[] table indexed by signal number | 73 | - Maintains registrations_[] table indexed by signal number | |||||
| 74 | - Each slot is a doubly-linked list of signal_registrations for that signal | 74 | - Each slot is a doubly-linked list of signal_registrations for that signal | |||||
| 75 | - Also owns a recycling pool of all posix_signal objects it owns | 75 | - Also owns a recycling pool of all posix_signal objects it owns | |||||
| 76 | 76 | |||||||
| 77 | 3. posix_signal (one per signal_set) | 77 | 3. posix_signal (one per signal_set) | |||||
| 78 | - Owns a singly-linked list (sorted by signal number) of signal_registrations | 78 | - Owns a singly-linked list (sorted by signal number) of signal_registrations | |||||
| 79 | - Contains the pending_op_ used for wait operations | 79 | - Contains the pending_op_ used for wait operations | |||||
| 80 | 80 | |||||||
| 81 | Signal Delivery Flow | 81 | Signal Delivery Flow | |||||
| 82 | -------------------- | 82 | -------------------- | |||||
| 83 | 83 | |||||||
| 84 | Delivery uses the self-pipe trick so the signal handler itself performs | 84 | Delivery uses the self-pipe trick so the signal handler itself performs | |||||
| 85 | only async-signal-safe work (mirrors Boost.Asio): | 85 | only async-signal-safe work (mirrors Boost.Asio): | |||||
| 86 | 86 | |||||||
| 87 | 1. Signal arrives -> corosio_posix_signal_handler(). The handler only | 87 | 1. Signal arrives -> corosio_posix_signal_handler(). The handler only | |||||
| 88 | write()s the signal number to the global self-pipe (write_fd) and | 88 | write()s the signal number to the global self-pipe (write_fd) and | |||||
| 89 | restores errno. No locks, no allocation, no scheduler dispatch. | 89 | restores errno. No locks, no allocation, no scheduler dispatch. | |||||
| 90 | 90 | |||||||
| 91 | 2. The read end of the pipe is watched by one backend's event loop | 91 | 2. The read end of the pipe is watched by one backend's event loop | |||||
| 92 | (registered via scheduler::register_signal_reader on the first | 92 | (registered via scheduler::register_signal_reader on the first | |||||
| 93 | registration). When it becomes readable the backend drains it | 93 | registration). When it becomes readable the backend drains it | |||||
| 94 | (drain_signal_pipe) and calls deliver_signal() in normal context. | 94 | (drain_signal_pipe) and calls deliver_signal() in normal context. | |||||
| 95 | 95 | |||||||
| 96 | 3. deliver_signal() iterates all posix_signal_service services: | 96 | 3. deliver_signal() iterates all posix_signal_service services: | |||||
| 97 | - If a signal_set is waiting (impl->waiting_ == true), post the signal_op | 97 | - If a signal_set is waiting (impl->waiting_ == true), post the signal_op | |||||
| 98 | to the scheduler for immediate completion | 98 | to the scheduler for immediate completion | |||||
| 99 | - Otherwise, increment reg->undelivered to queue the signal | 99 | - Otherwise, increment reg->undelivered to queue the signal | |||||
| 100 | 100 | |||||||
| 101 | 4. When wait() is called via start_wait(): | 101 | 4. When wait() is called via start_wait(): | |||||
| 102 | - First check for queued signals (undelivered > 0); if found, post | 102 | - First check for queued signals (undelivered > 0); if found, post | |||||
| 103 | immediate completion without blocking | 103 | immediate completion without blocking | |||||
| 104 | - Otherwise, set waiting_ = true and call work_started() to keep | 104 | - Otherwise, set waiting_ = true and call work_started() to keep | |||||
| 105 | the io_context alive | 105 | the io_context alive | |||||
| 106 | 106 | |||||||
| 107 | Locking Protocol | 107 | Locking Protocol | |||||
| 108 | ---------------- | 108 | ---------------- | |||||
| 109 | 109 | |||||||
| 110 | Two mutex levels exist (MUST acquire in this order to avoid deadlock): | 110 | Two mutex levels exist (MUST acquire in this order to avoid deadlock): | |||||
| 111 | 1. signal_state::mutex - protects handler registration and service list | 111 | 1. signal_state::mutex - protects handler registration and service list | |||||
| 112 | 2. posix_signal_service::mutex_ - protects per-service registration tables | 112 | 2. posix_signal_service::mutex_ - protects per-service registration tables | |||||
| 113 | 113 | |||||||
| 114 | Async-Signal-Safety | 114 | Async-Signal-Safety | |||||
| 115 | ------------------- | 115 | ------------------- | |||||
| 116 | 116 | |||||||
| 117 | The C signal handler (corosio_posix_signal_handler) performs only | 117 | The C signal handler (corosio_posix_signal_handler) performs only | |||||
| 118 | async-signal-safe operations: it reads the single global write_fd and | 118 | async-signal-safe operations: it reads the single global write_fd and | |||||
| 119 | calls write(), saving/restoring errno. It never locks a mutex, allocates | 119 | calls write(), saving/restoring errno. It never locks a mutex, allocates | |||||
| 120 | memory, or dispatches through the scheduler. All of that happens in | 120 | memory, or dispatches through the scheduler. All of that happens in | |||||
| 121 | deliver_signal(), which runs in normal thread context from the backend | 121 | deliver_signal(), which runs in normal thread context from the backend | |||||
| 122 | event loop after draining the self-pipe. There is therefore no | 122 | event loop after draining the self-pipe. There is therefore no | |||||
| 123 | self-deadlock risk if a signal arrives while a thread holds state->mutex | 123 | self-deadlock risk if a signal arrives while a thread holds state->mutex | |||||
| 124 | or service->mutex_. | 124 | or service->mutex_. | |||||
| 125 | 125 | |||||||
| 126 | Flag Handling | 126 | Flag Handling | |||||
| 127 | ------------- | 127 | ------------- | |||||
| 128 | 128 | |||||||
| 129 | - Flags are abstract values in the public API (signal_set::flags_t) | 129 | - Flags are abstract values in the public API (signal_set::flags_t) | |||||
| 130 | - flags_supported() validates that requested flags are available on | 130 | - flags_supported() validates that requested flags are available on | |||||
| 131 | this platform; returns false if SA_NOCLDWAIT is unavailable and | 131 | this platform; returns false if SA_NOCLDWAIT is unavailable and | |||||
| 132 | no_child_wait is requested | 132 | no_child_wait is requested | |||||
| 133 | - to_sigaction_flags() maps validated flags to actual SA_* constants | 133 | - to_sigaction_flags() maps validated flags to actual SA_* constants | |||||
| 134 | - First registration of a signal establishes the flags; subsequent | 134 | - First registration of a signal establishes the flags; subsequent | |||||
| 135 | registrations must be compatible (same flags or dont_care) | 135 | registrations must be compatible (same flags or dont_care) | |||||
| 136 | - Requesting unavailable flags returns operation_not_supported | 136 | - Requesting unavailable flags returns operation_not_supported | |||||
| 137 | 137 | |||||||
| 138 | Work Tracking | 138 | Work Tracking | |||||
| 139 | ------------- | 139 | ------------- | |||||
| 140 | 140 | |||||||
| 141 | When waiting for a signal: | 141 | When waiting for a signal: | |||||
| 142 | - start_wait() calls sched_->work_started() to prevent io_context::run() | 142 | - start_wait() calls sched_->work_started() to prevent io_context::run() | |||||
| 143 | from returning while we wait | 143 | from returning while we wait | |||||
| 144 | - signal_op::svc is set to point to the service | 144 | - signal_op::svc is set to point to the service | |||||
| 145 | - signal_op::operator()() calls work_finished() after resuming the coroutine | 145 | - signal_op::operator()() calls work_finished() after resuming the coroutine | |||||
| 146 | 146 | |||||||
| 147 | If a signal was already queued (undelivered > 0), no work tracking is needed | 147 | If a signal was already queued (undelivered > 0), no work tracking is needed | |||||
| 148 | because completion is posted immediately. | 148 | because completion is posted immediately. | |||||
| 149 | */ | 149 | */ | |||||
| 150 | 150 | |||||||
| 151 | namespace boost::corosio { | 151 | namespace boost::corosio { | |||||
| 152 | 152 | |||||||
| 153 | namespace detail { | 153 | namespace detail { | |||||
| 154 | 154 | |||||||
| 155 | /** Signal service for POSIX backends. | 155 | /** Signal service for POSIX backends. | |||||
| 156 | 156 | |||||||
| 157 | Manages signal registrations via sigaction() and dispatches signal | 157 | Manages signal registrations via sigaction() and dispatches signal | |||||
| 158 | completions through the scheduler. One instance per execution_context. | 158 | completions through the scheduler. One instance per execution_context. | |||||
| 159 | */ | 159 | */ | |||||
| 160 | class BOOST_COROSIO_DECL posix_signal_service final | 160 | class BOOST_COROSIO_DECL posix_signal_service final | |||||
| 161 | : public capy::execution_context::service | 161 | : public capy::execution_context::service | |||||
| 162 | , public io_object::io_service | 162 | , public io_object::io_service | |||||
| 163 | { | 163 | { | |||||
| 164 | friend class posix_signal; | 164 | friend class posix_signal; | |||||
| 165 | 165 | |||||||
| 166 | public: | 166 | public: | |||||
| 167 | using key_type = posix_signal_service; | 167 | using key_type = posix_signal_service; | |||||
| 168 | 168 | |||||||
| 169 | explicit posix_signal_service(capy::execution_context& ctx); | 169 | explicit posix_signal_service(capy::execution_context& ctx); | |||||
| 170 | ~posix_signal_service() override; | 170 | ~posix_signal_service() override; | |||||
| 171 | 171 | |||||||
| 172 | posix_signal_service(posix_signal_service const&) = delete; | 172 | posix_signal_service(posix_signal_service const&) = delete; | |||||
| 173 | posix_signal_service& operator=(posix_signal_service const&) = delete; | 173 | posix_signal_service& operator=(posix_signal_service const&) = delete; | |||||
| 174 | 174 | |||||||
| 175 | io_object::implementation* construct() override; | 175 | io_object::implementation* construct() override; | |||||
| 176 | 176 | |||||||
| HITCBC | 177 | 3476 | void destroy(io_object::implementation* p) override | 177 | 3476 | void destroy(io_object::implementation* p) override | ||
| 178 | { | 178 | { | |||||
| HITCBC | 179 | 3476 | auto& impl = static_cast<posix_signal&>(*p); | 179 | 3476 | auto& impl = static_cast<posix_signal&>(*p); | ||
| HITCBC | 180 | 3476 | [[maybe_unused]] auto n = impl.clear(); | 180 | 3476 | [[maybe_unused]] auto n = impl.clear(); | ||
| HITCBC | 181 | 3476 | impl.disarm_stop(); | 181 | 3476 | impl.disarm_stop(); | ||
| HITCBC | 182 | 3476 | impl.cancel(); | 182 | 3476 | impl.cancel(); | ||
| HITCBC | 183 | 3476 | release(&impl); | 183 | 3476 | release(&impl); | ||
| HITCBC | 184 | 3476 | } | 184 | 3476 | } | ||
| 185 | 185 | |||||||
| 186 | /** Shut down the service. | 186 | /** Shut down the service. | |||||
| 187 | 187 | |||||||
| 188 | Gives every registration its live implementations still hold | 188 | Gives every registration its live implementations still hold | |||||
| 189 | back to the process-global table. The implementations are | 189 | back to the process-global table. The implementations are | |||||
| 190 | freed when their handles release them, or when the service is | 190 | freed when their handles release them, or when the service is | |||||
| 191 | destroyed if a handle outlives it. | 191 | destroyed if a handle outlives it. | |||||
| 192 | */ | 192 | */ | |||||
| 193 | void shutdown() override; | 193 | void shutdown() override; | |||||
| 194 | 194 | |||||||
| 195 | std::error_code add_signal( | 195 | std::error_code add_signal( | |||||
| 196 | posix_signal& impl, int signal_number, signal_set::flags_t flags); | 196 | posix_signal& impl, int signal_number, signal_set::flags_t flags); | |||||
| 197 | 197 | |||||||
| 198 | std::error_code remove_signal(posix_signal& impl, int signal_number); | 198 | std::error_code remove_signal(posix_signal& impl, int signal_number); | |||||
| 199 | 199 | |||||||
| 200 | std::error_code clear_signals(posix_signal& impl); | 200 | std::error_code clear_signals(posix_signal& impl); | |||||
| 201 | 201 | |||||||
| 202 | void cancel_wait(posix_signal& impl) noexcept; | 202 | void cancel_wait(posix_signal& impl) noexcept; | |||||
| 203 | void start_wait(posix_signal& impl, signal_op* op); | 203 | void start_wait(posix_signal& impl, signal_op* op); | |||||
| 204 | 204 | |||||||
| 205 | /** Cancel an in-flight wait on behalf of a stop token. | 205 | /** Cancel an in-flight wait on behalf of a stop token. | |||||
| 206 | 206 | |||||||
| 207 | Identical to @ref cancel_wait except that it does not set the | 207 | Identical to @ref cancel_wait except that it does not set the | |||||
| 208 | sticky `cancelled_` latch: a stop token scopes to one operation, | 208 | sticky `cancelled_` latch: a stop token scopes to one operation, | |||||
| 209 | so a request arriving after the wait completed must do nothing. | 209 | so a request arriving after the wait completed must do nothing. | |||||
| 210 | */ | 210 | */ | |||||
| 211 | void cancel_wait_token(posix_signal& impl) noexcept; | 211 | void cancel_wait_token(posix_signal& impl) noexcept; | |||||
| 212 | 212 | |||||||
| 213 | /** Clear the per-operation stop flag before a new wait arms. | 213 | /** Clear the per-operation stop flag before a new wait arms. | |||||
| 214 | 214 | |||||||
| 215 | Lives here rather than on the implementation because `mutex_` is | 215 | Lives here rather than on the implementation because `mutex_` is | |||||
| 216 | the service's; the service is a friend of `posix_signal`, not the | 216 | the service's; the service is a friend of `posix_signal`, not the | |||||
| 217 | reverse. | 217 | reverse. | |||||
| 218 | */ | 218 | */ | |||||
| HITCBC | 219 | 205742 | void reset_token_cancel(posix_signal& impl) noexcept | 219 | 205897 | void reset_token_cancel(posix_signal& impl) noexcept | ||
| 220 | { | 220 | { | |||||
| HITCBC | 221 | 205742 | std::lock_guard lock(mutex_); | 221 | 205897 | std::lock_guard lock(mutex_); | ||
| HITCBC | 222 | 205742 | impl.token_cancelled_ = false; | 222 | 205897 | impl.token_cancelled_ = false; | ||
| HITCBC | 223 | 205742 | } | 223 | 205897 | } | ||
| 224 | 224 | |||||||
| 225 | static void deliver_signal(int signal_number); | 225 | static void deliver_signal(int signal_number); | |||||
| 226 | 226 | |||||||
| 227 | void work_started() noexcept; | 227 | void work_started() noexcept; | |||||
| 228 | void work_finished() noexcept; | 228 | void work_finished() noexcept; | |||||
| 229 | void post(signal_op* op); | 229 | void post(signal_op* op); | |||||
| 230 | 230 | |||||||
| 231 | private: | 231 | private: | |||||
| 232 | static void add_service(posix_signal_service* service); | 232 | static void add_service(posix_signal_service* service); | |||||
| 233 | static void remove_service(posix_signal_service* service); | 233 | static void remove_service(posix_signal_service* service); | |||||
| 234 | 234 | |||||||
| 235 | scheduler* sched_; | 235 | scheduler* sched_; | |||||
| 236 | std::mutex mutex_; | 236 | std::mutex mutex_; | |||||
| 237 | 237 | |||||||
| 238 | // Registers the signal self-pipe's read end with sched_ exactly once per | 238 | // Registers the signal self-pipe's read end with sched_ exactly once per | |||||
| 239 | // service, so every io_context that waits on a signal can drain the pipe. | 239 | // service, so every io_context that waits on a signal can drain the pipe. | |||||
| 240 | // A once_flag (not a bool under mutex_) because registration must run | 240 | // A once_flag (not a bool under mutex_) because registration must run | |||||
| 241 | // without holding mutex_ or the signal-state mutex — see add_signal. | 241 | // without holding mutex_ or the signal-state mutex — see add_signal. | |||||
| 242 | std::mutex reader_mutex_; | 242 | std::mutex reader_mutex_; | |||||
| 243 | bool reader_registered_ = false; | 243 | bool reader_registered_ = false; | |||||
| 244 | 244 | |||||||
| 245 | object_pool<posix_signal> pool_; | 245 | object_pool<posix_signal> pool_; | |||||
| 246 | 246 | |||||||
| 247 | // Per-signal registration table | 247 | // Per-signal registration table | |||||
| 248 | signal_registration* registrations_[max_signal_number]; | 248 | signal_registration* registrations_[max_signal_number]; | |||||
| 249 | 249 | |||||||
| 250 | // Registration counts for each signal | 250 | // Registration counts for each signal | |||||
| 251 | std::size_t registration_count_[max_signal_number]; | 251 | std::size_t registration_count_[max_signal_number]; | |||||
| 252 | 252 | |||||||
| 253 | // Linked list of all posix_signal_service services for signal delivery | 253 | // Linked list of all posix_signal_service services for signal delivery | |||||
| 254 | posix_signal_service* next_ = nullptr; | 254 | posix_signal_service* next_ = nullptr; | |||||
| 255 | posix_signal_service* prev_ = nullptr; | 255 | posix_signal_service* prev_ = nullptr; | |||||
| 256 | }; | 256 | }; | |||||
| 257 | 257 | |||||||
| 258 | } // namespace detail | 258 | } // namespace detail | |||||
| 259 | 259 | |||||||
| 260 | } // namespace boost::corosio | 260 | } // namespace boost::corosio | |||||
| 261 | 261 | |||||||
| 262 | // --------------------------------------------------------------------------- | 262 | // --------------------------------------------------------------------------- | |||||
| 263 | // Inline implementation | 263 | // Inline implementation | |||||
| 264 | // --------------------------------------------------------------------------- | 264 | // --------------------------------------------------------------------------- | |||||
| 265 | 265 | |||||||
| 266 | namespace boost::corosio { | 266 | namespace boost::corosio { | |||||
| 267 | 267 | |||||||
| 268 | namespace detail { | 268 | namespace detail { | |||||
| 269 | 269 | |||||||
| 270 | namespace posix_signal_detail { | 270 | namespace posix_signal_detail { | |||||
| 271 | 271 | |||||||
| 272 | struct signal_state | 272 | struct signal_state | |||||
| 273 | { | 273 | { | |||||
| 274 | std::mutex mutex; | 274 | std::mutex mutex; | |||||
| 275 | posix_signal_service* service_list = nullptr; | 275 | posix_signal_service* service_list = nullptr; | |||||
| 276 | std::size_t registration_count[max_signal_number] = {}; | 276 | std::size_t registration_count[max_signal_number] = {}; | |||||
| 277 | signal_set::flags_t registered_flags[max_signal_number] = {}; | 277 | signal_set::flags_t registered_flags[max_signal_number] = {}; | |||||
| 278 | 278 | |||||||
| 279 | // Self-pipe used to defer signal delivery out of handler context. | 279 | // Self-pipe used to defer signal delivery out of handler context. | |||||
| 280 | // The C handler writes the signal number to write_fd (async-signal- | 280 | // The C handler writes the signal number to write_fd (async-signal- | |||||
| 281 | // safe); a backend event loop drains read_fd and calls deliver_signal() | 281 | // safe); a backend event loop drains read_fd and calls deliver_signal() | |||||
| 282 | // in normal context. Created once (on the first signal registration) and | 282 | // in normal context. Created once (on the first signal registration) and | |||||
| 283 | // kept for the process lifetime. Each posix_signal_service registers the | 283 | // kept for the process lifetime. Each posix_signal_service registers the | |||||
| 284 | // read end with its own scheduler (see reader_once_) so every running | 284 | // read end with its own scheduler (see reader_once_) so every running | |||||
| 285 | // io_context can drain it; multiple readers on one pipe are safe because | 285 | // io_context can drain it; multiple readers on one pipe are safe because | |||||
| 286 | // each signal is a fixed sizeof(int) record read atomically. | 286 | // each signal is a fixed sizeof(int) record read atomically. | |||||
| 287 | int read_fd = -1; | 287 | int read_fd = -1; | |||||
| 288 | int write_fd = -1; | 288 | int write_fd = -1; | |||||
| 289 | }; | 289 | }; | |||||
| 290 | 290 | |||||||
| 291 | BOOST_COROSIO_DECL signal_state* get_signal_state(); | 291 | BOOST_COROSIO_DECL signal_state* get_signal_state(); | |||||
| 292 | 292 | |||||||
| 293 | // Check if requested flags are supported on this platform. | 293 | // Check if requested flags are supported on this platform. | |||||
| 294 | // Returns true if all flags are supported, false otherwise. | 294 | // Returns true if all flags are supported, false otherwise. | |||||
| 295 | inline bool | 295 | inline bool | |||||
| HITCBC | 296 | 3491 | flags_supported([[maybe_unused]] signal_set::flags_t flags) | 296 | 3491 | flags_supported([[maybe_unused]] signal_set::flags_t flags) | ||
| 297 | { | 297 | { | |||||
| 298 | #ifndef SA_NOCLDWAIT | 298 | #ifndef SA_NOCLDWAIT | |||||
| 299 | if (flags & signal_set::no_child_wait) | 299 | if (flags & signal_set::no_child_wait) | |||||
| 300 | return false; | 300 | return false; | |||||
| 301 | #endif | 301 | #endif | |||||
| HITCBC | 302 | 3491 | return true; | 302 | 3491 | return true; | ||
| 303 | } | 303 | } | |||||
| 304 | 304 | |||||||
| 305 | // Map abstract flags to sigaction() flags. | 305 | // Map abstract flags to sigaction() flags. | |||||
| 306 | // Caller must ensure flags_supported() returns true first. | 306 | // Caller must ensure flags_supported() returns true first. | |||||
| 307 | inline int | 307 | inline int | |||||
| HITCBC | 308 | 641 | to_sigaction_flags(signal_set::flags_t flags) | 308 | 641 | to_sigaction_flags(signal_set::flags_t flags) | ||
| 309 | { | 309 | { | |||||
| HITCBC | 310 | 641 | int sa_flags = 0; | 310 | 641 | int sa_flags = 0; | ||
| HITCBC | 311 | 641 | if (flags & signal_set::restart) | 311 | 641 | if (flags & signal_set::restart) | ||
| HITCBC | 312 | 23 | sa_flags |= SA_RESTART; | 312 | 23 | sa_flags |= SA_RESTART; | ||
| HITCBC | 313 | 641 | if (flags & signal_set::no_child_stop) | 313 | 641 | if (flags & signal_set::no_child_stop) | ||
| HITCBC | 314 | 3 | sa_flags |= SA_NOCLDSTOP; | 314 | 3 | sa_flags |= SA_NOCLDSTOP; | ||
| 315 | #ifdef SA_NOCLDWAIT | 315 | #ifdef SA_NOCLDWAIT | |||||
| HITCBC | 316 | 641 | if (flags & signal_set::no_child_wait) | 316 | 641 | if (flags & signal_set::no_child_wait) | ||
| HITCBC | 317 | 2 | sa_flags |= SA_NOCLDWAIT; | 317 | 2 | sa_flags |= SA_NOCLDWAIT; | ||
| 318 | #endif | 318 | #endif | |||||
| HITCBC | 319 | 641 | if (flags & signal_set::no_defer) | 319 | 641 | if (flags & signal_set::no_defer) | ||
| HITCBC | 320 | 4 | sa_flags |= SA_NODEFER; | 320 | 4 | sa_flags |= SA_NODEFER; | ||
| HITCBC | 321 | 641 | if (flags & signal_set::reset_handler) | 321 | 641 | if (flags & signal_set::reset_handler) | ||
| HITCBC | 322 | 2 | sa_flags |= SA_RESETHAND; | 322 | 2 | sa_flags |= SA_RESETHAND; | ||
| HITCBC | 323 | 641 | return sa_flags; | 323 | 641 | return sa_flags; | ||
| 324 | } | 324 | } | |||||
| 325 | 325 | |||||||
| 326 | // Check if two flag values are compatible | 326 | // Check if two flag values are compatible | |||||
| 327 | inline bool | 327 | inline bool | |||||
| HITCBC | 328 | 2841 | flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested) | 328 | 2841 | flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested) | ||
| 329 | { | 329 | { | |||||
| 330 | // dont_care is always compatible | 330 | // dont_care is always compatible | |||||
| HITCBC | 331 | 5680 | if ((existing & signal_set::dont_care) || | 331 | 5680 | if ((existing & signal_set::dont_care) || | ||
| HITCBC | 332 | 2839 | (requested & signal_set::dont_care)) | 332 | 2839 | (requested & signal_set::dont_care)) | ||
| HITCBC | 333 | 7 | return true; | 333 | 7 | return true; | ||
| 334 | 334 | |||||||
| 335 | // Mask out dont_care bit for comparison | 335 | // Mask out dont_care bit for comparison | |||||
| HITCBC | 336 | 2834 | constexpr auto mask = ~signal_set::dont_care; | 336 | 2834 | constexpr auto mask = ~signal_set::dont_care; | ||
| HITCBC | 337 | 2834 | return (existing & mask) == (requested & mask); | 337 | 2834 | return (existing & mask) == (requested & mask); | ||
| 338 | } | 338 | } | |||||
| 339 | 339 | |||||||
| 340 | // Lazily create the global signal self-pipe. Idempotent; call under | 340 | // Lazily create the global signal self-pipe. Idempotent; call under | |||||
| 341 | // state->mutex before installing the first signal handler so write_fd is | 341 | // state->mutex before installing the first signal handler so write_fd is | |||||
| 342 | // valid by the time the handler can fire. Both ends are non-blocking and | 342 | // valid by the time the handler can fire. Both ends are non-blocking and | |||||
| 343 | // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler). | 343 | // close-on-exec (mirrors the reactor self-pipe setup in select_scheduler). | |||||
| 344 | // Returns the failing call's errno and leaves the fds at -1 if creation | 344 | // Returns the failing call's errno and leaves the fds at -1 if creation | |||||
| 345 | // fails: an exhausted descriptor table and a rejected fcntl are different | 345 | // fails: an exhausted descriptor table and a rejected fcntl are different | |||||
| 346 | // problems to the caller of add(). | 346 | // problems to the caller of add(). | |||||
| 347 | [[nodiscard]] inline std::error_code | 347 | [[nodiscard]] inline std::error_code | |||||
| HITCBC | 348 | 3491 | open_signal_pipe(signal_state* state) | 348 | 3491 | open_signal_pipe(signal_state* state) | ||
| 349 | { | 349 | { | |||||
| HITCBC | 350 | 3491 | if (state->read_fd >= 0) | 350 | 3491 | if (state->read_fd >= 0) | ||
| HITCBC | 351 | 3475 | return {}; | 351 | 3475 | return {}; | ||
| 352 | 352 | |||||||
| 353 | int fds[2]; | 353 | int fds[2]; | |||||
| HITCBC | 354 | 16 | if (::pipe(fds) < 0) | 354 | 16 | if (::pipe(fds) < 0) | ||
| HITCBC | 355 | 1 | return make_err(errno); | 355 | 1 | return make_err(errno); | ||
| 356 | 356 | |||||||
| HITCBC | 357 | 36 | for (int i = 0; i < 2; ++i) | 357 | 36 | for (int i = 0; i < 2; ++i) | ||
| 358 | { | 358 | { | |||||
| HITCBC | 359 | 27 | int fl = ::fcntl(fds[i], F_GETFL, 0); | 359 | 27 | int fl = ::fcntl(fds[i], F_GETFL, 0); | ||
| HITCBC | 360 | 50 | if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 || | 360 | 50 | if (fl == -1 || ::fcntl(fds[i], F_SETFL, fl | O_NONBLOCK) == -1 || | ||
| HITCBC | 361 | 23 | ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) | 361 | 23 | ::fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) | ||
| 362 | { | 362 | { | |||||
| HITCBC | 363 | 6 | auto ec = make_err(errno); | 363 | 6 | auto ec = make_err(errno); | ||
| HITCBC | 364 | 6 | ::close(fds[0]); | 364 | 6 | ::close(fds[0]); | ||
| HITCBC | 365 | 6 | ::close(fds[1]); | 365 | 6 | ::close(fds[1]); | ||
| HITCBC | 366 | 6 | return ec; | 366 | 6 | return ec; | ||
| 367 | } | 367 | } | |||||
| 368 | } | 368 | } | |||||
| 369 | 369 | |||||||
| HITCBC | 370 | 9 | state->read_fd = fds[0]; | 370 | 9 | state->read_fd = fds[0]; | ||
| HITCBC | 371 | 9 | state->write_fd = fds[1]; | 371 | 9 | state->write_fd = fds[1]; | ||
| HITCBC | 372 | 9 | return {}; | 372 | 9 | return {}; | ||
| 373 | } | 373 | } | |||||
| 374 | 374 | |||||||
| 375 | // C signal handler. Async-signal-safe: it touches only the single global | 375 | // C signal handler. Async-signal-safe: it touches only the single global | |||||
| 376 | // write_fd (an int set before any handler is installed) and calls write(), | 376 | // write_fd (an int set before any handler is installed) and calls write(), | |||||
| 377 | // which POSIX lists as async-signal-safe. errno is saved and restored so an | 377 | // which POSIX lists as async-signal-safe. errno is saved and restored so an | |||||
| 378 | // interrupted foreground syscall is unaffected. A full pipe (write returns | 378 | // interrupted foreground syscall is unaffected. A full pipe (write returns | |||||
| 379 | // EAGAIN) or a short write is intentionally dropped — the reactor still | 379 | // EAGAIN) or a short write is intentionally dropped — the reactor still | |||||
| 380 | // coalesces because deliver_signal reports the signal to every waiting set. | 380 | // coalesces because deliver_signal reports the signal to every waiting set. | |||||
| 381 | inline void | 381 | inline void | |||||
| HITCBC | 382 | 1521 | corosio_posix_signal_handler(int signal_number) | 382 | 1521 | corosio_posix_signal_handler(int signal_number) | ||
| 383 | { | 383 | { | |||||
| HITCBC | 384 | 1521 | int saved_errno = errno; | 384 | 1521 | int saved_errno = errno; | ||
| HITCBC | 385 | 1521 | signal_state* state = get_signal_state(); | 385 | 1521 | signal_state* state = get_signal_state(); | ||
| 386 | [[maybe_unused]] ssize_t r = | 386 | [[maybe_unused]] ssize_t r = | |||||
| HITCBC | 387 | 1521 | ::write(state->write_fd, &signal_number, sizeof(int)); | 387 | 1521 | ::write(state->write_fd, &signal_number, sizeof(int)); | ||
| HITCBC | 388 | 1521 | errno = saved_errno; | 388 | 1521 | errno = saved_errno; | ||
| 389 | // With sigaction(), the handler persists automatically (unlike some | 389 | // With sigaction(), the handler persists automatically (unlike some | |||||
| 390 | // signal() implementations that reset to SIG_DFL). | 390 | // signal() implementations that reset to SIG_DFL). | |||||
| HITCBC | 391 | 1521 | } | 391 | 1521 | } | ||
| 392 | 392 | |||||||
| 393 | // Drain the signal self-pipe and deliver each pending signal. Runs in normal | 393 | // Drain the signal self-pipe and deliver each pending signal. Runs in normal | |||||
| 394 | // thread context from the backend event loop, so deliver_signal()'s mutex | 394 | // thread context from the backend event loop, so deliver_signal()'s mutex | |||||
| 395 | // locking and scheduler post are safe here. Reads until EAGAIN (edge- | 395 | // locking and scheduler post are safe here. Reads until EAGAIN (edge- | |||||
| 396 | // triggered backends require a full drain per readiness event). | 396 | // triggered backends require a full drain per readiness event). | |||||
| 397 | inline void | 397 | inline void | |||||
| HITCBC | 398 | 964 | drain_signal_pipe() | 398 | 941 | drain_signal_pipe() | ||
| 399 | { | 399 | { | |||||
| HITCBC | 400 | 964 | signal_state* state = get_signal_state(); | 400 | 941 | signal_state* state = get_signal_state(); | ||
| 401 | int signal_number; | 401 | int signal_number; | |||||
| HITCBC | 402 | 2485 | while (::read(state->read_fd, &signal_number, sizeof(int)) == | 402 | 2462 | while (::read(state->read_fd, &signal_number, sizeof(int)) == | ||
| 403 | static_cast<ssize_t>(sizeof(int))) | 403 | static_cast<ssize_t>(sizeof(int))) | |||||
| 404 | { | 404 | { | |||||
| HITCBC | 405 | 1521 | posix_signal_service::deliver_signal(signal_number); | 405 | 1521 | posix_signal_service::deliver_signal(signal_number); | ||
| 406 | } | 406 | } | |||||
| HITCBC | 407 | 964 | } | 407 | 941 | } | ||
| 408 | 408 | |||||||
| 409 | } // namespace posix_signal_detail | 409 | } // namespace posix_signal_detail | |||||
| 410 | 410 | |||||||
| 411 | // signal_op implementation | 411 | // signal_op implementation | |||||
| 412 | 412 | |||||||
| 413 | inline void | 413 | inline void | |||||
| HITCBC | 414 | 1791 | signal_op::operator()() noexcept | 414 | 1982 | signal_op::operator()() noexcept | ||
| 415 | { | 415 | { | |||||
| HITCBC | 416 | 1791 | if (ec_out) | 416 | 1982 | if (ec_out) | ||
| HITCBC | 417 | 1791 | *ec_out = {}; | 417 | 1982 | *ec_out = {}; | ||
| HITCBC | 418 | 1791 | if (signal_out) | 418 | 1982 | if (signal_out) | ||
| HITCBC | 419 | 1791 | *signal_out = signal_number; | 419 | 1982 | *signal_out = signal_number; | ||
| 420 | 420 | |||||||
| 421 | // Copied out before the reference drops: this op is embedded in | 421 | // Copied out before the reference drops: this op is embedded in | |||||
| 422 | // the set, which the release may recycle. | 422 | // the set, which the release may recycle. | |||||
| HITCBC | 423 | 1791 | auto* service = svc; | 423 | 1982 | auto* service = svc; | ||
| HITCBC | 424 | 1791 | svc = nullptr; | 424 | 1982 | svc = nullptr; | ||
| HITCBC | 425 | 1791 | auto* c = cont; | 425 | 1982 | auto* c = cont; | ||
| HITCBC | 426 | 1791 | auto ex = d; | 426 | 1982 | auto ex = d; | ||
| HITCBC | 427 | 1791 | auto k = std::move(keep); | 427 | 1982 | auto k = std::move(keep); | ||
| 428 | 428 | |||||||
| 429 | // A throw terminates: the wait is no longer parked, so nothing else | 429 | // A throw terminates: the wait is no longer parked, so nothing else | |||||
| 430 | // could resume or abandon the coroutine. | 430 | // could resume or abandon the coroutine. | |||||
| HITCBC | 431 | 1791 | ex.post(*c); | 431 | 1982 | ex.post(*c); | ||
| 432 | 432 | |||||||
| 433 | // Balance the work_started() from start_wait | 433 | // Balance the work_started() from start_wait | |||||
| HITCBC | 434 | 1791 | if (service) | 434 | 1982 | if (service) | ||
| HITCBC | 435 | 738 | service->work_finished(); | 435 | 792 | service->work_finished(); | ||
| HITCBC | 436 | 1791 | } | 436 | 1982 | } | ||
| 437 | 437 | |||||||
| 438 | inline void | 438 | inline void | |||||
| MISUBC | 439 | ✗ | signal_op::destroy() | 439 | ✗ | signal_op::destroy() | ||
| 440 | { | 440 | { | |||||
| 441 | // Embedded in posix_signal: the queued reference keeps it alive | 441 | // Embedded in posix_signal: the queued reference keeps it alive | |||||
| 442 | // while the awaiting coroutine, which may own the set, is destroyed. | 442 | // while the awaiting coroutine, which may own the set, is destroyed. | |||||
| MISUBC | 443 | ✗ | auto* c = std::exchange(cont, nullptr); | 443 | ✗ | auto* c = std::exchange(cont, nullptr); | ||
| MISUBC | 444 | ✗ | auto k = std::move(keep); | 444 | ✗ | auto k = std::move(keep); | ||
| MISUBC | 445 | ✗ | if (c && c->h) | 445 | ✗ | if (c && c->h) | ||
| MISUBC | 446 | ✗ | capy::abandon(*c); | 446 | ✗ | capy::abandon(*c); | ||
| MISUBC | 447 | ✗ | } | 447 | ✗ | } | ||
| 448 | 448 | |||||||
| 449 | // posix_signal implementation | 449 | // posix_signal implementation | |||||
| 450 | 450 | |||||||
| HITCBC | 451 | 3404 | inline posix_signal::posix_signal(posix_signal_service& svc) noexcept | 451 | 3404 | inline posix_signal::posix_signal(posix_signal_service& svc) noexcept | ||
| HITCBC | 452 | 3404 | : svc_(svc) | 452 | 3404 | : svc_(svc) | ||
| 453 | { | 453 | { | |||||
| HITCBC | 454 | 3404 | } | 454 | 3404 | } | ||
| 455 | 455 | |||||||
| 456 | inline std::coroutine_handle<> | 456 | inline std::coroutine_handle<> | |||||
| HITCBC | 457 | 205800 | posix_signal::wait( | 457 | 205987 | posix_signal::wait( | ||
| 458 | capy::continuation& cont, | 458 | capy::continuation& cont, | |||||
| 459 | capy::executor_ref d, | 459 | capy::executor_ref d, | |||||
| 460 | std::stop_token token, | 460 | std::stop_token token, | |||||
| 461 | std::error_code* ec, | 461 | std::error_code* ec, | |||||
| 462 | int* signal_out) | 462 | int* signal_out) | |||||
| 463 | { | 463 | { | |||||
| HITCBC | 464 | 205800 | pending_op_.cont = &cont; | 464 | 205987 | pending_op_.cont = &cont; | ||
| HITCBC | 465 | 205800 | pending_op_.d = d; | 465 | 205987 | pending_op_.d = d; | ||
| HITCBC | 466 | 205800 | pending_op_.ec_out = ec; | 466 | 205987 | pending_op_.ec_out = ec; | ||
| HITCBC | 467 | 205800 | pending_op_.signal_out = signal_out; | 467 | 205987 | pending_op_.signal_out = signal_out; | ||
| HITCBC | 468 | 205800 | pending_op_.signal_number = 0; | 468 | 205987 | pending_op_.signal_number = 0; | ||
| 469 | 469 | |||||||
| 470 | // Disarm any callback left over from a previous wait before doing | 470 | // Disarm any callback left over from a previous wait before doing | |||||
| 471 | // anything else, including the early return below: otherwise that | 471 | // anything else, including the early return below: otherwise that | |||||
| 472 | // path leaves this object owning a callback it no longer uses. | 472 | // path leaves this object owning a callback it no longer uses. | |||||
| 473 | // Outside start_wait's lock on purpose: ~stop_callback blocks until a | 473 | // Outside start_wait's lock on purpose: ~stop_callback blocks until a | |||||
| 474 | // concurrently running callback returns, and that callback takes | 474 | // concurrently running callback returns, and that callback takes | |||||
| 475 | // posix_signal_service::mutex_. | 475 | // posix_signal_service::mutex_. | |||||
| HITCBC | 476 | 205800 | stop_cb_.reset(); | 476 | 205987 | stop_cb_.reset(); | ||
| 477 | 477 | |||||||
| HITCBC | 478 | 205800 | if (token.stop_requested()) | 478 | 205987 | if (token.stop_requested()) | ||
| 479 | { | 479 | { | |||||
| HITCBC | 480 | 58 | if (ec) | 480 | 90 | if (ec) | ||
| HITCBC | 481 | 58 | *ec = make_error_code(capy::error::canceled); | 481 | 90 | *ec = make_error_code(capy::error::canceled); | ||
| HITCBC | 482 | 58 | if (signal_out) | 482 | 90 | if (signal_out) | ||
| HITCBC | 483 | 58 | *signal_out = 0; | 483 | 90 | *signal_out = 0; | ||
| HITCBC | 484 | 58 | d.post(*pending_op_.cont); | 484 | 90 | d.post(*pending_op_.cont); | ||
| 485 | // completion is always posted to scheduler queue, never inline. | 485 | // completion is always posted to scheduler queue, never inline. | |||||
| HITCBC | 486 | 58 | return std::noop_coroutine(); | 486 | 90 | return std::noop_coroutine(); | ||
| 487 | } | 487 | } | |||||
| 488 | 488 | |||||||
| 489 | // Clearing the flag before arming is load-bearing: reset_token_cancel | 489 | // Clearing the flag before arming is load-bearing: reset_token_cancel | |||||
| 490 | // must run immediately before emplace, not before the early return | 490 | // must run immediately before emplace, not before the early return | |||||
| 491 | // above. | 491 | // above. | |||||
| HITCBC | 492 | 205742 | svc_.reset_token_cancel(*this); | 492 | 205897 | svc_.reset_token_cancel(*this); | ||
| HITCBC | 493 | 205742 | if (token.stop_possible()) | 493 | 205897 | if (token.stop_possible()) | ||
| HITCBC | 494 | 5000 | stop_cb_.emplace(token, token_canceller{this}); | 494 | 5155 | stop_cb_.emplace(token, token_canceller{this}); | ||
| 495 | 495 | |||||||
| HITCBC | 496 | 205742 | svc_.start_wait(*this, &pending_op_); | 496 | 205897 | svc_.start_wait(*this, &pending_op_); | ||
| 497 | // completion is always posted to scheduler queue, never inline. | 497 | // completion is always posted to scheduler queue, never inline. | |||||
| HITCBC | 498 | 205742 | return std::noop_coroutine(); | 498 | 205897 | return std::noop_coroutine(); | ||
| 499 | } | 499 | } | |||||
| 500 | 500 | |||||||
| 501 | inline std::error_code | 501 | inline std::error_code | |||||
| HITCBC | 502 | 3495 | posix_signal::add(int signal_number, signal_set::flags_t flags) | 502 | 3495 | posix_signal::add(int signal_number, signal_set::flags_t flags) | ||
| 503 | { | 503 | { | |||||
| HITCBC | 504 | 3495 | return svc_.add_signal(*this, signal_number, flags); | 504 | 3495 | return svc_.add_signal(*this, signal_number, flags); | ||
| 505 | } | 505 | } | |||||
| 506 | 506 | |||||||
| 507 | inline std::error_code | 507 | inline std::error_code | |||||
| HITCBC | 508 | 26 | posix_signal::remove(int signal_number) | 508 | 26 | posix_signal::remove(int signal_number) | ||
| 509 | { | 509 | { | |||||
| HITCBC | 510 | 26 | return svc_.remove_signal(*this, signal_number); | 510 | 26 | return svc_.remove_signal(*this, signal_number); | ||
| 511 | } | 511 | } | |||||
| 512 | 512 | |||||||
| 513 | inline std::error_code | 513 | inline std::error_code | |||||
| HITCBC | 514 | 3490 | posix_signal::clear() | 514 | 3490 | posix_signal::clear() | ||
| 515 | { | 515 | { | |||||
| HITCBC | 516 | 3490 | return svc_.clear_signals(*this); | 516 | 3490 | return svc_.clear_signals(*this); | ||
| 517 | } | 517 | } | |||||
| 518 | 518 | |||||||
| 519 | inline void | 519 | inline void | |||||
| HITCBC | 520 | 203497 | posix_signal::cancel() noexcept | 520 | 203497 | posix_signal::cancel() noexcept | ||
| 521 | { | 521 | { | |||||
| HITCBC | 522 | 203497 | svc_.cancel_wait(*this); | 522 | 203497 | svc_.cancel_wait(*this); | ||
| HITCBC | 523 | 203497 | } | 523 | 203497 | } | ||
| 524 | 524 | |||||||
| 525 | // posix_signal_service implementation | 525 | // posix_signal_service implementation | |||||
| 526 | 526 | |||||||
| HITCBC | 527 | 583 | inline posix_signal_service::posix_signal_service( | 527 | 583 | inline posix_signal_service::posix_signal_service( | ||
| HITCBC | 528 | 583 | capy::execution_context& ctx) | 528 | 583 | capy::execution_context& ctx) | ||
| HITCBC | 529 | 583 | : sched_(&get_scheduler(ctx)) | 529 | 583 | : sched_(&get_scheduler(ctx)) | ||
| 530 | { | 530 | { | |||||
| HITCBC | 531 | 37895 | for (int i = 0; i < max_signal_number; ++i) | 531 | 37895 | for (int i = 0; i < max_signal_number; ++i) | ||
| 532 | { | 532 | { | |||||
| HITCBC | 533 | 37312 | registrations_[i] = nullptr; | 533 | 37312 | registrations_[i] = nullptr; | ||
| HITCBC | 534 | 37312 | registration_count_[i] = 0; | 534 | 37312 | registration_count_[i] = 0; | ||
| 535 | } | 535 | } | |||||
| HITCBC | 536 | 583 | add_service(this); | 536 | 583 | add_service(this); | ||
| HITCBC | 537 | 583 | } | 537 | 583 | } | ||
| 538 | 538 | |||||||
| HITCBC | 539 | 1166 | inline posix_signal_service::~posix_signal_service() | 539 | 1166 | inline posix_signal_service::~posix_signal_service() | ||
| 540 | { | 540 | { | |||||
| HITCBC | 541 | 583 | remove_service(this); | 541 | 583 | remove_service(this); | ||
| HITCBC | 542 | 1166 | } | 542 | 1166 | } | ||
| 543 | 543 | |||||||
| 544 | inline void | 544 | inline void | |||||
| HITCBC | 545 | 583 | posix_signal_service::shutdown() | 545 | 583 | posix_signal_service::shutdown() | ||
| 546 | { | 546 | { | |||||
| 547 | // Each visited impl is pinned with acquire() so a handle | 547 | // Each visited impl is pinned with acquire() so a handle | |||||
| 548 | // destroy() racing this walk cannot free it mid-walk. The | 548 | // destroy() racing this walk cannot free it mid-walk. The | |||||
| 549 | // matching release() runs after the locks drop: if that handle's | 549 | // matching release() runs after the locks drop: if that handle's | |||||
| 550 | // destroy() raced in, this release() is the last one, and | 550 | // destroy() raced in, this release() is the last one, and | |||||
| 551 | // ~posix_signal destroys an armed stop_cb_ whose ~stop_callback | 551 | // ~posix_signal destroys an armed stop_cb_ whose ~stop_callback | |||||
| 552 | // waits on a token_canceller that takes mutex_. | 552 | // waits on a token_canceller that takes mutex_. | |||||
| HITCBC | 553 | 583 | std::vector<posix_signal*> doomed; | 553 | 583 | std::vector<posix_signal*> doomed; | ||
| HITCBC | 554 | 583 | std::vector<capy::continuation> parked; | 554 | 583 | std::vector<capy::continuation> parked; | ||
| 555 | 555 | |||||||
| 556 | { | 556 | { | |||||
| 557 | posix_signal_detail::signal_state* state = | 557 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 558 | 583 | posix_signal_detail::get_signal_state(); | 558 | 583 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 559 | 583 | std::lock_guard state_lock(state->mutex); | 559 | 583 | std::lock_guard state_lock(state->mutex); | ||
| HITCBC | 560 | 583 | std::lock_guard lock(mutex_); | 560 | 583 | std::lock_guard lock(mutex_); | ||
| 561 | 561 | |||||||
| HITCBC | 562 | 583 | pool_.shutdown( | 562 | 583 | pool_.shutdown( | ||
| HITCBC | 563 | 583 | [&](posix_signal* impl) | 563 | 583 | [&](posix_signal* impl) | ||
| 564 | { | 564 | { | |||||
| HITCBC | 565 | 3210 | acquire(impl); | 565 | 3210 | acquire(impl); | ||
| HITCBC | 566 | 3210 | doomed.push_back(impl); | 566 | 3210 | doomed.push_back(impl); | ||
| HITCBC | 567 | 3210 | }); | 567 | 3210 | }); | ||
| HITCBC | 568 | 3793 | for (auto* impl : doomed) | 568 | 3793 | for (auto* impl : doomed) | ||
| 569 | { | 569 | { | |||||
| 570 | // The parked wait's coroutine is destroyed once the locks | 570 | // The parked wait's coroutine is destroyed once the locks | |||||
| 571 | // drop: destroying it can destroy the set, which takes them. | 571 | // drop: destroying it can destroy the set, which takes them. | |||||
| HITCBC | 572 | 3210 | if (std::exchange(impl->waiting_, false)) | 572 | 3210 | if (std::exchange(impl->waiting_, false)) | ||
| 573 | { | 573 | { | |||||
| HITCBC | 574 | 3210 | if (auto* c = std::exchange(impl->pending_op_.cont, nullptr)) | 574 | 3210 | if (auto* c = std::exchange(impl->pending_op_.cont, nullptr)) | ||
| 575 | { | 575 | { | |||||
| HITCBC | 576 | 3210 | if (c->h) | 576 | 3210 | if (c->h) | ||
| HITCBC | 577 | 3210 | parked.push_back(*c); | 577 | 3210 | parked.push_back(*c); | ||
| 578 | } | 578 | } | |||||
| 579 | } | 579 | } | |||||
| 580 | 580 | |||||||
| HITCBC | 581 | 6420 | while (auto* reg = impl->signals_) | 581 | 6420 | while (auto* reg = impl->signals_) | ||
| 582 | { | 582 | { | |||||
| HITCBC | 583 | 3210 | int const signal_number = reg->signal_number; | 583 | 3210 | int const signal_number = reg->signal_number; | ||
| 584 | 584 | |||||||
| 585 | // The registration table outlives every io_context, so a set | 585 | // The registration table outlives every io_context, so a set | |||||
| 586 | // still registered here has to give its count and disposition | 586 | // still registered here has to give its count and disposition | |||||
| 587 | // back the way clear() would: otherwise the signal stays | 587 | // back the way clear() would: otherwise the signal stays | |||||
| 588 | // installed with these flags and the next add() of it is | 588 | // installed with these flags and the next add() of it is | |||||
| 589 | // refused. The per-node table unlink clear() also does is | 589 | // refused. The per-node table unlink clear() also does is | |||||
| 590 | // skipped in favour of the wholesale null-out below. | 590 | // skipped in favour of the wholesale null-out below. | |||||
| HITCBC | 591 | 3210 | if (state->registration_count[signal_number] == 1) | 591 | 3210 | if (state->registration_count[signal_number] == 1) | ||
| 592 | { | 592 | { | |||||
| HITCBC | 593 | 408 | struct sigaction sa = {}; | 593 | 408 | struct sigaction sa = {}; | ||
| HITCBC | 594 | 408 | sa.sa_handler = SIG_DFL; | 594 | 408 | sa.sa_handler = SIG_DFL; | ||
| HITCBC | 595 | 408 | sigemptyset(&sa.sa_mask); | 595 | 408 | sigemptyset(&sa.sa_mask); | ||
| HITCBC | 596 | 408 | sa.sa_flags = 0; | 596 | 408 | sa.sa_flags = 0; | ||
| HITCBC | 597 | 408 | std::ignore = ::sigaction(signal_number, &sa, nullptr); | 597 | 408 | std::ignore = ::sigaction(signal_number, &sa, nullptr); | ||
| HITCBC | 598 | 408 | state->registered_flags[signal_number] = signal_set::none; | 598 | 408 | state->registered_flags[signal_number] = signal_set::none; | ||
| 599 | } | 599 | } | |||||
| 600 | 600 | |||||||
| HITCBC | 601 | 3210 | --state->registration_count[signal_number]; | 601 | 3210 | --state->registration_count[signal_number]; | ||
| HITCBC | 602 | 3210 | --registration_count_[signal_number]; | 602 | 3210 | --registration_count_[signal_number]; | ||
| 603 | 603 | |||||||
| HITCBC | 604 | 3210 | impl->signals_ = reg->next_in_set; | 604 | 3210 | impl->signals_ = reg->next_in_set; | ||
| HITCBC | 605 | 3210 | delete reg; | 605 | 3210 | delete reg; | ||
| HITCBC | 606 | 3210 | } | 606 | 3210 | } | ||
| 607 | } | 607 | } | |||||
| 608 | 608 | |||||||
| 609 | // Every live registration hung off an implementation this pool | 609 | // Every live registration hung off an implementation this pool | |||||
| 610 | // owns, so the whole table goes stale at once and can be dropped | 610 | // owns, so the whole table goes stale at once and can be dropped | |||||
| 611 | // wholesale rather than node by node. It has to be dropped: | 611 | // wholesale rather than node by node. It has to be dropped: | |||||
| 612 | // deliver_signal() walks this service until the destructor | 612 | // deliver_signal() walks this service until the destructor | |||||
| 613 | // unlinks it from the global list. | 613 | // unlinks it from the global list. | |||||
| HITCBC | 614 | 37895 | for (int i = 0; i < max_signal_number; ++i) | 614 | 37895 | for (int i = 0; i < max_signal_number; ++i) | ||
| HITCBC | 615 | 37312 | registrations_[i] = nullptr; | 615 | 37312 | registrations_[i] = nullptr; | ||
| HITCBC | 616 | 583 | } | 616 | 583 | } | ||
| 617 | 617 | |||||||
| HITCBC | 618 | 3793 | for (auto& c : parked) | 618 | 3793 | for (auto& c : parked) | ||
| HITCBC | 619 | 3210 | capy::abandon(c); | 619 | 3210 | capy::abandon(c); | ||
| 620 | 620 | |||||||
| HITCBC | 621 | 3793 | for (auto* impl : doomed) | 621 | 3793 | for (auto* impl : doomed) | ||
| HITCBC | 622 | 3210 | release(impl); | 622 | 3210 | release(impl); | ||
| HITCBC | 623 | 583 | } | 623 | 583 | } | ||
| 624 | 624 | |||||||
| 625 | inline io_object::implementation* | 625 | inline io_object::implementation* | |||||
| HITCBC | 626 | 3476 | posix_signal_service::construct() | 626 | 3476 | posix_signal_service::construct() | ||
| 627 | { | 627 | { | |||||
| HITCBC | 628 | 3476 | return pool_.acquire(*this); | 628 | 3476 | return pool_.acquire(*this); | ||
| 629 | } | 629 | } | |||||
| 630 | 630 | |||||||
| 631 | inline void | 631 | inline void | |||||
| HITCBC | 632 | 3476 | posix_signal::retire() noexcept | 632 | 3476 | posix_signal::retire() noexcept | ||
| 633 | { | 633 | { | |||||
| HITCBC | 634 | 3476 | svc_.pool_.recycle(this); | 634 | 3476 | svc_.pool_.recycle(this); | ||
| HITCBC | 635 | 3476 | } | 635 | 3476 | } | ||
| 636 | 636 | |||||||
| 637 | inline std::error_code | 637 | inline std::error_code | |||||
| HITCBC | 638 | 3495 | posix_signal_service::add_signal( | 638 | 3495 | posix_signal_service::add_signal( | ||
| 639 | posix_signal& impl, int signal_number, signal_set::flags_t flags) | 639 | posix_signal& impl, int signal_number, signal_set::flags_t flags) | |||||
| 640 | { | 640 | { | |||||
| HITCBC | 641 | 3495 | if (signal_number < 0 || signal_number >= max_signal_number) | 641 | 3495 | if (signal_number < 0 || signal_number >= max_signal_number) | ||
| HITCBC | 642 | 4 | return make_error_code(std::errc::invalid_argument); | 642 | 4 | return make_error_code(std::errc::invalid_argument); | ||
| 643 | 643 | |||||||
| 644 | // Validate that requested flags are supported on this platform | 644 | // Validate that requested flags are supported on this platform | |||||
| 645 | // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems) | 645 | // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems) | |||||
| HITCBC | 646 | 3491 | if (!posix_signal_detail::flags_supported(flags)) | 646 | 3491 | if (!posix_signal_detail::flags_supported(flags)) | ||
| MISUBC | 647 | ✗ | return make_error_code(std::errc::operation_not_supported); | 647 | ✗ | return make_error_code(std::errc::operation_not_supported); | ||
| 648 | 648 | |||||||
| 649 | posix_signal_detail::signal_state* state = | 649 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 650 | 3491 | posix_signal_detail::get_signal_state(); | 650 | 3491 | posix_signal_detail::get_signal_state(); | ||
| 651 | 651 | |||||||
| 652 | // Ensure the global self-pipe exists and this service's scheduler is | 652 | // Ensure the global self-pipe exists and this service's scheduler is | |||||
| 653 | // watching its read end, BEFORE taking the registration locks: | 653 | // watching its read end, BEFORE taking the registration locks: | |||||
| 654 | // register_signal_reader takes the descriptor mutex (via | 654 | // register_signal_reader takes the descriptor mutex (via | |||||
| 655 | // register_descriptor), which no path may take while holding the | 655 | // register_descriptor), which no path may take while holding the | |||||
| 656 | // signal-state or service mutexes. call_once makes the once-per-service | 656 | // signal-state or service mutexes. call_once makes the once-per-service | |||||
| 657 | // registration safe when two signal_sets on this context race add() | 657 | // registration safe when two signal_sets on this context race add() | |||||
| 658 | // from different threads. | 658 | // from different threads. | |||||
| 659 | { | 659 | { | |||||
| HITCBC | 660 | 3491 | std::lock_guard state_lock(state->mutex); | 660 | 3491 | std::lock_guard state_lock(state->mutex); | ||
| HITCBC | 661 | 3491 | if (auto ec = posix_signal_detail::open_signal_pipe(state)) | 661 | 3491 | if (auto ec = posix_signal_detail::open_signal_pipe(state)) | ||
| HITCBC | 662 | 7 | return ec; | 662 | 7 | return ec; | ||
| HITCBC | 663 | 3491 | } | 663 | 3491 | } | ||
| 664 | { | 664 | { | |||||
| 665 | // Success-latched so a failed environmental registration | 665 | // Success-latched so a failed environmental registration | |||||
| 666 | // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add() | 666 | // (epoll_ctl ENOMEM/ENOSPC) is retried by the next add() | |||||
| 667 | // instead of being lost; the code travels the return channel. | 667 | // instead of being lost; the code travels the return channel. | |||||
| HITCBC | 668 | 3484 | std::lock_guard reg_lock(reader_mutex_); | 668 | 3484 | std::lock_guard reg_lock(reader_mutex_); | ||
| HITCBC | 669 | 3484 | if (!reader_registered_) | 669 | 3484 | if (!reader_registered_) | ||
| 670 | { | 670 | { | |||||
| HITCBC | 671 | 555 | if (auto ec = sched_->register_signal_reader(state->read_fd)) | 671 | 555 | if (auto ec = sched_->register_signal_reader(state->read_fd)) | ||
| HITCBC | 672 | 2 | return ec; | 672 | 2 | return ec; | ||
| HITCBC | 673 | 553 | reader_registered_ = true; | 673 | 553 | reader_registered_ = true; | ||
| 674 | } | 674 | } | |||||
| HITCBC | 675 | 3484 | } | 675 | 3484 | } | ||
| 676 | 676 | |||||||
| HITCBC | 677 | 3482 | std::lock_guard state_lock(state->mutex); | 677 | 3482 | std::lock_guard state_lock(state->mutex); | ||
| HITCBC | 678 | 3482 | std::lock_guard lock(mutex_); | 678 | 3482 | std::lock_guard lock(mutex_); | ||
| 679 | 679 | |||||||
| 680 | // Find insertion point (list is sorted by signal number) | 680 | // Find insertion point (list is sorted by signal number) | |||||
| HITCBC | 681 | 3482 | signal_registration** insertion_point = &impl.signals_; | 681 | 3482 | signal_registration** insertion_point = &impl.signals_; | ||
| HITCBC | 682 | 3482 | signal_registration* reg = impl.signals_; | 682 | 3482 | signal_registration* reg = impl.signals_; | ||
| HITCBC | 683 | 3505 | while (reg && reg->signal_number < signal_number) | 683 | 3505 | while (reg && reg->signal_number < signal_number) | ||
| 684 | { | 684 | { | |||||
| HITCBC | 685 | 23 | insertion_point = ®->next_in_set; | 685 | 23 | insertion_point = ®->next_in_set; | ||
| HITCBC | 686 | 23 | reg = reg->next_in_set; | 686 | 23 | reg = reg->next_in_set; | ||
| 687 | } | 687 | } | |||||
| 688 | 688 | |||||||
| 689 | // Already registered in this set - check flag compatibility | 689 | // Already registered in this set - check flag compatibility | |||||
| 690 | // (same signal_set adding same signal twice with different flags) | 690 | // (same signal_set adding same signal twice with different flags) | |||||
| HITCBC | 691 | 3482 | if (reg && reg->signal_number == signal_number) | 691 | 3482 | if (reg && reg->signal_number == signal_number) | ||
| 692 | { | 692 | { | |||||
| HITCBC | 693 | 13 | if (!posix_signal_detail::flags_compatible(reg->flags, flags)) | 693 | 13 | if (!posix_signal_detail::flags_compatible(reg->flags, flags)) | ||
| HITCBC | 694 | 4 | return make_error_code(std::errc::invalid_argument); | 694 | 4 | return make_error_code(std::errc::invalid_argument); | ||
| HITCBC | 695 | 9 | return {}; | 695 | 9 | return {}; | ||
| 696 | } | 696 | } | |||||
| 697 | 697 | |||||||
| 698 | // Check flag compatibility with global registration | 698 | // Check flag compatibility with global registration | |||||
| 699 | // (different signal_set already registered this signal with different flags) | 699 | // (different signal_set already registered this signal with different flags) | |||||
| HITCBC | 700 | 3469 | if (state->registration_count[signal_number] > 0) | 700 | 3469 | if (state->registration_count[signal_number] > 0) | ||
| 701 | { | 701 | { | |||||
| HITCBC | 702 | 2828 | if (!posix_signal_detail::flags_compatible( | 702 | 2828 | if (!posix_signal_detail::flags_compatible( | ||
| 703 | state->registered_flags[signal_number], flags)) | 703 | state->registered_flags[signal_number], flags)) | |||||
| HITCBC | 704 | 2 | return make_error_code(std::errc::invalid_argument); | 704 | 2 | return make_error_code(std::errc::invalid_argument); | ||
| 705 | } | 705 | } | |||||
| 706 | 706 | |||||||
| HITCBC | 707 | 3467 | auto* new_reg = new signal_registration; | 707 | 3467 | auto* new_reg = new signal_registration; | ||
| HITCBC | 708 | 3467 | new_reg->signal_number = signal_number; | 708 | 3467 | new_reg->signal_number = signal_number; | ||
| HITCBC | 709 | 3467 | new_reg->flags = flags; | 709 | 3467 | new_reg->flags = flags; | ||
| HITCBC | 710 | 3467 | new_reg->owner = &impl; | 710 | 3467 | new_reg->owner = &impl; | ||
| HITCBC | 711 | 3467 | new_reg->undelivered = 0; | 711 | 3467 | new_reg->undelivered = 0; | ||
| 712 | 712 | |||||||
| 713 | // Install signal handler on first global registration | 713 | // Install signal handler on first global registration | |||||
| HITCBC | 714 | 3467 | if (state->registration_count[signal_number] == 0) | 714 | 3467 | if (state->registration_count[signal_number] == 0) | ||
| 715 | { | 715 | { | |||||
| HITCBC | 716 | 641 | struct sigaction sa = {}; | 716 | 641 | struct sigaction sa = {}; | ||
| HITCBC | 717 | 641 | sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; | 717 | 641 | sa.sa_handler = posix_signal_detail::corosio_posix_signal_handler; | ||
| HITCBC | 718 | 641 | sigemptyset(&sa.sa_mask); | 718 | 641 | sigemptyset(&sa.sa_mask); | ||
| HITCBC | 719 | 641 | sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); | 719 | 641 | sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags); | ||
| 720 | 720 | |||||||
| HITCBC | 721 | 641 | if (::sigaction(signal_number, &sa, nullptr) < 0) | 721 | 641 | if (::sigaction(signal_number, &sa, nullptr) < 0) | ||
| 722 | { | 722 | { | |||||
| HITCBC | 723 | 1 | delete new_reg; | 723 | 1 | delete new_reg; | ||
| HITCBC | 724 | 1 | return make_error_code(std::errc::invalid_argument); | 724 | 1 | return make_error_code(std::errc::invalid_argument); | ||
| 725 | } | 725 | } | |||||
| 726 | 726 | |||||||
| 727 | // Store the flags used for first registration | 727 | // Store the flags used for first registration | |||||
| HITCBC | 728 | 640 | state->registered_flags[signal_number] = flags; | 728 | 640 | state->registered_flags[signal_number] = flags; | ||
| 729 | } | 729 | } | |||||
| 730 | 730 | |||||||
| HITCBC | 731 | 3466 | new_reg->next_in_set = reg; | 731 | 3466 | new_reg->next_in_set = reg; | ||
| HITCBC | 732 | 3466 | *insertion_point = new_reg; | 732 | 3466 | *insertion_point = new_reg; | ||
| 733 | 733 | |||||||
| HITCBC | 734 | 3466 | new_reg->next_in_table = registrations_[signal_number]; | 734 | 3466 | new_reg->next_in_table = registrations_[signal_number]; | ||
| HITCBC | 735 | 3466 | new_reg->prev_in_table = nullptr; | 735 | 3466 | new_reg->prev_in_table = nullptr; | ||
| HITCBC | 736 | 3466 | if (registrations_[signal_number]) | 736 | 3466 | if (registrations_[signal_number]) | ||
| HITCBC | 737 | 2818 | registrations_[signal_number]->prev_in_table = new_reg; | 737 | 2818 | registrations_[signal_number]->prev_in_table = new_reg; | ||
| HITCBC | 738 | 3466 | registrations_[signal_number] = new_reg; | 738 | 3466 | registrations_[signal_number] = new_reg; | ||
| 739 | 739 | |||||||
| HITCBC | 740 | 3466 | ++state->registration_count[signal_number]; | 740 | 3466 | ++state->registration_count[signal_number]; | ||
| HITCBC | 741 | 3466 | ++registration_count_[signal_number]; | 741 | 3466 | ++registration_count_[signal_number]; | ||
| 742 | 742 | |||||||
| HITCBC | 743 | 3466 | return {}; | 743 | 3466 | return {}; | ||
| HITCBC | 744 | 3482 | } | 744 | 3482 | } | ||
| 745 | 745 | |||||||
| 746 | inline std::error_code | 746 | inline std::error_code | |||||
| HITCBC | 747 | 26 | posix_signal_service::remove_signal(posix_signal& impl, int signal_number) | 747 | 26 | posix_signal_service::remove_signal(posix_signal& impl, int signal_number) | ||
| 748 | { | 748 | { | |||||
| HITCBC | 749 | 26 | if (signal_number < 0 || signal_number >= max_signal_number) | 749 | 26 | if (signal_number < 0 || signal_number >= max_signal_number) | ||
| HITCBC | 750 | 2 | return make_error_code(std::errc::invalid_argument); | 750 | 2 | return make_error_code(std::errc::invalid_argument); | ||
| 751 | 751 | |||||||
| 752 | posix_signal_detail::signal_state* state = | 752 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 753 | 24 | posix_signal_detail::get_signal_state(); | 753 | 24 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 754 | 24 | std::lock_guard state_lock(state->mutex); | 754 | 24 | std::lock_guard state_lock(state->mutex); | ||
| HITCBC | 755 | 24 | std::lock_guard lock(mutex_); | 755 | 24 | std::lock_guard lock(mutex_); | ||
| 756 | 756 | |||||||
| HITCBC | 757 | 24 | signal_registration** deletion_point = &impl.signals_; | 757 | 24 | signal_registration** deletion_point = &impl.signals_; | ||
| HITCBC | 758 | 24 | signal_registration* reg = impl.signals_; | 758 | 24 | signal_registration* reg = impl.signals_; | ||
| HITCBC | 759 | 26 | while (reg && reg->signal_number < signal_number) | 759 | 26 | while (reg && reg->signal_number < signal_number) | ||
| 760 | { | 760 | { | |||||
| HITCBC | 761 | 2 | deletion_point = ®->next_in_set; | 761 | 2 | deletion_point = ®->next_in_set; | ||
| HITCBC | 762 | 2 | reg = reg->next_in_set; | 762 | 2 | reg = reg->next_in_set; | ||
| 763 | } | 763 | } | |||||
| 764 | 764 | |||||||
| HITCBC | 765 | 24 | if (!reg || reg->signal_number != signal_number) | 765 | 24 | if (!reg || reg->signal_number != signal_number) | ||
| HITCBC | 766 | 3 | return {}; | 766 | 3 | return {}; | ||
| 767 | 767 | |||||||
| 768 | // Restore default handler on last global unregistration | 768 | // Restore default handler on last global unregistration | |||||
| HITCBC | 769 | 21 | if (state->registration_count[signal_number] == 1) | 769 | 21 | if (state->registration_count[signal_number] == 1) | ||
| 770 | { | 770 | { | |||||
| HITCBC | 771 | 17 | struct sigaction sa = {}; | 771 | 17 | struct sigaction sa = {}; | ||
| HITCBC | 772 | 17 | sa.sa_handler = SIG_DFL; | 772 | 17 | sa.sa_handler = SIG_DFL; | ||
| HITCBC | 773 | 17 | sigemptyset(&sa.sa_mask); | 773 | 17 | sigemptyset(&sa.sa_mask); | ||
| HITCBC | 774 | 17 | sa.sa_flags = 0; | 774 | 17 | sa.sa_flags = 0; | ||
| 775 | 775 | |||||||
| HITCBC | 776 | 17 | if (::sigaction(signal_number, &sa, nullptr) < 0) | 776 | 17 | if (::sigaction(signal_number, &sa, nullptr) < 0) | ||
| HITCBC | 777 | 1 | return make_error_code(std::errc::invalid_argument); | 777 | 1 | return make_error_code(std::errc::invalid_argument); | ||
| 778 | 778 | |||||||
| 779 | // Clear stored flags | 779 | // Clear stored flags | |||||
| HITCBC | 780 | 16 | state->registered_flags[signal_number] = signal_set::none; | 780 | 16 | state->registered_flags[signal_number] = signal_set::none; | ||
| 781 | } | 781 | } | |||||
| 782 | 782 | |||||||
| HITCBC | 783 | 20 | *deletion_point = reg->next_in_set; | 783 | 20 | *deletion_point = reg->next_in_set; | ||
| 784 | 784 | |||||||
| HITCBC | 785 | 20 | if (registrations_[signal_number] == reg) | 785 | 20 | if (registrations_[signal_number] == reg) | ||
| HITCBC | 786 | 18 | registrations_[signal_number] = reg->next_in_table; | 786 | 18 | registrations_[signal_number] = reg->next_in_table; | ||
| HITCBC | 787 | 20 | if (reg->prev_in_table) | 787 | 20 | if (reg->prev_in_table) | ||
| HITCBC | 788 | 2 | reg->prev_in_table->next_in_table = reg->next_in_table; | 788 | 2 | reg->prev_in_table->next_in_table = reg->next_in_table; | ||
| HITCBC | 789 | 20 | if (reg->next_in_table) | 789 | 20 | if (reg->next_in_table) | ||
| HITCBC | 790 | 2 | reg->next_in_table->prev_in_table = reg->prev_in_table; | 790 | 2 | reg->next_in_table->prev_in_table = reg->prev_in_table; | ||
| 791 | 791 | |||||||
| HITCBC | 792 | 20 | --state->registration_count[signal_number]; | 792 | 20 | --state->registration_count[signal_number]; | ||
| HITCBC | 793 | 20 | --registration_count_[signal_number]; | 793 | 20 | --registration_count_[signal_number]; | ||
| 794 | 794 | |||||||
| HITCBC | 795 | 20 | delete reg; | 795 | 20 | delete reg; | ||
| HITCBC | 796 | 20 | return {}; | 796 | 20 | return {}; | ||
| HITCBC | 797 | 24 | } | 797 | 24 | } | ||
| 798 | 798 | |||||||
| 799 | inline std::error_code | 799 | inline std::error_code | |||||
| HITCBC | 800 | 3490 | posix_signal_service::clear_signals(posix_signal& impl) | 800 | 3490 | posix_signal_service::clear_signals(posix_signal& impl) | ||
| 801 | { | 801 | { | |||||
| 802 | posix_signal_detail::signal_state* state = | 802 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 803 | 3490 | posix_signal_detail::get_signal_state(); | 803 | 3490 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 804 | 3490 | std::lock_guard state_lock(state->mutex); | 804 | 3490 | std::lock_guard state_lock(state->mutex); | ||
| HITCBC | 805 | 3490 | std::lock_guard lock(mutex_); | 805 | 3490 | std::lock_guard lock(mutex_); | ||
| 806 | 806 | |||||||
| HITCBC | 807 | 3490 | std::error_code first_error; | 807 | 3490 | std::error_code first_error; | ||
| 808 | 808 | |||||||
| HITCBC | 809 | 3726 | while (signal_registration* reg = impl.signals_) | 809 | 3726 | while (signal_registration* reg = impl.signals_) | ||
| 810 | { | 810 | { | |||||
| HITCBC | 811 | 236 | int signal_number = reg->signal_number; | 811 | 236 | int signal_number = reg->signal_number; | ||
| 812 | 812 | |||||||
| HITCBC | 813 | 236 | if (state->registration_count[signal_number] == 1) | 813 | 236 | if (state->registration_count[signal_number] == 1) | ||
| 814 | { | 814 | { | |||||
| HITCBC | 815 | 216 | struct sigaction sa = {}; | 815 | 216 | struct sigaction sa = {}; | ||
| HITCBC | 816 | 216 | sa.sa_handler = SIG_DFL; | 816 | 216 | sa.sa_handler = SIG_DFL; | ||
| HITCBC | 817 | 216 | sigemptyset(&sa.sa_mask); | 817 | 216 | sigemptyset(&sa.sa_mask); | ||
| HITCBC | 818 | 216 | sa.sa_flags = 0; | 818 | 216 | sa.sa_flags = 0; | ||
| 819 | 819 | |||||||
| HITCBC | 820 | 216 | if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) | 820 | 216 | if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error) | ||
| HITCBC | 821 | 1 | first_error = make_error_code(std::errc::invalid_argument); | 821 | 1 | first_error = make_error_code(std::errc::invalid_argument); | ||
| 822 | 822 | |||||||
| 823 | // Clear stored flags | 823 | // Clear stored flags | |||||
| HITCBC | 824 | 216 | state->registered_flags[signal_number] = signal_set::none; | 824 | 216 | state->registered_flags[signal_number] = signal_set::none; | ||
| 825 | } | 825 | } | |||||
| 826 | 826 | |||||||
| HITCBC | 827 | 236 | impl.signals_ = reg->next_in_set; | 827 | 236 | impl.signals_ = reg->next_in_set; | ||
| 828 | 828 | |||||||
| HITCBC | 829 | 236 | if (registrations_[signal_number] == reg) | 829 | 236 | if (registrations_[signal_number] == reg) | ||
| HITCBC | 830 | 234 | registrations_[signal_number] = reg->next_in_table; | 830 | 234 | registrations_[signal_number] = reg->next_in_table; | ||
| HITCBC | 831 | 236 | if (reg->prev_in_table) | 831 | 236 | if (reg->prev_in_table) | ||
| HITCBC | 832 | 2 | reg->prev_in_table->next_in_table = reg->next_in_table; | 832 | 2 | reg->prev_in_table->next_in_table = reg->next_in_table; | ||
| HITCBC | 833 | 236 | if (reg->next_in_table) | 833 | 236 | if (reg->next_in_table) | ||
| HITCBC | 834 | 12 | reg->next_in_table->prev_in_table = reg->prev_in_table; | 834 | 12 | reg->next_in_table->prev_in_table = reg->prev_in_table; | ||
| 835 | 835 | |||||||
| HITCBC | 836 | 236 | --state->registration_count[signal_number]; | 836 | 236 | --state->registration_count[signal_number]; | ||
| HITCBC | 837 | 236 | --registration_count_[signal_number]; | 837 | 236 | --registration_count_[signal_number]; | ||
| 838 | 838 | |||||||
| HITCBC | 839 | 236 | delete reg; | 839 | 236 | delete reg; | ||
| HITCBC | 840 | 236 | } | 840 | 236 | } | ||
| 841 | 841 | |||||||
| HITCBC | 842 | 3490 | if (first_error) | 842 | 3490 | if (first_error) | ||
| HITCBC | 843 | 1 | return first_error; | 843 | 1 | return first_error; | ||
| HITCBC | 844 | 3489 | return {}; | 844 | 3489 | return {}; | ||
| HITCBC | 845 | 3490 | } | 845 | 3490 | } | ||
| 846 | 846 | |||||||
| 847 | inline void | 847 | inline void | |||||
| HITCBC | 848 | 203497 | posix_signal_service::cancel_wait(posix_signal& impl) noexcept | 848 | 203497 | posix_signal_service::cancel_wait(posix_signal& impl) noexcept | ||
| 849 | { | 849 | { | |||||
| HITCBC | 850 | 203497 | bool was_waiting = false; | 850 | 203497 | bool was_waiting = false; | ||
| HITCBC | 851 | 203497 | signal_op* op = nullptr; | 851 | 203497 | signal_op* op = nullptr; | ||
| 852 | 852 | |||||||
| 853 | { | 853 | { | |||||
| HITCBC | 854 | 203497 | std::lock_guard lock(mutex_); | 854 | 203497 | std::lock_guard lock(mutex_); | ||
| HITCBC | 855 | 203497 | impl.cancelled_ = true; | 855 | 203497 | impl.cancelled_ = true; | ||
| HITCBC | 856 | 203497 | if (impl.waiting_) | 856 | 203497 | if (impl.waiting_) | ||
| 857 | { | 857 | { | |||||
| HITCBC | 858 | 11 | was_waiting = true; | 858 | 11 | was_waiting = true; | ||
| HITCBC | 859 | 11 | impl.waiting_ = false; | 859 | 11 | impl.waiting_ = false; | ||
| HITCBC | 860 | 11 | op = &impl.pending_op_; | 860 | 11 | op = &impl.pending_op_; | ||
| HITCBC | 861 | 11 | sched_->cancel_posting_begin(); | 861 | 11 | sched_->cancel_posting_begin(); | ||
| 862 | } | 862 | } | |||||
| HITCBC | 863 | 203497 | } | 863 | 203497 | } | ||
| 864 | 864 | |||||||
| HITCBC | 865 | 203497 | if (was_waiting) | 865 | 203497 | if (was_waiting) | ||
| 866 | { | 866 | { | |||||
| HITCBC | 867 | 11 | if (op->ec_out) | 867 | 11 | if (op->ec_out) | ||
| HITCBC | 868 | 11 | *op->ec_out = make_error_code(capy::error::canceled); | 868 | 11 | *op->ec_out = make_error_code(capy::error::canceled); | ||
| HITCBC | 869 | 11 | if (op->signal_out) | 869 | 11 | if (op->signal_out) | ||
| HITCBC | 870 | 11 | *op->signal_out = 0; | 870 | 11 | *op->signal_out = 0; | ||
| HITCBC | 871 | 11 | op->d.post(*op->cont); | 871 | 11 | op->d.post(*op->cont); | ||
| HITCBC | 872 | 11 | sched_->work_finished(); | 872 | 11 | sched_->work_finished(); | ||
| HITCBC | 873 | 11 | sched_->cancel_posting_end(); | 873 | 11 | sched_->cancel_posting_end(); | ||
| 874 | } | 874 | } | |||||
| HITCBC | 875 | 203497 | } | 875 | 203497 | } | ||
| 876 | 876 | |||||||
| 877 | inline void | 877 | inline void | |||||
| HITCBC | 878 | 1014 | posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept | 878 | 961 | posix_signal_service::cancel_wait_token(posix_signal& impl) noexcept | ||
| 879 | { | 879 | { | |||||
| HITCBC | 880 | 1014 | bool was_waiting = false; | 880 | 961 | bool was_waiting = false; | ||
| HITCBC | 881 | 1014 | signal_op* op = nullptr; | 881 | 961 | signal_op* op = nullptr; | ||
| 882 | 882 | |||||||
| 883 | { | 883 | { | |||||
| HITCBC | 884 | 1014 | std::lock_guard lock(mutex_); | 884 | 961 | std::lock_guard lock(mutex_); | ||
| 885 | // Persist the request even when no wait is parked yet: wait() | 885 | // Persist the request even when no wait is parked yet: wait() | |||||
| 886 | // arms the callback before start_wait takes this lock, and | 886 | // arms the callback before start_wait takes this lock, and | |||||
| 887 | // start_wait consumes this flag. | 887 | // start_wait consumes this flag. | |||||
| HITCBC | 888 | 1014 | impl.token_cancelled_ = true; | 888 | 961 | impl.token_cancelled_ = true; | ||
| HITCBC | 889 | 1014 | if (impl.waiting_) | 889 | 961 | if (impl.waiting_) | ||
| 890 | { | 890 | { | |||||
| HITCBC | 891 | 665 | was_waiting = true; | 891 | 610 | was_waiting = true; | ||
| HITCBC | 892 | 665 | impl.waiting_ = false; | 892 | 610 | impl.waiting_ = false; | ||
| HITCBC | 893 | 665 | op = &impl.pending_op_; | 893 | 610 | op = &impl.pending_op_; | ||
| HITCBC | 894 | 665 | sched_->cancel_posting_begin(); | 894 | 610 | sched_->cancel_posting_begin(); | ||
| 895 | } | 895 | } | |||||
| HITCBC | 896 | 1014 | } | 896 | 961 | } | ||
| 897 | 897 | |||||||
| HITCBC | 898 | 1014 | if (was_waiting) | 898 | 961 | if (was_waiting) | ||
| 899 | { | 899 | { | |||||
| HITCBC | 900 | 665 | if (op->ec_out) | 900 | 610 | if (op->ec_out) | ||
| HITCBC | 901 | 665 | *op->ec_out = make_error_code(capy::error::canceled); | 901 | 610 | *op->ec_out = make_error_code(capy::error::canceled); | ||
| HITCBC | 902 | 665 | if (op->signal_out) | 902 | 610 | if (op->signal_out) | ||
| HITCBC | 903 | 665 | *op->signal_out = 0; | 903 | 610 | *op->signal_out = 0; | ||
| HITCBC | 904 | 665 | op->d.post(*op->cont); | 904 | 610 | op->d.post(*op->cont); | ||
| HITCBC | 905 | 665 | sched_->work_finished(); | 905 | 610 | sched_->work_finished(); | ||
| HITCBC | 906 | 665 | sched_->cancel_posting_end(); | 906 | 610 | sched_->cancel_posting_end(); | ||
| 907 | } | 907 | } | |||||
| HITCBC | 908 | 1014 | } | 908 | 961 | } | ||
| 909 | 909 | |||||||
| 910 | inline void | 910 | inline void | |||||
| HITCBC | 911 | 1014 | posix_signal::token_canceller::operator()() const noexcept | 911 | 961 | posix_signal::token_canceller::operator()() const noexcept | ||
| 912 | { | 912 | { | |||||
| HITCBC | 913 | 1014 | self->svc_.cancel_wait_token(*self); | 913 | 961 | self->svc_.cancel_wait_token(*self); | ||
| HITCBC | 914 | 1014 | } | 914 | 961 | } | ||
| 915 | 915 | |||||||
| 916 | inline void | 916 | inline void | |||||
| HITCBC | 917 | 205742 | posix_signal_service::start_wait(posix_signal& impl, signal_op* op) | 917 | 205897 | posix_signal_service::start_wait(posix_signal& impl, signal_op* op) | ||
| 918 | { | 918 | { | |||||
| 919 | { | 919 | { | |||||
| HITCBC | 920 | 205742 | std::lock_guard lock(mutex_); | 920 | 205897 | std::lock_guard lock(mutex_); | ||
| 921 | 921 | |||||||
| 922 | // Check if cancel() was called before this wait started | 922 | // Check if cancel() was called before this wait started | |||||
| HITCBC | 923 | 205742 | if (impl.cancelled_) | 923 | 205897 | if (impl.cancelled_) | ||
| 924 | { | 924 | { | |||||
| HITCBC | 925 | 200002 | impl.cancelled_ = false; | 925 | 200002 | impl.cancelled_ = false; | ||
| HITCBC | 926 | 200002 | if (op->ec_out) | 926 | 200002 | if (op->ec_out) | ||
| HITCBC | 927 | 200002 | *op->ec_out = make_error_code(capy::error::canceled); | 927 | 200002 | *op->ec_out = make_error_code(capy::error::canceled); | ||
| HITCBC | 928 | 200002 | if (op->signal_out) | 928 | 200002 | if (op->signal_out) | ||
| HITCBC | 929 | 200002 | *op->signal_out = 0; | 929 | 200002 | *op->signal_out = 0; | ||
| HITCBC | 930 | 200002 | op->d.post(*op->cont); | 930 | 200002 | op->d.post(*op->cont); | ||
| HITCBC | 931 | 200002 | return; | 931 | 200002 | return; | ||
| 932 | } | 932 | } | |||||
| 933 | 933 | |||||||
| 934 | // A stop request that arrived between wait() arming the callback | 934 | // A stop request that arrived between wait() arming the callback | |||||
| 935 | // and this lock: complete now rather than parking forever. | 935 | // and this lock: complete now rather than parking forever. | |||||
| HITCBC | 936 | 5740 | if (impl.token_cancelled_) | 936 | 5895 | if (impl.token_cancelled_) | ||
| 937 | { | 937 | { | |||||
| HITCBC | 938 | 63 | impl.token_cancelled_ = false; | 938 | 82 | impl.token_cancelled_ = false; | ||
| HITCBC | 939 | 63 | if (op->ec_out) | 939 | 82 | if (op->ec_out) | ||
| HITCBC | 940 | 63 | *op->ec_out = make_error_code(capy::error::canceled); | 940 | 82 | *op->ec_out = make_error_code(capy::error::canceled); | ||
| HITCBC | 941 | 63 | if (op->signal_out) | 941 | 82 | if (op->signal_out) | ||
| HITCBC | 942 | 63 | *op->signal_out = 0; | 942 | 82 | *op->signal_out = 0; | ||
| HITCBC | 943 | 63 | op->d.post(*op->cont); | 943 | 82 | op->d.post(*op->cont); | ||
| HITCBC | 944 | 63 | return; | 944 | 82 | return; | ||
| 945 | } | 945 | } | |||||
| 946 | 946 | |||||||
| 947 | // Check for queued signals first (signal arrived before wait started) | 947 | // Check for queued signals first (signal arrived before wait started) | |||||
| HITCBC | 948 | 5677 | signal_registration* reg = impl.signals_; | 948 | 5813 | signal_registration* reg = impl.signals_; | ||
| HITCBC | 949 | 10305 | while (reg) | 949 | 10440 | while (reg) | ||
| 950 | { | 950 | { | |||||
| HITCBC | 951 | 5681 | if (reg->undelivered > 0) | 951 | 5817 | if (reg->undelivered > 0) | ||
| 952 | { | 952 | { | |||||
| HITCBC | 953 | 1053 | --reg->undelivered; | 953 | 1190 | --reg->undelivered; | ||
| HITCBC | 954 | 1053 | op->signal_number = reg->signal_number; | 954 | 1190 | op->signal_number = reg->signal_number; | ||
| 955 | // svc=nullptr: no work_finished needed since we never called work_started | 955 | // svc=nullptr: no work_finished needed since we never called work_started | |||||
| HITCBC | 956 | 1053 | op->svc = nullptr; | 956 | 1190 | op->svc = nullptr; | ||
| 957 | // The caller's wait() holds the handle's reference. | 957 | // The caller's wait() holds the handle's reference. | |||||
| HITCBC | 958 | 1053 | op->keep = detail::object_ref(&impl); | 958 | 1190 | op->keep = detail::object_ref(&impl); | ||
| HITCBC | 959 | 1053 | sched_->post(op); | 959 | 1190 | sched_->post(op); | ||
| HITCBC | 960 | 1053 | return; | 960 | 1190 | return; | ||
| 961 | } | 961 | } | |||||
| HITCBC | 962 | 4628 | reg = reg->next_in_set; | 962 | 4627 | reg = reg->next_in_set; | ||
| 963 | } | 963 | } | |||||
| 964 | 964 | |||||||
| 965 | // No queued signals - wait for delivery | 965 | // No queued signals - wait for delivery | |||||
| HITCBC | 966 | 4624 | impl.waiting_ = true; | 966 | 4623 | impl.waiting_ = true; | ||
| 967 | // svc=this: signal_op::operator() will call work_finished() to balance this | 967 | // svc=this: signal_op::operator() will call work_finished() to balance this | |||||
| HITCBC | 968 | 4624 | op->svc = this; | 968 | 4623 | op->svc = this; | ||
| HITCBC | 969 | 4624 | sched_->work_started(); | 969 | 4623 | sched_->work_started(); | ||
| HITCBC | 970 | 205742 | } | 970 | 205897 | } | ||
| 971 | } | 971 | } | |||||
| 972 | 972 | |||||||
| 973 | inline void | 973 | inline void | |||||
| HITCBC | 974 | 1521 | posix_signal_service::deliver_signal(int signal_number) | 974 | 1521 | posix_signal_service::deliver_signal(int signal_number) | ||
| 975 | { | 975 | { | |||||
| HITCBC | 976 | 1521 | if (signal_number < 0 || signal_number >= max_signal_number) | 976 | 1521 | if (signal_number < 0 || signal_number >= max_signal_number) | ||
| MISUBC | 977 | ✗ | return; | 977 | ✗ | return; | ||
| 978 | 978 | |||||||
| 979 | posix_signal_detail::signal_state* state = | 979 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 980 | 1521 | posix_signal_detail::get_signal_state(); | 980 | 1521 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 981 | 1521 | std::lock_guard lock(state->mutex); | 981 | 1521 | std::lock_guard lock(state->mutex); | ||
| 982 | 982 | |||||||
| HITCBC | 983 | 1521 | posix_signal_service* service = state->service_list; | 983 | 1521 | posix_signal_service* service = state->service_list; | ||
| HITCBC | 984 | 3842 | while (service) | 984 | 3842 | while (service) | ||
| 985 | { | 985 | { | |||||
| HITCBC | 986 | 2321 | std::lock_guard svc_lock(service->mutex_); | 986 | 2321 | std::lock_guard svc_lock(service->mutex_); | ||
| 987 | 987 | |||||||
| HITCBC | 988 | 2321 | signal_registration* reg = service->registrations_[signal_number]; | 988 | 2321 | signal_registration* reg = service->registrations_[signal_number]; | ||
| HITCBC | 989 | 4646 | while (reg) | 989 | 4646 | while (reg) | ||
| 990 | { | 990 | { | |||||
| HITCBC | 991 | 2325 | posix_signal* impl = static_cast<posix_signal*>(reg->owner); | 991 | 2325 | posix_signal* impl = static_cast<posix_signal*>(reg->owner); | ||
| 992 | 992 | |||||||
| HITCBC | 993 | 2325 | if (impl->waiting_) | 993 | 2325 | if (impl->waiting_) | ||
| 994 | { | 994 | { | |||||
| 995 | // Reached through the registry, not a held reference. | 995 | // Reached through the registry, not a held reference. | |||||
| 996 | // A set whose count reached zero is being destroyed, | 996 | // A set whose count reached zero is being destroyed, | |||||
| 997 | // and that destruction completes its wait itself. | 997 | // and that destruction completes its wait itself. | |||||
| HITCBC | 998 | 738 | auto keep = detail::object_ref::try_from(impl); | 998 | 792 | auto keep = detail::object_ref::try_from(impl); | ||
| HITCBC | 999 | 738 | if (keep) | 999 | 792 | if (keep) | ||
| 1000 | { | 1000 | { | |||||
| HITCBC | 1001 | 738 | impl->waiting_ = false; | 1001 | 792 | impl->waiting_ = false; | ||
| HITCBC | 1002 | 738 | impl->pending_op_.signal_number = signal_number; | 1002 | 792 | impl->pending_op_.signal_number = signal_number; | ||
| HITCBC | 1003 | 738 | impl->pending_op_.keep = std::move(keep); | 1003 | 792 | impl->pending_op_.keep = std::move(keep); | ||
| HITCBC | 1004 | 738 | service->post(&impl->pending_op_); | 1004 | 792 | service->post(&impl->pending_op_); | ||
| 1005 | } | 1005 | } | |||||
| HITCBC | 1006 | 738 | } | 1006 | 792 | } | ||
| 1007 | else | 1007 | else | |||||
| 1008 | { | 1008 | { | |||||
| HITCBC | 1009 | 1587 | ++reg->undelivered; | 1009 | 1533 | ++reg->undelivered; | ||
| 1010 | } | 1010 | } | |||||
| 1011 | 1011 | |||||||
| HITCBC | 1012 | 2325 | reg = reg->next_in_table; | 1012 | 2325 | reg = reg->next_in_table; | ||
| 1013 | } | 1013 | } | |||||
| 1014 | 1014 | |||||||
| HITCBC | 1015 | 2321 | service = service->next_; | 1015 | 2321 | service = service->next_; | ||
| HITCBC | 1016 | 2321 | } | 1016 | 2321 | } | ||
| HITCBC | 1017 | 1521 | } | 1017 | 1521 | } | ||
| 1018 | 1018 | |||||||
| 1019 | inline void | 1019 | inline void | |||||
| 1020 | posix_signal_service::work_started() noexcept | 1020 | posix_signal_service::work_started() noexcept | |||||
| 1021 | { | 1021 | { | |||||
| 1022 | sched_->work_started(); | 1022 | sched_->work_started(); | |||||
| 1023 | } | 1023 | } | |||||
| 1024 | 1024 | |||||||
| 1025 | inline void | 1025 | inline void | |||||
| HITCBC | 1026 | 738 | posix_signal_service::work_finished() noexcept | 1026 | 792 | posix_signal_service::work_finished() noexcept | ||
| 1027 | { | 1027 | { | |||||
| HITCBC | 1028 | 738 | sched_->work_finished(); | 1028 | 792 | sched_->work_finished(); | ||
| HITCBC | 1029 | 738 | } | 1029 | 792 | } | ||
| 1030 | 1030 | |||||||
| 1031 | inline void | 1031 | inline void | |||||
| HITCBC | 1032 | 738 | posix_signal_service::post(signal_op* op) | 1032 | 792 | posix_signal_service::post(signal_op* op) | ||
| 1033 | { | 1033 | { | |||||
| HITCBC | 1034 | 738 | sched_->post(op); | 1034 | 792 | sched_->post(op); | ||
| HITCBC | 1035 | 738 | } | 1035 | 792 | } | ||
| 1036 | 1036 | |||||||
| 1037 | inline void | 1037 | inline void | |||||
| HITCBC | 1038 | 583 | posix_signal_service::add_service(posix_signal_service* service) | 1038 | 583 | posix_signal_service::add_service(posix_signal_service* service) | ||
| 1039 | { | 1039 | { | |||||
| 1040 | posix_signal_detail::signal_state* state = | 1040 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 1041 | 583 | posix_signal_detail::get_signal_state(); | 1041 | 583 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 1042 | 583 | std::lock_guard lock(state->mutex); | 1042 | 583 | std::lock_guard lock(state->mutex); | ||
| 1043 | 1043 | |||||||
| HITCBC | 1044 | 583 | service->next_ = state->service_list; | 1044 | 583 | service->next_ = state->service_list; | ||
| HITCBC | 1045 | 583 | service->prev_ = nullptr; | 1045 | 583 | service->prev_ = nullptr; | ||
| HITCBC | 1046 | 583 | if (state->service_list) | 1046 | 583 | if (state->service_list) | ||
| HITCBC | 1047 | 10 | state->service_list->prev_ = service; | 1047 | 10 | state->service_list->prev_ = service; | ||
| HITCBC | 1048 | 583 | state->service_list = service; | 1048 | 583 | state->service_list = service; | ||
| HITCBC | 1049 | 583 | } | 1049 | 583 | } | ||
| 1050 | 1050 | |||||||
| 1051 | inline void | 1051 | inline void | |||||
| HITCBC | 1052 | 583 | posix_signal_service::remove_service(posix_signal_service* service) | 1052 | 583 | posix_signal_service::remove_service(posix_signal_service* service) | ||
| 1053 | { | 1053 | { | |||||
| 1054 | posix_signal_detail::signal_state* state = | 1054 | posix_signal_detail::signal_state* state = | |||||
| HITCBC | 1055 | 583 | posix_signal_detail::get_signal_state(); | 1055 | 583 | posix_signal_detail::get_signal_state(); | ||
| HITCBC | 1056 | 583 | std::lock_guard lock(state->mutex); | 1056 | 583 | std::lock_guard lock(state->mutex); | ||
| 1057 | 1057 | |||||||
| HITCBC | 1058 | 583 | if (service->next_ || service->prev_ || state->service_list == service) | 1058 | 583 | if (service->next_ || service->prev_ || state->service_list == service) | ||
| 1059 | { | 1059 | { | |||||
| HITCBC | 1060 | 583 | if (state->service_list == service) | 1060 | 583 | if (state->service_list == service) | ||
| HITCBC | 1061 | 581 | state->service_list = service->next_; | 1061 | 581 | state->service_list = service->next_; | ||
| HITCBC | 1062 | 583 | if (service->prev_) | 1062 | 583 | if (service->prev_) | ||
| HITCBC | 1063 | 2 | service->prev_->next_ = service->next_; | 1063 | 2 | service->prev_->next_ = service->next_; | ||
| HITCBC | 1064 | 583 | if (service->next_) | 1064 | 583 | if (service->next_) | ||
| HITCBC | 1065 | 8 | service->next_->prev_ = service->prev_; | 1065 | 8 | service->next_->prev_ = service->prev_; | ||
| HITCBC | 1066 | 583 | service->next_ = nullptr; | 1066 | 583 | service->next_ = nullptr; | ||
| HITCBC | 1067 | 583 | service->prev_ = nullptr; | 1067 | 583 | service->prev_ = nullptr; | ||
| 1068 | } | 1068 | } | |||||
| HITCBC | 1069 | 583 | } | 1069 | 583 | } | ||
| 1070 | 1070 | |||||||
| 1071 | } // namespace detail | 1071 | } // namespace detail | |||||
| 1072 | } // namespace boost::corosio | 1072 | } // namespace boost::corosio | |||||
| 1073 | 1073 | |||||||
| 1074 | #endif // BOOST_COROSIO_POSIX | 1074 | #endif // BOOST_COROSIO_POSIX | |||||
| 1075 | 1075 | |||||||
| 1076 | #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP | 1076 | #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP | |||||