include/boost/corosio/native/detail/reactor/reactor_signal_pipe.hpp

90.0% Lines (18 / 20) 75.0% Functions (3 / 4)
reactor_signal_pipe.hpp
f(x) Functions (4)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SIGNAL_PIPE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SIGNAL_PIPE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_POSIX
16
17 #include <boost/corosio/native/detail/posix/posix_signal_service.hpp>
18 #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
19 #include <boost/corosio/native/detail/reactor/reactor_op_base.hpp>
20
21 #include <errno.h>
22
23 /*
24 Reactor signal-pipe reader
25 ==========================
26
27 Bridges the global POSIX signal self-pipe (see posix_signal_service.hpp)
28 to the reactor backends (epoll/kqueue/select). The concrete scheduler owns
29 one of these for its lifetime and, in register_signal_reader(), parks the
30 drain op as the descriptor's read_op then calls register_descriptor().
31
32 On each read-readiness edge the drain op completes without touching the
33 pipe, and invoke_deferred_io runs it once the descriptor lock is
34 released. Only then does it drain the pipe and deliver the signals:
35 delivery posts into every context watching the pipe, and a post made
36 under this descriptor's lock would order it before another context's
37 scheduler lock, the reverse of that scheduler's own order. The op then
38 parks itself again, draining once more if an edge arrived meanwhile.
39 */
40
41 namespace boost::corosio::detail {
42
43 struct reactor_signal_pipe_reader
44 {
45 struct drain_op final : reactor_op_base
46 {
47 reactor_signal_pipe_reader* reader = nullptr;
48
49 982x void perform_io() noexcept override
50 {
51 982x errn = 0;
52 982x }
53
54 982x void operator()() override
55 {
56 982x auto& desc = reader->desc;
57 for (;;)
58 {
59 982x posix_signal_detail::drain_signal_pipe();
60 982x conditionally_enabled_mutex::scoped_lock lock(desc.mutex);
61 // An edge seen while unparked set read_ready instead.
62 982x if (!desc.read_ready)
63 {
64 982x desc.read_op = this;
65 982x break;
66 }
67 ✗ desc.read_ready = false;
68 982x }
69 // A select() started while this op was out ran without the
70 // pipe in its read set.
71 982x desc.scheduler_->notify_reparked();
72 // Nothing counted this op as work; balance the decrement
73 // that follows the inline run of a completed op.
74 982x desc.scheduler_->compensating_work_started();
75 982x }
76
77 ✗ void destroy() override {}
78 };
79
80 reactor_descriptor_state desc;
81 drain_op op;
82
83 // Park the drain op and return the descriptor to hand to
84 // scheduler::register_descriptor(read_fd, ...).
85 557x reactor_descriptor_state* arm() noexcept
86 {
87 557x op.reader = this;
88 557x desc.read_op = &op;
89 557x return &desc;
90 }
91 };
92
93 } // namespace boost::corosio::detail
94
95 #endif // BOOST_COROSIO_POSIX
96
97 #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SIGNAL_PIPE_HPP
98