TLA Line data 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 HIT 127248 : static ssize_t write(int fd, iovec* iovecs, int count) noexcept
92 : {
93 127248 : msghdr msg{};
94 127248 : msg.msg_iov = iovecs;
95 127248 : msg.msg_iovlen = static_cast<std::size_t>(count);
96 :
97 : ssize_t n;
98 : do
99 : {
100 127248 : n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
101 : }
102 127248 : while (n < 0 && errno == EINTR);
103 127248 : 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 4395 : static int do_accept(int fd, sockaddr_storage& peer) noexcept
117 : {
118 4395 : socklen_t addrlen = sizeof(peer);
119 : int new_fd;
120 : do
121 : {
122 4395 : new_fd = ::accept4(
123 : fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
124 : SOCK_NONBLOCK | SOCK_CLOEXEC);
125 : }
126 4395 : while (new_fd < 0 && errno == EINTR);
127 4395 : 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
|