1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
12  

12  

13  
#include <boost/corosio/detail/platform.hpp>
13  
#include <boost/corosio/detail/platform.hpp>
14  

14  

15  
#if BOOST_COROSIO_POSIX
15  
#if BOOST_COROSIO_POSIX
16  

16  

17  
#include <boost/corosio/native/detail/posix/posix_signal.hpp>
17  
#include <boost/corosio/native/detail/posix/posix_signal.hpp>
18  

18  

19  
#include <boost/corosio/detail/config.hpp>
19  
#include <boost/corosio/detail/config.hpp>
20  
#include <boost/capy/ex/execution_context.hpp>
20  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/corosio/detail/scheduler.hpp>
21  
#include <boost/corosio/detail/scheduler.hpp>
22  
#include <boost/capy/error.hpp>
22  
#include <boost/capy/error.hpp>
23  

23  

24  
#include <mutex>
24  
#include <mutex>
25  

25  

26  
#include <signal.h>
26  
#include <signal.h>
27  

27  

28  
/*
28  
/*
29  
    POSIX Signal Service
29  
    POSIX Signal Service
30  
    ====================
30  
    ====================
31  

31  

32  
    Concrete signal service implementation for POSIX backends. Manages signal
32  
    Concrete signal service implementation for POSIX backends. Manages signal
33  
    registrations via sigaction() and dispatches completions through the
33  
    registrations via sigaction() and dispatches completions through the
34  
    scheduler. One instance per execution_context, created by
34  
    scheduler. One instance per execution_context, created by
35  
    get_signal_service().
35  
    get_signal_service().
36  

36  

37  
    See the block comment further down for the full architecture overview.
37  
    See the block comment further down for the full architecture overview.
38  
*/
38  
*/
39  

39  

40  
/*
40  
/*
41  
    POSIX Signal Implementation
41  
    POSIX Signal Implementation
42  
    ===========================
42  
    ===========================
43  

43  

44  
    This file implements signal handling for POSIX systems using sigaction().
44  
    This file implements signal handling for POSIX systems using sigaction().
45  
    The implementation supports signal flags (SA_RESTART, etc.) and integrates
45  
    The implementation supports signal flags (SA_RESTART, etc.) and integrates
46  
    with any POSIX-compatible scheduler via the abstract scheduler interface.
46  
    with any POSIX-compatible scheduler via the abstract scheduler interface.
47  

47  

48  
    Architecture Overview
48  
    Architecture Overview
49  
    ---------------------
49  
    ---------------------
50  

50  

51  
    Three layers manage signal registrations:
51  
    Three layers manage signal registrations:
52  

52  

53  
    1. signal_state (global singleton)
53  
    1. signal_state (global singleton)
54  
       - Tracks the global service list and per-signal registration counts
54  
       - Tracks the global service list and per-signal registration counts
55  
       - Stores the flags used for first registration of each signal (for
55  
       - Stores the flags used for first registration of each signal (for
56  
         conflict detection when multiple signal_sets register same signal)
56  
         conflict detection when multiple signal_sets register same signal)
57  
       - Owns the mutex that protects signal handler installation/removal
57  
       - Owns the mutex that protects signal handler installation/removal
58  

58  

59  
    2. posix_signal_service (one per execution_context)
59  
    2. posix_signal_service (one per execution_context)
60  
       - Maintains registrations_[] table indexed by signal number
60  
       - Maintains registrations_[] table indexed by signal number
61  
       - Each slot is a doubly-linked list of signal_registrations for that signal
61  
       - Each slot is a doubly-linked list of signal_registrations for that signal
62  
       - Also maintains impl_list_ of all posix_signal objects it owns
62  
       - Also maintains impl_list_ of all posix_signal objects it owns
63  

63  

64  
    3. posix_signal (one per signal_set)
64  
    3. posix_signal (one per signal_set)
65  
       - Owns a singly-linked list (sorted by signal number) of signal_registrations
65  
       - Owns a singly-linked list (sorted by signal number) of signal_registrations
66  
       - Contains the pending_op_ used for wait operations
66  
       - Contains the pending_op_ used for wait operations
67  

67  

68  
    Signal Delivery Flow
68  
    Signal Delivery Flow
69  
    --------------------
69  
    --------------------
70  

70  

71  
    1. Signal arrives -> corosio_posix_signal_handler() (must be async-signal-safe)
71  
    1. Signal arrives -> corosio_posix_signal_handler() (must be async-signal-safe)
72  
       -> deliver_signal()
72  
       -> deliver_signal()
73  

73  

74  
    2. deliver_signal() iterates all posix_signal_service services:
74  
    2. deliver_signal() iterates all posix_signal_service services:
75  
       - If a signal_set is waiting (impl->waiting_ == true), post the signal_op
75  
       - If a signal_set is waiting (impl->waiting_ == true), post the signal_op
76  
         to the scheduler for immediate completion
76  
         to the scheduler for immediate completion
77  
       - Otherwise, increment reg->undelivered to queue the signal
77  
       - Otherwise, increment reg->undelivered to queue the signal
78  

78  

79  
    3. When wait() is called via start_wait():
79  
    3. When wait() is called via start_wait():
80  
       - First check for queued signals (undelivered > 0); if found, post
80  
       - First check for queued signals (undelivered > 0); if found, post
81  
         immediate completion without blocking
81  
         immediate completion without blocking
82  
       - Otherwise, set waiting_ = true and call work_started() to keep
82  
       - Otherwise, set waiting_ = true and call work_started() to keep
83  
         the io_context alive
83  
         the io_context alive
84  

84  

85  
    Locking Protocol
85  
    Locking Protocol
86  
    ----------------
86  
    ----------------
87  

87  

88  
    Two mutex levels exist (MUST acquire in this order to avoid deadlock):
88  
    Two mutex levels exist (MUST acquire in this order to avoid deadlock):
89  
      1. signal_state::mutex - protects handler registration and service list
89  
      1. signal_state::mutex - protects handler registration and service list
90  
      2. posix_signal_service::mutex_ - protects per-service registration tables
90  
      2. posix_signal_service::mutex_ - protects per-service registration tables
91  

91  

92  
    Async-Signal-Safety Limitation
92  
    Async-Signal-Safety Limitation
93  
    ------------------------------
93  
    ------------------------------
94  

94  

95  
    IMPORTANT: deliver_signal() is called from signal handler context and
95  
    IMPORTANT: deliver_signal() is called from signal handler context and
96  
    acquires mutexes. This is NOT strictly async-signal-safe per POSIX.
96  
    acquires mutexes. This is NOT strictly async-signal-safe per POSIX.
97  
    The limitation:
97  
    The limitation:
98  
      - If a signal arrives while another thread holds state->mutex or
98  
      - If a signal arrives while another thread holds state->mutex or
99  
        service->mutex_, and that same thread receives the signal, a
99  
        service->mutex_, and that same thread receives the signal, a
100  
        deadlock can occur (self-deadlock on non-recursive mutex).
100  
        deadlock can occur (self-deadlock on non-recursive mutex).
101  

101  

102  
    This design trades strict async-signal-safety for implementation simplicity.
102  
    This design trades strict async-signal-safety for implementation simplicity.
103  
    In practice, deadlocks are rare because:
103  
    In practice, deadlocks are rare because:
104  
      - Mutexes are held only briefly during registration changes
104  
      - Mutexes are held only briefly during registration changes
105  
      - Most programs don't modify signal sets while signals are expected
105  
      - Most programs don't modify signal sets while signals are expected
106  
      - The window for signal arrival during mutex hold is small
106  
      - The window for signal arrival during mutex hold is small
107  

107  

108  
    A fully async-signal-safe implementation would require lock-free data
108  
    A fully async-signal-safe implementation would require lock-free data
109  
    structures and atomic operations throughout, significantly increasing
109  
    structures and atomic operations throughout, significantly increasing
110  
    complexity.
110  
    complexity.
111  

111  

112  
    Flag Handling
112  
    Flag Handling
113  
    -------------
113  
    -------------
114  

114  

115  
    - Flags are abstract values in the public API (signal_set::flags_t)
115  
    - Flags are abstract values in the public API (signal_set::flags_t)
116  
    - flags_supported() validates that requested flags are available on
116  
    - flags_supported() validates that requested flags are available on
117  
      this platform; returns false if SA_NOCLDWAIT is unavailable and
117  
      this platform; returns false if SA_NOCLDWAIT is unavailable and
118  
      no_child_wait is requested
118  
      no_child_wait is requested
119  
    - to_sigaction_flags() maps validated flags to actual SA_* constants
119  
    - to_sigaction_flags() maps validated flags to actual SA_* constants
120  
    - First registration of a signal establishes the flags; subsequent
120  
    - First registration of a signal establishes the flags; subsequent
121  
      registrations must be compatible (same flags or dont_care)
121  
      registrations must be compatible (same flags or dont_care)
122  
    - Requesting unavailable flags returns operation_not_supported
122  
    - Requesting unavailable flags returns operation_not_supported
123  

123  

124  
    Work Tracking
124  
    Work Tracking
125  
    -------------
125  
    -------------
126  

126  

127  
    When waiting for a signal:
127  
    When waiting for a signal:
128  
      - start_wait() calls sched_->work_started() to prevent io_context::run()
128  
      - start_wait() calls sched_->work_started() to prevent io_context::run()
129  
        from returning while we wait
129  
        from returning while we wait
130  
      - signal_op::svc is set to point to the service
130  
      - signal_op::svc is set to point to the service
131  
      - signal_op::operator()() calls work_finished() after resuming the coroutine
131  
      - signal_op::operator()() calls work_finished() after resuming the coroutine
132  

132  

133  
    If a signal was already queued (undelivered > 0), no work tracking is needed
133  
    If a signal was already queued (undelivered > 0), no work tracking is needed
134  
    because completion is posted immediately.
134  
    because completion is posted immediately.
135  
*/
135  
*/
136  

136  

137  
namespace boost::corosio {
137  
namespace boost::corosio {
138  

138  

139  
namespace detail {
139  
namespace detail {
140  

140  

141  
/** Signal service for POSIX backends.
141  
/** Signal service for POSIX backends.
142  

142  

143  
    Manages signal registrations via sigaction() and dispatches signal
143  
    Manages signal registrations via sigaction() and dispatches signal
144  
    completions through the scheduler. One instance per execution_context.
144  
    completions through the scheduler. One instance per execution_context.
145  
*/
145  
*/
146  
class BOOST_COROSIO_DECL posix_signal_service final
146  
class BOOST_COROSIO_DECL posix_signal_service final
147  
    : public capy::execution_context::service
147  
    : public capy::execution_context::service
148  
    , public io_object::io_service
148  
    , public io_object::io_service
149  
{
149  
{
150  
public:
150  
public:
151  
    using key_type = posix_signal_service;
151  
    using key_type = posix_signal_service;
152  

152  

153  
    posix_signal_service(capy::execution_context& ctx, scheduler& sched);
153  
    posix_signal_service(capy::execution_context& ctx, scheduler& sched);
154  
    ~posix_signal_service() override;
154  
    ~posix_signal_service() override;
155  

155  

156  
    posix_signal_service(posix_signal_service const&)            = delete;
156  
    posix_signal_service(posix_signal_service const&)            = delete;
157  
    posix_signal_service& operator=(posix_signal_service const&) = delete;
157  
    posix_signal_service& operator=(posix_signal_service const&) = delete;
158  

158  

159  
    io_object::implementation* construct() override;
159  
    io_object::implementation* construct() override;
160  

160  

161  
    void destroy(io_object::implementation* p) override
161  
    void destroy(io_object::implementation* p) override
162  
    {
162  
    {
163  
        auto& impl              = static_cast<posix_signal&>(*p);
163  
        auto& impl              = static_cast<posix_signal&>(*p);
164  
        [[maybe_unused]] auto n = impl.clear();
164  
        [[maybe_unused]] auto n = impl.clear();
165  
        impl.cancel();
165  
        impl.cancel();
166  
        destroy_impl(impl);
166  
        destroy_impl(impl);
167  
    }
167  
    }
168  

168  

169  
    void shutdown() override;
169  
    void shutdown() override;
170  

170  

171  
    void destroy_impl(posix_signal& impl);
171  
    void destroy_impl(posix_signal& impl);
172  

172  

173  
    std::error_code add_signal(
173  
    std::error_code add_signal(
174  
        posix_signal& impl, int signal_number, signal_set::flags_t flags);
174  
        posix_signal& impl, int signal_number, signal_set::flags_t flags);
175  

175  

176  
    std::error_code remove_signal(posix_signal& impl, int signal_number);
176  
    std::error_code remove_signal(posix_signal& impl, int signal_number);
177  

177  

178  
    std::error_code clear_signals(posix_signal& impl);
178  
    std::error_code clear_signals(posix_signal& impl);
179  

179  

180  
    void cancel_wait(posix_signal& impl);
180  
    void cancel_wait(posix_signal& impl);
181  
    void start_wait(posix_signal& impl, signal_op* op);
181  
    void start_wait(posix_signal& impl, signal_op* op);
182  

182  

183  
    static void deliver_signal(int signal_number);
183  
    static void deliver_signal(int signal_number);
184  

184  

185  
    void work_started() noexcept;
185  
    void work_started() noexcept;
186  
    void work_finished() noexcept;
186  
    void work_finished() noexcept;
187  
    void post(signal_op* op);
187  
    void post(signal_op* op);
188  

188  

189  
private:
189  
private:
190  
    static void add_service(posix_signal_service* service);
190  
    static void add_service(posix_signal_service* service);
191  
    static void remove_service(posix_signal_service* service);
191  
    static void remove_service(posix_signal_service* service);
192  

192  

193  
    scheduler* sched_;
193  
    scheduler* sched_;
194  
    std::mutex mutex_;
194  
    std::mutex mutex_;
195  
    intrusive_list<posix_signal> impl_list_;
195  
    intrusive_list<posix_signal> impl_list_;
196  

196  

197  
    // Per-signal registration table
197  
    // Per-signal registration table
198  
    signal_registration* registrations_[max_signal_number];
198  
    signal_registration* registrations_[max_signal_number];
199  

199  

200  
    // Registration counts for each signal
200  
    // Registration counts for each signal
201  
    std::size_t registration_count_[max_signal_number];
201  
    std::size_t registration_count_[max_signal_number];
202  

202  

203  
    // Linked list of all posix_signal_service services for signal delivery
203  
    // Linked list of all posix_signal_service services for signal delivery
204  
    posix_signal_service* next_ = nullptr;
204  
    posix_signal_service* next_ = nullptr;
205  
    posix_signal_service* prev_ = nullptr;
205  
    posix_signal_service* prev_ = nullptr;
206  
};
206  
};
207  

207  

208  
/** Get or create the signal service for the given context.
208  
/** Get or create the signal service for the given context.
209  

209  

210  
    This function is called by the concrete scheduler during initialization
210  
    This function is called by the concrete scheduler during initialization
211  
    to create the signal service with a reference to itself.
211  
    to create the signal service with a reference to itself.
212  

212  

213  
    @param ctx Reference to the owning execution_context.
213  
    @param ctx Reference to the owning execution_context.
214  
    @param sched Reference to the scheduler for posting completions.
214  
    @param sched Reference to the scheduler for posting completions.
215  
    @return Reference to the signal service.
215  
    @return Reference to the signal service.
216  
*/
216  
*/
217  
posix_signal_service&
217  
posix_signal_service&
218  
get_signal_service(capy::execution_context& ctx, scheduler& sched);
218  
get_signal_service(capy::execution_context& ctx, scheduler& sched);
219  

219  

220  
} // namespace detail
220  
} // namespace detail
221  

221  

222  
} // namespace boost::corosio
222  
} // namespace boost::corosio
223  

223  

224  
// ---------------------------------------------------------------------------
224  
// ---------------------------------------------------------------------------
225  
// Inline implementation
225  
// Inline implementation
226  
// ---------------------------------------------------------------------------
226  
// ---------------------------------------------------------------------------
227  

227  

228  
namespace boost::corosio {
228  
namespace boost::corosio {
229  

229  

230  
namespace detail {
230  
namespace detail {
231  

231  

232  
namespace posix_signal_detail {
232  
namespace posix_signal_detail {
233  

233  

234  
struct signal_state
234  
struct signal_state
235  
{
235  
{
236  
    std::mutex mutex;
236  
    std::mutex mutex;
237  
    posix_signal_service* service_list                      = nullptr;
237  
    posix_signal_service* service_list                      = nullptr;
238  
    std::size_t registration_count[max_signal_number]       = {};
238  
    std::size_t registration_count[max_signal_number]       = {};
239  
    signal_set::flags_t registered_flags[max_signal_number] = {};
239  
    signal_set::flags_t registered_flags[max_signal_number] = {};
240  
};
240  
};
241  

241  

242  
BOOST_COROSIO_DECL signal_state* get_signal_state();
242  
BOOST_COROSIO_DECL signal_state* get_signal_state();
243  

243  

244  
// Check if requested flags are supported on this platform.
244  
// Check if requested flags are supported on this platform.
245  
// Returns true if all flags are supported, false otherwise.
245  
// Returns true if all flags are supported, false otherwise.
246  
inline bool
246  
inline bool
247  
flags_supported([[maybe_unused]] signal_set::flags_t flags)
247  
flags_supported([[maybe_unused]] signal_set::flags_t flags)
248  
{
248  
{
249  
#ifndef SA_NOCLDWAIT
249  
#ifndef SA_NOCLDWAIT
250  
    if (flags & signal_set::no_child_wait)
250  
    if (flags & signal_set::no_child_wait)
251  
        return false;
251  
        return false;
252  
#endif
252  
#endif
253  
    return true;
253  
    return true;
254  
}
254  
}
255  

255  

256  
// Map abstract flags to sigaction() flags.
256  
// Map abstract flags to sigaction() flags.
257  
// Caller must ensure flags_supported() returns true first.
257  
// Caller must ensure flags_supported() returns true first.
258  
inline int
258  
inline int
259  
to_sigaction_flags(signal_set::flags_t flags)
259  
to_sigaction_flags(signal_set::flags_t flags)
260  
{
260  
{
261  
    int sa_flags = 0;
261  
    int sa_flags = 0;
262  
    if (flags & signal_set::restart)
262  
    if (flags & signal_set::restart)
263  
        sa_flags |= SA_RESTART;
263  
        sa_flags |= SA_RESTART;
264  
    if (flags & signal_set::no_child_stop)
264  
    if (flags & signal_set::no_child_stop)
265  
        sa_flags |= SA_NOCLDSTOP;
265  
        sa_flags |= SA_NOCLDSTOP;
266  
#ifdef SA_NOCLDWAIT
266  
#ifdef SA_NOCLDWAIT
267  
    if (flags & signal_set::no_child_wait)
267  
    if (flags & signal_set::no_child_wait)
268  
        sa_flags |= SA_NOCLDWAIT;
268  
        sa_flags |= SA_NOCLDWAIT;
269  
#endif
269  
#endif
270  
    if (flags & signal_set::no_defer)
270  
    if (flags & signal_set::no_defer)
271  
        sa_flags |= SA_NODEFER;
271  
        sa_flags |= SA_NODEFER;
272  
    if (flags & signal_set::reset_handler)
272  
    if (flags & signal_set::reset_handler)
273  
        sa_flags |= SA_RESETHAND;
273  
        sa_flags |= SA_RESETHAND;
274  
    return sa_flags;
274  
    return sa_flags;
275  
}
275  
}
276  

276  

277  
// Check if two flag values are compatible
277  
// Check if two flag values are compatible
278  
inline bool
278  
inline bool
279  
flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
279  
flags_compatible(signal_set::flags_t existing, signal_set::flags_t requested)
280  
{
280  
{
281  
    // dont_care is always compatible
281  
    // dont_care is always compatible
282  
    if ((existing & signal_set::dont_care) ||
282  
    if ((existing & signal_set::dont_care) ||
283  
        (requested & signal_set::dont_care))
283  
        (requested & signal_set::dont_care))
284  
        return true;
284  
        return true;
285  

285  

286  
    // Mask out dont_care bit for comparison
286  
    // Mask out dont_care bit for comparison
287  
    constexpr auto mask = ~signal_set::dont_care;
287  
    constexpr auto mask = ~signal_set::dont_care;
288  
    return (existing & mask) == (requested & mask);
288  
    return (existing & mask) == (requested & mask);
289  
}
289  
}
290  

290  

291  
// C signal handler - must be async-signal-safe
291  
// C signal handler - must be async-signal-safe
292  
inline void
292  
inline void
293  
corosio_posix_signal_handler(int signal_number)
293  
corosio_posix_signal_handler(int signal_number)
294  
{
294  
{
295  
    posix_signal_service::deliver_signal(signal_number);
295  
    posix_signal_service::deliver_signal(signal_number);
296  
    // Note: With sigaction(), the handler persists automatically
296  
    // Note: With sigaction(), the handler persists automatically
297  
    // (unlike some signal() implementations that reset to SIG_DFL)
297  
    // (unlike some signal() implementations that reset to SIG_DFL)
298  
}
298  
}
299  

299  

300  
} // namespace posix_signal_detail
300  
} // namespace posix_signal_detail
301  

301  

302  
// signal_op implementation
302  
// signal_op implementation
303  

303  

304  
inline void
304  
inline void
305  
signal_op::operator()()
305  
signal_op::operator()()
306  
{
306  
{
307  
    if (ec_out)
307  
    if (ec_out)
308  
        *ec_out = {};
308  
        *ec_out = {};
309  
    if (signal_out)
309  
    if (signal_out)
310  
        *signal_out = signal_number;
310  
        *signal_out = signal_number;
311  

311  

312  
    // Capture svc before resuming (coro may destroy us)
312  
    // Capture svc before resuming (coro may destroy us)
313  
    auto* service = svc;
313  
    auto* service = svc;
314  
    svc           = nullptr;
314  
    svc           = nullptr;
315  

315  

316  
    d.post(h);
316  
    d.post(h);
317  

317  

318  
    // Balance the work_started() from start_wait
318  
    // Balance the work_started() from start_wait
319  
    if (service)
319  
    if (service)
320  
        service->work_finished();
320  
        service->work_finished();
321  
}
321  
}
322  

322  

323  
inline void
323  
inline void
324  
signal_op::destroy()
324  
signal_op::destroy()
325  
{
325  
{
326  
    // No-op: signal_op is embedded in posix_signal
326  
    // No-op: signal_op is embedded in posix_signal
327  
}
327  
}
328  

328  

329  
// posix_signal implementation
329  
// posix_signal implementation
330  

330  

331  
inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
331  
inline posix_signal::posix_signal(posix_signal_service& svc) noexcept
332  
    : svc_(svc)
332  
    : svc_(svc)
333  
{
333  
{
334  
}
334  
}
335  

335  

336  
inline std::coroutine_handle<>
336  
inline std::coroutine_handle<>
337  
posix_signal::wait(
337  
posix_signal::wait(
338  
    std::coroutine_handle<> h,
338  
    std::coroutine_handle<> h,
339  
    capy::executor_ref d,
339  
    capy::executor_ref d,
340  
    std::stop_token token,
340  
    std::stop_token token,
341  
    std::error_code* ec,
341  
    std::error_code* ec,
342  
    int* signal_out)
342  
    int* signal_out)
343  
{
343  
{
344  
    pending_op_.h             = h;
344  
    pending_op_.h             = h;
345  
    pending_op_.d             = d;
345  
    pending_op_.d             = d;
346  
    pending_op_.ec_out        = ec;
346  
    pending_op_.ec_out        = ec;
347  
    pending_op_.signal_out    = signal_out;
347  
    pending_op_.signal_out    = signal_out;
348  
    pending_op_.signal_number = 0;
348  
    pending_op_.signal_number = 0;
349  

349  

350  
    if (token.stop_requested())
350  
    if (token.stop_requested())
351  
    {
351  
    {
352  
        if (ec)
352  
        if (ec)
353  
            *ec = make_error_code(capy::error::canceled);
353  
            *ec = make_error_code(capy::error::canceled);
354  
        if (signal_out)
354  
        if (signal_out)
355  
            *signal_out = 0;
355  
            *signal_out = 0;
356  
        d.post(h);
356  
        d.post(h);
357  
        // completion is always posted to scheduler queue, never inline.
357  
        // completion is always posted to scheduler queue, never inline.
358  
        return std::noop_coroutine();
358  
        return std::noop_coroutine();
359  
    }
359  
    }
360  

360  

361  
    svc_.start_wait(*this, &pending_op_);
361  
    svc_.start_wait(*this, &pending_op_);
362  
    // completion is always posted to scheduler queue, never inline.
362  
    // completion is always posted to scheduler queue, never inline.
363  
    return std::noop_coroutine();
363  
    return std::noop_coroutine();
364  
}
364  
}
365  

365  

366  
inline std::error_code
366  
inline std::error_code
367  
posix_signal::add(int signal_number, signal_set::flags_t flags)
367  
posix_signal::add(int signal_number, signal_set::flags_t flags)
368  
{
368  
{
369  
    return svc_.add_signal(*this, signal_number, flags);
369  
    return svc_.add_signal(*this, signal_number, flags);
370  
}
370  
}
371  

371  

372  
inline std::error_code
372  
inline std::error_code
373  
posix_signal::remove(int signal_number)
373  
posix_signal::remove(int signal_number)
374  
{
374  
{
375  
    return svc_.remove_signal(*this, signal_number);
375  
    return svc_.remove_signal(*this, signal_number);
376  
}
376  
}
377  

377  

378  
inline std::error_code
378  
inline std::error_code
379  
posix_signal::clear()
379  
posix_signal::clear()
380  
{
380  
{
381  
    return svc_.clear_signals(*this);
381  
    return svc_.clear_signals(*this);
382  
}
382  
}
383  

383  

384  
inline void
384  
inline void
385  
posix_signal::cancel()
385  
posix_signal::cancel()
386  
{
386  
{
387  
    svc_.cancel_wait(*this);
387  
    svc_.cancel_wait(*this);
388  
}
388  
}
389  

389  

390  
// posix_signal_service implementation
390  
// posix_signal_service implementation
391  

391  

392  
inline posix_signal_service::posix_signal_service(
392  
inline posix_signal_service::posix_signal_service(
393  
    capy::execution_context&, scheduler& sched)
393  
    capy::execution_context&, scheduler& sched)
394  
    : sched_(&sched)
394  
    : sched_(&sched)
395  
{
395  
{
396  
    for (int i = 0; i < max_signal_number; ++i)
396  
    for (int i = 0; i < max_signal_number; ++i)
397  
    {
397  
    {
398  
        registrations_[i]      = nullptr;
398  
        registrations_[i]      = nullptr;
399  
        registration_count_[i] = 0;
399  
        registration_count_[i] = 0;
400  
    }
400  
    }
401  
    add_service(this);
401  
    add_service(this);
402  
}
402  
}
403  

403  

404  
inline posix_signal_service::~posix_signal_service()
404  
inline posix_signal_service::~posix_signal_service()
405  
{
405  
{
406  
    remove_service(this);
406  
    remove_service(this);
407  
}
407  
}
408  

408  

409  
inline void
409  
inline void
410  
posix_signal_service::shutdown()
410  
posix_signal_service::shutdown()
411  
{
411  
{
412  
    std::lock_guard lock(mutex_);
412  
    std::lock_guard lock(mutex_);
413  

413  

414  
    for (auto* impl = impl_list_.pop_front(); impl != nullptr;
414  
    for (auto* impl = impl_list_.pop_front(); impl != nullptr;
415  
         impl       = impl_list_.pop_front())
415  
         impl       = impl_list_.pop_front())
416  
    {
416  
    {
417  
        while (auto* reg = impl->signals_)
417  
        while (auto* reg = impl->signals_)
418  
        {
418  
        {
419  
            impl->signals_ = reg->next_in_set;
419  
            impl->signals_ = reg->next_in_set;
420  
            delete reg;
420  
            delete reg;
421  
        }
421  
        }
422  
        delete impl;
422  
        delete impl;
423  
    }
423  
    }
424  
}
424  
}
425  

425  

426  
inline io_object::implementation*
426  
inline io_object::implementation*
427  
posix_signal_service::construct()
427  
posix_signal_service::construct()
428  
{
428  
{
429  
    auto* impl = new posix_signal(*this);
429  
    auto* impl = new posix_signal(*this);
430  

430  

431  
    {
431  
    {
432  
        std::lock_guard lock(mutex_);
432  
        std::lock_guard lock(mutex_);
433  
        impl_list_.push_back(impl);
433  
        impl_list_.push_back(impl);
434  
    }
434  
    }
435  

435  

436  
    return impl;
436  
    return impl;
437  
}
437  
}
438  

438  

439  
inline void
439  
inline void
440  
posix_signal_service::destroy_impl(posix_signal& impl)
440  
posix_signal_service::destroy_impl(posix_signal& impl)
441  
{
441  
{
442  
    {
442  
    {
443  
        std::lock_guard lock(mutex_);
443  
        std::lock_guard lock(mutex_);
444  
        impl_list_.remove(&impl);
444  
        impl_list_.remove(&impl);
445  
    }
445  
    }
446  

446  

447  
    delete &impl;
447  
    delete &impl;
448  
}
448  
}
449  

449  

450  
inline std::error_code
450  
inline std::error_code
451  
posix_signal_service::add_signal(
451  
posix_signal_service::add_signal(
452  
    posix_signal& impl, int signal_number, signal_set::flags_t flags)
452  
    posix_signal& impl, int signal_number, signal_set::flags_t flags)
453  
{
453  
{
454  
    if (signal_number < 0 || signal_number >= max_signal_number)
454  
    if (signal_number < 0 || signal_number >= max_signal_number)
455  
        return make_error_code(std::errc::invalid_argument);
455  
        return make_error_code(std::errc::invalid_argument);
456  

456  

457  
    // Validate that requested flags are supported on this platform
457  
    // Validate that requested flags are supported on this platform
458  
    // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
458  
    // (e.g., SA_NOCLDWAIT may not be available on all POSIX systems)
459  
    if (!posix_signal_detail::flags_supported(flags))
459  
    if (!posix_signal_detail::flags_supported(flags))
460  
        return make_error_code(std::errc::operation_not_supported);
460  
        return make_error_code(std::errc::operation_not_supported);
461  

461  

462  
    posix_signal_detail::signal_state* state =
462  
    posix_signal_detail::signal_state* state =
463  
        posix_signal_detail::get_signal_state();
463  
        posix_signal_detail::get_signal_state();
464  
    std::lock_guard state_lock(state->mutex);
464  
    std::lock_guard state_lock(state->mutex);
465  
    std::lock_guard lock(mutex_);
465  
    std::lock_guard lock(mutex_);
466  

466  

467  
    // Find insertion point (list is sorted by signal number)
467  
    // Find insertion point (list is sorted by signal number)
468  
    signal_registration** insertion_point = &impl.signals_;
468  
    signal_registration** insertion_point = &impl.signals_;
469  
    signal_registration* reg              = impl.signals_;
469  
    signal_registration* reg              = impl.signals_;
470  
    while (reg && reg->signal_number < signal_number)
470  
    while (reg && reg->signal_number < signal_number)
471  
    {
471  
    {
472  
        insertion_point = &reg->next_in_set;
472  
        insertion_point = &reg->next_in_set;
473  
        reg             = reg->next_in_set;
473  
        reg             = reg->next_in_set;
474  
    }
474  
    }
475  

475  

476  
    // Already registered in this set - check flag compatibility
476  
    // Already registered in this set - check flag compatibility
477  
    // (same signal_set adding same signal twice with different flags)
477  
    // (same signal_set adding same signal twice with different flags)
478  
    if (reg && reg->signal_number == signal_number)
478  
    if (reg && reg->signal_number == signal_number)
479  
    {
479  
    {
480  
        if (!posix_signal_detail::flags_compatible(reg->flags, flags))
480  
        if (!posix_signal_detail::flags_compatible(reg->flags, flags))
481  
            return make_error_code(std::errc::invalid_argument);
481  
            return make_error_code(std::errc::invalid_argument);
482  
        return {};
482  
        return {};
483  
    }
483  
    }
484  

484  

485  
    // Check flag compatibility with global registration
485  
    // Check flag compatibility with global registration
486  
    // (different signal_set already registered this signal with different flags)
486  
    // (different signal_set already registered this signal with different flags)
487  
    if (state->registration_count[signal_number] > 0)
487  
    if (state->registration_count[signal_number] > 0)
488  
    {
488  
    {
489  
        if (!posix_signal_detail::flags_compatible(
489  
        if (!posix_signal_detail::flags_compatible(
490  
                state->registered_flags[signal_number], flags))
490  
                state->registered_flags[signal_number], flags))
491  
            return make_error_code(std::errc::invalid_argument);
491  
            return make_error_code(std::errc::invalid_argument);
492  
    }
492  
    }
493  

493  

494  
    auto* new_reg          = new signal_registration;
494  
    auto* new_reg          = new signal_registration;
495  
    new_reg->signal_number = signal_number;
495  
    new_reg->signal_number = signal_number;
496  
    new_reg->flags         = flags;
496  
    new_reg->flags         = flags;
497  
    new_reg->owner         = &impl;
497  
    new_reg->owner         = &impl;
498  
    new_reg->undelivered   = 0;
498  
    new_reg->undelivered   = 0;
499  

499  

500  
    // Install signal handler on first global registration
500  
    // Install signal handler on first global registration
501  
    if (state->registration_count[signal_number] == 0)
501  
    if (state->registration_count[signal_number] == 0)
502  
    {
502  
    {
503  
        struct sigaction sa = {};
503  
        struct sigaction sa = {};
504  
        sa.sa_handler       = posix_signal_detail::corosio_posix_signal_handler;
504  
        sa.sa_handler       = posix_signal_detail::corosio_posix_signal_handler;
505  
        sigemptyset(&sa.sa_mask);
505  
        sigemptyset(&sa.sa_mask);
506  
        sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
506  
        sa.sa_flags = posix_signal_detail::to_sigaction_flags(flags);
507  

507  

508  
        if (::sigaction(signal_number, &sa, nullptr) < 0)
508  
        if (::sigaction(signal_number, &sa, nullptr) < 0)
509  
        {
509  
        {
510  
            delete new_reg;
510  
            delete new_reg;
511  
            return make_error_code(std::errc::invalid_argument);
511  
            return make_error_code(std::errc::invalid_argument);
512  
        }
512  
        }
513  

513  

514  
        // Store the flags used for first registration
514  
        // Store the flags used for first registration
515  
        state->registered_flags[signal_number] = flags;
515  
        state->registered_flags[signal_number] = flags;
516  
    }
516  
    }
517  

517  

518  
    new_reg->next_in_set = reg;
518  
    new_reg->next_in_set = reg;
519  
    *insertion_point     = new_reg;
519  
    *insertion_point     = new_reg;
520  

520  

521  
    new_reg->next_in_table = registrations_[signal_number];
521  
    new_reg->next_in_table = registrations_[signal_number];
522  
    new_reg->prev_in_table = nullptr;
522  
    new_reg->prev_in_table = nullptr;
523  
    if (registrations_[signal_number])
523  
    if (registrations_[signal_number])
524  
        registrations_[signal_number]->prev_in_table = new_reg;
524  
        registrations_[signal_number]->prev_in_table = new_reg;
525  
    registrations_[signal_number] = new_reg;
525  
    registrations_[signal_number] = new_reg;
526  

526  

527  
    ++state->registration_count[signal_number];
527  
    ++state->registration_count[signal_number];
528  
    ++registration_count_[signal_number];
528  
    ++registration_count_[signal_number];
529  

529  

530  
    return {};
530  
    return {};
531  
}
531  
}
532  

532  

533  
inline std::error_code
533  
inline std::error_code
534  
posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
534  
posix_signal_service::remove_signal(posix_signal& impl, int signal_number)
535  
{
535  
{
536  
    if (signal_number < 0 || signal_number >= max_signal_number)
536  
    if (signal_number < 0 || signal_number >= max_signal_number)
537  
        return make_error_code(std::errc::invalid_argument);
537  
        return make_error_code(std::errc::invalid_argument);
538  

538  

539  
    posix_signal_detail::signal_state* state =
539  
    posix_signal_detail::signal_state* state =
540  
        posix_signal_detail::get_signal_state();
540  
        posix_signal_detail::get_signal_state();
541  
    std::lock_guard state_lock(state->mutex);
541  
    std::lock_guard state_lock(state->mutex);
542  
    std::lock_guard lock(mutex_);
542  
    std::lock_guard lock(mutex_);
543  

543  

544  
    signal_registration** deletion_point = &impl.signals_;
544  
    signal_registration** deletion_point = &impl.signals_;
545  
    signal_registration* reg             = impl.signals_;
545  
    signal_registration* reg             = impl.signals_;
546  
    while (reg && reg->signal_number < signal_number)
546  
    while (reg && reg->signal_number < signal_number)
547  
    {
547  
    {
548  
        deletion_point = &reg->next_in_set;
548  
        deletion_point = &reg->next_in_set;
549  
        reg            = reg->next_in_set;
549  
        reg            = reg->next_in_set;
550  
    }
550  
    }
551  

551  

552  
    if (!reg || reg->signal_number != signal_number)
552  
    if (!reg || reg->signal_number != signal_number)
553  
        return {};
553  
        return {};
554  

554  

555  
    // Restore default handler on last global unregistration
555  
    // Restore default handler on last global unregistration
556  
    if (state->registration_count[signal_number] == 1)
556  
    if (state->registration_count[signal_number] == 1)
557  
    {
557  
    {
558  
        struct sigaction sa = {};
558  
        struct sigaction sa = {};
559  
        sa.sa_handler       = SIG_DFL;
559  
        sa.sa_handler       = SIG_DFL;
560  
        sigemptyset(&sa.sa_mask);
560  
        sigemptyset(&sa.sa_mask);
561  
        sa.sa_flags = 0;
561  
        sa.sa_flags = 0;
562  

562  

563  
        if (::sigaction(signal_number, &sa, nullptr) < 0)
563  
        if (::sigaction(signal_number, &sa, nullptr) < 0)
564  
            return make_error_code(std::errc::invalid_argument);
564  
            return make_error_code(std::errc::invalid_argument);
565  

565  

566  
        // Clear stored flags
566  
        // Clear stored flags
567  
        state->registered_flags[signal_number] = signal_set::none;
567  
        state->registered_flags[signal_number] = signal_set::none;
568  
    }
568  
    }
569  

569  

570  
    *deletion_point = reg->next_in_set;
570  
    *deletion_point = reg->next_in_set;
571  

571  

572  
    if (registrations_[signal_number] == reg)
572  
    if (registrations_[signal_number] == reg)
573  
        registrations_[signal_number] = reg->next_in_table;
573  
        registrations_[signal_number] = reg->next_in_table;
574  
    if (reg->prev_in_table)
574  
    if (reg->prev_in_table)
575  
        reg->prev_in_table->next_in_table = reg->next_in_table;
575  
        reg->prev_in_table->next_in_table = reg->next_in_table;
576  
    if (reg->next_in_table)
576  
    if (reg->next_in_table)
577  
        reg->next_in_table->prev_in_table = reg->prev_in_table;
577  
        reg->next_in_table->prev_in_table = reg->prev_in_table;
578  

578  

579  
    --state->registration_count[signal_number];
579  
    --state->registration_count[signal_number];
580  
    --registration_count_[signal_number];
580  
    --registration_count_[signal_number];
581  

581  

582  
    delete reg;
582  
    delete reg;
583  
    return {};
583  
    return {};
584  
}
584  
}
585  

585  

586  
inline std::error_code
586  
inline std::error_code
587  
posix_signal_service::clear_signals(posix_signal& impl)
587  
posix_signal_service::clear_signals(posix_signal& impl)
588  
{
588  
{
589  
    posix_signal_detail::signal_state* state =
589  
    posix_signal_detail::signal_state* state =
590  
        posix_signal_detail::get_signal_state();
590  
        posix_signal_detail::get_signal_state();
591  
    std::lock_guard state_lock(state->mutex);
591  
    std::lock_guard state_lock(state->mutex);
592  
    std::lock_guard lock(mutex_);
592  
    std::lock_guard lock(mutex_);
593  

593  

594  
    std::error_code first_error;
594  
    std::error_code first_error;
595  

595  

596  
    while (signal_registration* reg = impl.signals_)
596  
    while (signal_registration* reg = impl.signals_)
597  
    {
597  
    {
598  
        int signal_number = reg->signal_number;
598  
        int signal_number = reg->signal_number;
599  

599  

600  
        if (state->registration_count[signal_number] == 1)
600  
        if (state->registration_count[signal_number] == 1)
601  
        {
601  
        {
602  
            struct sigaction sa = {};
602  
            struct sigaction sa = {};
603  
            sa.sa_handler       = SIG_DFL;
603  
            sa.sa_handler       = SIG_DFL;
604  
            sigemptyset(&sa.sa_mask);
604  
            sigemptyset(&sa.sa_mask);
605  
            sa.sa_flags = 0;
605  
            sa.sa_flags = 0;
606  

606  

607  
            if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
607  
            if (::sigaction(signal_number, &sa, nullptr) < 0 && !first_error)
608  
                first_error = make_error_code(std::errc::invalid_argument);
608  
                first_error = make_error_code(std::errc::invalid_argument);
609  

609  

610  
            // Clear stored flags
610  
            // Clear stored flags
611  
            state->registered_flags[signal_number] = signal_set::none;
611  
            state->registered_flags[signal_number] = signal_set::none;
612  
        }
612  
        }
613  

613  

614  
        impl.signals_ = reg->next_in_set;
614  
        impl.signals_ = reg->next_in_set;
615  

615  

616  
        if (registrations_[signal_number] == reg)
616  
        if (registrations_[signal_number] == reg)
617  
            registrations_[signal_number] = reg->next_in_table;
617  
            registrations_[signal_number] = reg->next_in_table;
618  
        if (reg->prev_in_table)
618  
        if (reg->prev_in_table)
619  
            reg->prev_in_table->next_in_table = reg->next_in_table;
619  
            reg->prev_in_table->next_in_table = reg->next_in_table;
620  
        if (reg->next_in_table)
620  
        if (reg->next_in_table)
621  
            reg->next_in_table->prev_in_table = reg->prev_in_table;
621  
            reg->next_in_table->prev_in_table = reg->prev_in_table;
622  

622  

623  
        --state->registration_count[signal_number];
623  
        --state->registration_count[signal_number];
624  
        --registration_count_[signal_number];
624  
        --registration_count_[signal_number];
625  

625  

626  
        delete reg;
626  
        delete reg;
627  
    }
627  
    }
628  

628  

629  
    if (first_error)
629  
    if (first_error)
630  
        return first_error;
630  
        return first_error;
631  
    return {};
631  
    return {};
632  
}
632  
}
633  

633  

634  
inline void
634  
inline void
635  
posix_signal_service::cancel_wait(posix_signal& impl)
635  
posix_signal_service::cancel_wait(posix_signal& impl)
636  
{
636  
{
637  
    bool was_waiting = false;
637  
    bool was_waiting = false;
638  
    signal_op* op    = nullptr;
638  
    signal_op* op    = nullptr;
639  

639  

640  
    {
640  
    {
641  
        std::lock_guard lock(mutex_);
641  
        std::lock_guard lock(mutex_);
642  
        if (impl.waiting_)
642  
        if (impl.waiting_)
643  
        {
643  
        {
644  
            was_waiting   = true;
644  
            was_waiting   = true;
645  
            impl.waiting_ = false;
645  
            impl.waiting_ = false;
646  
            op            = &impl.pending_op_;
646  
            op            = &impl.pending_op_;
647  
        }
647  
        }
648  
    }
648  
    }
649  

649  

650  
    if (was_waiting)
650  
    if (was_waiting)
651  
    {
651  
    {
652  
        if (op->ec_out)
652  
        if (op->ec_out)
653  
            *op->ec_out = make_error_code(capy::error::canceled);
653  
            *op->ec_out = make_error_code(capy::error::canceled);
654  
        if (op->signal_out)
654  
        if (op->signal_out)
655  
            *op->signal_out = 0;
655  
            *op->signal_out = 0;
656  
        op->d.post(op->h);
656  
        op->d.post(op->h);
657  
        sched_->work_finished();
657  
        sched_->work_finished();
658  
    }
658  
    }
659  
}
659  
}
660  

660  

661  
inline void
661  
inline void
662  
posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
662  
posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
663  
{
663  
{
664  
    {
664  
    {
665  
        std::lock_guard lock(mutex_);
665  
        std::lock_guard lock(mutex_);
666  

666  

667  
        // Check for queued signals first (signal arrived before wait started)
667  
        // Check for queued signals first (signal arrived before wait started)
668  
        signal_registration* reg = impl.signals_;
668  
        signal_registration* reg = impl.signals_;
669  
        while (reg)
669  
        while (reg)
670  
        {
670  
        {
671  
            if (reg->undelivered > 0)
671  
            if (reg->undelivered > 0)
672  
            {
672  
            {
673  
                --reg->undelivered;
673  
                --reg->undelivered;
674  
                op->signal_number = reg->signal_number;
674  
                op->signal_number = reg->signal_number;
675  
                // svc=nullptr: no work_finished needed since we never called work_started
675  
                // svc=nullptr: no work_finished needed since we never called work_started
676  
                op->svc = nullptr;
676  
                op->svc = nullptr;
677  
                sched_->post(op);
677  
                sched_->post(op);
678  
                return;
678  
                return;
679  
            }
679  
            }
680  
            reg = reg->next_in_set;
680  
            reg = reg->next_in_set;
681  
        }
681  
        }
682  

682  

683  
        // No queued signals - wait for delivery
683  
        // No queued signals - wait for delivery
684  
        impl.waiting_ = true;
684  
        impl.waiting_ = true;
685  
        // svc=this: signal_op::operator() will call work_finished() to balance this
685  
        // svc=this: signal_op::operator() will call work_finished() to balance this
686  
        op->svc = this;
686  
        op->svc = this;
687  
        sched_->work_started();
687  
        sched_->work_started();
688  
    }
688  
    }
689  
}
689  
}
690  

690  

691  
inline void
691  
inline void
692  
posix_signal_service::deliver_signal(int signal_number)
692  
posix_signal_service::deliver_signal(int signal_number)
693  
{
693  
{
694  
    if (signal_number < 0 || signal_number >= max_signal_number)
694  
    if (signal_number < 0 || signal_number >= max_signal_number)
695  
        return;
695  
        return;
696  

696  

697  
    posix_signal_detail::signal_state* state =
697  
    posix_signal_detail::signal_state* state =
698  
        posix_signal_detail::get_signal_state();
698  
        posix_signal_detail::get_signal_state();
699  
    std::lock_guard lock(state->mutex);
699  
    std::lock_guard lock(state->mutex);
700  

700  

701  
    posix_signal_service* service = state->service_list;
701  
    posix_signal_service* service = state->service_list;
702  
    while (service)
702  
    while (service)
703  
    {
703  
    {
704  
        std::lock_guard svc_lock(service->mutex_);
704  
        std::lock_guard svc_lock(service->mutex_);
705  

705  

706  
        signal_registration* reg = service->registrations_[signal_number];
706  
        signal_registration* reg = service->registrations_[signal_number];
707  
        while (reg)
707  
        while (reg)
708  
        {
708  
        {
709  
            posix_signal* impl = static_cast<posix_signal*>(reg->owner);
709  
            posix_signal* impl = static_cast<posix_signal*>(reg->owner);
710  

710  

711  
            if (impl->waiting_)
711  
            if (impl->waiting_)
712  
            {
712  
            {
713  
                impl->waiting_                  = false;
713  
                impl->waiting_                  = false;
714  
                impl->pending_op_.signal_number = signal_number;
714  
                impl->pending_op_.signal_number = signal_number;
715  
                service->post(&impl->pending_op_);
715  
                service->post(&impl->pending_op_);
716  
            }
716  
            }
717  
            else
717  
            else
718  
            {
718  
            {
719  
                ++reg->undelivered;
719  
                ++reg->undelivered;
720  
            }
720  
            }
721  

721  

722  
            reg = reg->next_in_table;
722  
            reg = reg->next_in_table;
723  
        }
723  
        }
724  

724  

725  
        service = service->next_;
725  
        service = service->next_;
726  
    }
726  
    }
727  
}
727  
}
728  

728  

729  
inline void
729  
inline void
730  
posix_signal_service::work_started() noexcept
730  
posix_signal_service::work_started() noexcept
731  
{
731  
{
732  
    sched_->work_started();
732  
    sched_->work_started();
733  
}
733  
}
734  

734  

735  
inline void
735  
inline void
736  
posix_signal_service::work_finished() noexcept
736  
posix_signal_service::work_finished() noexcept
737  
{
737  
{
738  
    sched_->work_finished();
738  
    sched_->work_finished();
739  
}
739  
}
740  

740  

741  
inline void
741  
inline void
742  
posix_signal_service::post(signal_op* op)
742  
posix_signal_service::post(signal_op* op)
743  
{
743  
{
744  
    sched_->post(op);
744  
    sched_->post(op);
745  
}
745  
}
746  

746  

747  
inline void
747  
inline void
748  
posix_signal_service::add_service(posix_signal_service* service)
748  
posix_signal_service::add_service(posix_signal_service* service)
749  
{
749  
{
750  
    posix_signal_detail::signal_state* state =
750  
    posix_signal_detail::signal_state* state =
751  
        posix_signal_detail::get_signal_state();
751  
        posix_signal_detail::get_signal_state();
752  
    std::lock_guard lock(state->mutex);
752  
    std::lock_guard lock(state->mutex);
753  

753  

754  
    service->next_ = state->service_list;
754  
    service->next_ = state->service_list;
755  
    service->prev_ = nullptr;
755  
    service->prev_ = nullptr;
756  
    if (state->service_list)
756  
    if (state->service_list)
757  
        state->service_list->prev_ = service;
757  
        state->service_list->prev_ = service;
758  
    state->service_list = service;
758  
    state->service_list = service;
759  
}
759  
}
760  

760  

761  
inline void
761  
inline void
762  
posix_signal_service::remove_service(posix_signal_service* service)
762  
posix_signal_service::remove_service(posix_signal_service* service)
763  
{
763  
{
764  
    posix_signal_detail::signal_state* state =
764  
    posix_signal_detail::signal_state* state =
765  
        posix_signal_detail::get_signal_state();
765  
        posix_signal_detail::get_signal_state();
766  
    std::lock_guard lock(state->mutex);
766  
    std::lock_guard lock(state->mutex);
767  

767  

768  
    if (service->next_ || service->prev_ || state->service_list == service)
768  
    if (service->next_ || service->prev_ || state->service_list == service)
769  
    {
769  
    {
770  
        if (state->service_list == service)
770  
        if (state->service_list == service)
771  
            state->service_list = service->next_;
771  
            state->service_list = service->next_;
772  
        if (service->prev_)
772  
        if (service->prev_)
773  
            service->prev_->next_ = service->next_;
773  
            service->prev_->next_ = service->next_;
774  
        if (service->next_)
774  
        if (service->next_)
775  
            service->next_->prev_ = service->prev_;
775  
            service->next_->prev_ = service->prev_;
776  
        service->next_ = nullptr;
776  
        service->next_ = nullptr;
777  
        service->prev_ = nullptr;
777  
        service->prev_ = nullptr;
778  
    }
778  
    }
779  
}
779  
}
780  

780  

781  
// get_signal_service - factory function
781  
// get_signal_service - factory function
782  

782  

783  
inline posix_signal_service&
783  
inline posix_signal_service&
784  
get_signal_service(capy::execution_context& ctx, scheduler& sched)
784  
get_signal_service(capy::execution_context& ctx, scheduler& sched)
785  
{
785  
{
786  
    return ctx.make_service<posix_signal_service>(sched);
786  
    return ctx.make_service<posix_signal_service>(sched);
787  
}
787  
}
788  

788  

789  
} // namespace detail
789  
} // namespace detail
790  
} // namespace boost::corosio
790  
} // namespace boost::corosio
791  

791  

792  
#endif // BOOST_COROSIO_POSIX
792  
#endif // BOOST_COROSIO_POSIX
793  

793  

794  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
794  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP