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

100.0% Lines (12/12) 100.0% List of functions (2/2)
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_socket;
59 class epoll_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_socket, epoll_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 127248x static ssize_t write(int fd, iovec* iovecs, int count) noexcept
92 {
93 127248x msghdr msg{};
94 127248x msg.msg_iov = iovecs;
95 127248x msg.msg_iovlen = static_cast<std::size_t>(count);
96
97 ssize_t n;
98 do
99 {
100 127248x n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
101 }
102 127248x while (n < 0 && errno == EINTR);
103 127248x 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 4395x static int do_accept(int fd, sockaddr_storage& peer) noexcept
117 {
118 4395x socklen_t addrlen = sizeof(peer);
119 int new_fd;
120 do
121 {
122 4395x new_fd = ::accept4(
123 fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
124 SOCK_NONBLOCK | SOCK_CLOEXEC);
125 }
126 4395x while (new_fd < 0 && errno == EINTR);
127 4395x return new_fd;
128 }
129 };
130
131 /// epoll accept operation.
132 struct epoll_accept_op final : reactor_accept_op<epoll_op, epoll_accept_policy>
133 {
134 void operator()() override;
135 void cancel() noexcept override;
136 };
137
138 } // namespace boost::corosio::detail
139
140 #endif // BOOST_COROSIO_HAS_EPOLL
141
142 #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_OP_HPP
143