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