include/boost/corosio/native/detail/epoll/epoll_op.hpp

100.0% Lines (12/12) 100.0% List of functions (2/2)
epoll_op.hpp
f(x) Functions (2)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
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_EPOLL_EPOLL_OP_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_HAS_EPOLL
16
17 #include <boost/corosio/native/detail/reactor/reactor_op.hpp>
18 #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
19
20 /*
21 epoll Operation State
22 =====================
23
24 Each async I/O operation has a corresponding epoll_op-derived struct that
25 holds the operation's state while it's in flight. The socket impl owns
26 fixed slots for each operation type (conn_, rd_, wr_), so only one
27 operation of each type can be pending per socket at a time.
28
29 Persistent Registration
30 -----------------------
31 File descriptors are registered with epoll once (via descriptor_state) and
32 stay registered until closed. The descriptor_state tracks which operations
33 are pending (read_op, write_op, connect_op). When an event arrives, the
34 reactor dispatches to the appropriate pending operation.
35
36 Impl Lifetime Management
37 ------------------------
38 When cancel() posts an op to the scheduler's ready queue, the socket impl
39 might be destroyed before the scheduler processes the op. The `impl_ptr`
40 member holds a shared_ptr to the impl, keeping it alive until the op
41 completes. This is set by cancel() and cleared in operator() after the
42 coroutine is resumed.
43
44 EOF Detection
45 -------------
46 For reads, 0 bytes with no error means EOF. But an empty user buffer also
47 returns 0 bytes. The `empty_buffer_read` flag distinguishes these cases.
48
49 SIGPIPE Prevention
50 ------------------
51 Writes use sendmsg() with MSG_NOSIGNAL instead of writev() to prevent
52 SIGPIPE when the peer has closed.
53 */
54
55 namespace boost::corosio::detail {
56
57 // Forward declarations
58 class epoll_tcp_socket;
59 class epoll_tcp_acceptor;
60 struct epoll_op;
61
62 // Forward declaration
63 class epoll_scheduler;
64
65 /// Per-descriptor state for persistent epoll registration.
66 struct descriptor_state final : reactor_descriptor_state
67 {};
68
69 /// epoll base operation — thin wrapper over reactor_op.
70 struct epoll_op : reactor_op<epoll_tcp_socket, epoll_tcp_acceptor>
71 {
72 void operator()() override;
73 };
74
75 /// epoll connect operation.
76 struct epoll_connect_op final : reactor_connect_op<epoll_op>
77 {
78 void operator()() override;
79 void cancel() noexcept override;
80 };
81
82 /// epoll scatter-read operation.
83 struct epoll_read_op final : reactor_read_op<epoll_op>
84 {
85 void cancel() noexcept override;
86 };
87
88 /** Provides sendmsg(MSG_NOSIGNAL) with EINTR retry for epoll writes. */
89 struct epoll_write_policy
90 {
91 182281x static ssize_t write(int fd, iovec* iovecs, int count) noexcept
92 {
93 182281x msghdr msg{};
94 182281x msg.msg_iov = iovecs;
95 182281x msg.msg_iovlen = static_cast<std::size_t>(count);
96
97 ssize_t n;
98 do
99 {
100 182281x n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
101 }
102 182281x while (n < 0 && errno == EINTR);
103 182281x return n;
104 }
105 };
106
107 /// epoll gather-write operation.
108 struct epoll_write_op final : reactor_write_op<epoll_op, epoll_write_policy>
109 {
110 void cancel() noexcept override;
111 };
112
113 /** Provides accept4(SOCK_NONBLOCK|SOCK_CLOEXEC) with EINTR retry. */
114 struct epoll_accept_policy
115 {
116 4000x static int do_accept(
117 int fd, sockaddr_storage& peer, socklen_t& addrlen_out) noexcept
118 {
119 4000x addrlen_out = sizeof(peer);
120 int new_fd;
121 do
122 {
123 4000x new_fd = ::accept4(
124 fd, reinterpret_cast<sockaddr*>(&peer), &addrlen_out,
125 SOCK_NONBLOCK | SOCK_CLOEXEC);
126 }
127 4000x while (new_fd < 0 && errno == EINTR);
128 4000x return new_fd;
129 }
130 };
131
132 /// epoll accept operation.
133 struct epoll_accept_op final : reactor_accept_op<epoll_op, epoll_accept_policy>
134 {
135 void operator()() override;
136 void cancel() noexcept override;
137 };
138
139 } // namespace boost::corosio::detail
140
141 #endif // BOOST_COROSIO_HAS_EPOLL
142
143 #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP
144