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  
    cont_op.cont.h = h;
316  
    cont_op.cont.h = h;
317  
    d.post(cont_op.cont);
317  
    d.post(cont_op.cont);
318  

318  

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

323  

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

329  

330  
// posix_signal implementation
330  
// posix_signal implementation
331  

331  

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

336  

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

350  

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

362  

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

367  

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

373  

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

379  

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

385  

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

391  

392  
// posix_signal_service implementation
392  
// posix_signal_service implementation
393  

393  

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

405  

406  
inline posix_signal_service::~posix_signal_service()
406  
inline posix_signal_service::~posix_signal_service()
407  
{
407  
{
408  
    remove_service(this);
408  
    remove_service(this);
409  
}
409  
}
410  

410  

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

415  

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

427  

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

432  

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

437  

438  
    return impl;
438  
    return impl;
439  
}
439  
}
440  

440  

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

448  

449  
    delete &impl;
449  
    delete &impl;
450  
}
450  
}
451  

451  

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

458  

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

463  

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

468  

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

477  

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

486  

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

495  

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

501  

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

509  

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

515  

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

519  

520  
    new_reg->next_in_set = reg;
520  
    new_reg->next_in_set = reg;
521  
    *insertion_point     = new_reg;
521  
    *insertion_point     = new_reg;
522  

522  

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

528  

529  
    ++state->registration_count[signal_number];
529  
    ++state->registration_count[signal_number];
530  
    ++registration_count_[signal_number];
530  
    ++registration_count_[signal_number];
531  

531  

532  
    return {};
532  
    return {};
533  
}
533  
}
534  

534  

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

540  

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

545  

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

553  

554  
    if (!reg || reg->signal_number != signal_number)
554  
    if (!reg || reg->signal_number != signal_number)
555  
        return {};
555  
        return {};
556  

556  

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

564  

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

567  

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

571  

572  
    *deletion_point = reg->next_in_set;
572  
    *deletion_point = reg->next_in_set;
573  

573  

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

580  

581  
    --state->registration_count[signal_number];
581  
    --state->registration_count[signal_number];
582  
    --registration_count_[signal_number];
582  
    --registration_count_[signal_number];
583  

583  

584  
    delete reg;
584  
    delete reg;
585  
    return {};
585  
    return {};
586  
}
586  
}
587  

587  

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

595  

596  
    std::error_code first_error;
596  
    std::error_code first_error;
597  

597  

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

601  

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

608  

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

611  

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

615  

616  
        impl.signals_ = reg->next_in_set;
616  
        impl.signals_ = reg->next_in_set;
617  

617  

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

624  

625  
        --state->registration_count[signal_number];
625  
        --state->registration_count[signal_number];
626  
        --registration_count_[signal_number];
626  
        --registration_count_[signal_number];
627  

627  

628  
        delete reg;
628  
        delete reg;
629  
    }
629  
    }
630  

630  

631  
    if (first_error)
631  
    if (first_error)
632  
        return first_error;
632  
        return first_error;
633  
    return {};
633  
    return {};
634  
}
634  
}
635  

635  

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

641  

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

651  

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

663  

664  
inline void
664  
inline void
665  
posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
665  
posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
666  
{
666  
{
667  
    {
667  
    {
668  
        std::lock_guard lock(mutex_);
668  
        std::lock_guard lock(mutex_);
669  

669  

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

685  

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

693  

694  
inline void
694  
inline void
695  
posix_signal_service::deliver_signal(int signal_number)
695  
posix_signal_service::deliver_signal(int signal_number)
696  
{
696  
{
697  
    if (signal_number < 0 || signal_number >= max_signal_number)
697  
    if (signal_number < 0 || signal_number >= max_signal_number)
698  
        return;
698  
        return;
699  

699  

700  
    posix_signal_detail::signal_state* state =
700  
    posix_signal_detail::signal_state* state =
701  
        posix_signal_detail::get_signal_state();
701  
        posix_signal_detail::get_signal_state();
702  
    std::lock_guard lock(state->mutex);
702  
    std::lock_guard lock(state->mutex);
703  

703  

704  
    posix_signal_service* service = state->service_list;
704  
    posix_signal_service* service = state->service_list;
705  
    while (service)
705  
    while (service)
706  
    {
706  
    {
707  
        std::lock_guard svc_lock(service->mutex_);
707  
        std::lock_guard svc_lock(service->mutex_);
708  

708  

709  
        signal_registration* reg = service->registrations_[signal_number];
709  
        signal_registration* reg = service->registrations_[signal_number];
710  
        while (reg)
710  
        while (reg)
711  
        {
711  
        {
712  
            posix_signal* impl = static_cast<posix_signal*>(reg->owner);
712  
            posix_signal* impl = static_cast<posix_signal*>(reg->owner);
713  

713  

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

724  

725  
            reg = reg->next_in_table;
725  
            reg = reg->next_in_table;
726  
        }
726  
        }
727  

727  

728  
        service = service->next_;
728  
        service = service->next_;
729  
    }
729  
    }
730  
}
730  
}
731  

731  

732  
inline void
732  
inline void
733  
posix_signal_service::work_started() noexcept
733  
posix_signal_service::work_started() noexcept
734  
{
734  
{
735  
    sched_->work_started();
735  
    sched_->work_started();
736  
}
736  
}
737  

737  

738  
inline void
738  
inline void
739  
posix_signal_service::work_finished() noexcept
739  
posix_signal_service::work_finished() noexcept
740  
{
740  
{
741  
    sched_->work_finished();
741  
    sched_->work_finished();
742  
}
742  
}
743  

743  

744  
inline void
744  
inline void
745  
posix_signal_service::post(signal_op* op)
745  
posix_signal_service::post(signal_op* op)
746  
{
746  
{
747  
    sched_->post(op);
747  
    sched_->post(op);
748  
}
748  
}
749  

749  

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

756  

757  
    service->next_ = state->service_list;
757  
    service->next_ = state->service_list;
758  
    service->prev_ = nullptr;
758  
    service->prev_ = nullptr;
759  
    if (state->service_list)
759  
    if (state->service_list)
760  
        state->service_list->prev_ = service;
760  
        state->service_list->prev_ = service;
761  
    state->service_list = service;
761  
    state->service_list = service;
762  
}
762  
}
763  

763  

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

770  

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

783  

784  
// get_signal_service - factory function
784  
// get_signal_service - factory function
785  

785  

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

791  

792  
} // namespace detail
792  
} // namespace detail
793  
} // namespace boost::corosio
793  
} // namespace boost::corosio
794  

794  

795  
#endif // BOOST_COROSIO_POSIX
795  
#endif // BOOST_COROSIO_POSIX
796  

796  

797  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP
797  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_SIGNAL_SERVICE_HPP