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

95.0% Lines (19 / 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 940x void perform_io() noexcept override
50 {
51 940x errn = 0;
52 940x }
53
54 940x void operator()() override
55 {
56 940x auto& desc = reader->desc;
57 for (;;)
58 {
59 941x posix_signal_detail::drain_signal_pipe();
60 941x conditionally_enabled_mutex::scoped_lock lock(desc.mutex);
61 // An edge seen while unparked set read_ready instead.
62 941x if (!desc.read_ready)
63 {
64 940x desc.read_op = this;
65 940x break;
66 }
67 1x desc.read_ready = false;
68 942x }
69 // A select() started while this op was out ran without the
70 // pipe in its read set.
71 940x 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 940x desc.scheduler_->compensating_work_started();
75 940x }
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 555x reactor_descriptor_state* arm() noexcept
86 {
87 555x op.reader = this;
88 555x desc.read_op = &op;
89 555x 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