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_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP
12 :
13 : #include <boost/corosio/detail/platform.hpp>
14 :
15 : #if BOOST_COROSIO_HAS_SELECT
16 :
17 : #include <boost/corosio/detail/config.hpp>
18 : #include <boost/capy/ex/execution_context.hpp>
19 : #include <boost/corosio/detail/tcp_acceptor_service.hpp>
20 :
21 : #include <boost/corosio/native/detail/select/select_tcp_acceptor.hpp>
22 : #include <boost/corosio/native/detail/select/select_tcp_service.hpp>
23 : #include <boost/corosio/native/detail/select/select_scheduler.hpp>
24 : #include <boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp>
25 : #include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
26 :
27 : #include <memory>
28 : #include <mutex>
29 : #include <utility>
30 :
31 : #include <errno.h>
32 : #include <fcntl.h>
33 : #include <netinet/in.h>
34 : #include <sys/select.h>
35 : #include <sys/socket.h>
36 : #include <unistd.h>
37 :
38 : namespace boost::corosio::detail {
39 :
40 : /** select acceptor service implementation.
41 :
42 : Inherits from tcp_acceptor_service to enable runtime polymorphism.
43 : Uses key_type = tcp_acceptor_service for service lookup.
44 : */
45 : class BOOST_COROSIO_DECL select_tcp_acceptor_service final
46 : : public reactor_acceptor_service<
47 : select_tcp_acceptor_service,
48 : tcp_acceptor_service,
49 : select_scheduler,
50 : select_tcp_acceptor,
51 : select_tcp_service>
52 : {
53 : using base_type = reactor_acceptor_service<
54 : select_tcp_acceptor_service,
55 : tcp_acceptor_service,
56 : select_scheduler,
57 : select_tcp_acceptor,
58 : select_tcp_service>;
59 : friend base_type;
60 :
61 : public:
62 : explicit select_tcp_acceptor_service(capy::execution_context& ctx);
63 : ~select_tcp_acceptor_service() override;
64 :
65 : std::error_code open_acceptor_socket(
66 : tcp_acceptor::implementation& impl,
67 : int family,
68 : int type,
69 : int protocol) override;
70 : std::error_code
71 : bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep) override;
72 : std::error_code
73 : listen_acceptor(tcp_acceptor::implementation& impl, int backlog) override;
74 : };
75 :
76 : inline void
77 MIS 0 : select_accept_op::cancel() noexcept
78 : {
79 0 : if (acceptor_impl_)
80 0 : acceptor_impl_->cancel_single_op(*this);
81 : else
82 0 : request_cancel();
83 0 : }
84 :
85 : inline void
86 HIT 3153 : select_accept_op::operator()()
87 : {
88 3153 : complete_accept_op<select_tcp_socket>(*this);
89 3153 : }
90 :
91 65 : inline select_tcp_acceptor::select_tcp_acceptor(
92 65 : select_tcp_acceptor_service& svc) noexcept
93 65 : : reactor_acceptor(svc)
94 : {
95 65 : }
96 :
97 : inline std::coroutine_handle<>
98 3153 : select_tcp_acceptor::accept(
99 : std::coroutine_handle<> h,
100 : capy::executor_ref ex,
101 : std::stop_token token,
102 : std::error_code* ec,
103 : io_object::implementation** impl_out)
104 : {
105 3153 : auto& op = acc_;
106 3153 : op.reset();
107 3153 : op.h = h;
108 3153 : op.ex = ex;
109 3153 : op.ec_out = ec;
110 3153 : op.impl_out = impl_out;
111 3153 : op.fd = fd_;
112 3153 : op.start(token, this);
113 :
114 3153 : sockaddr_storage peer_storage{};
115 : socklen_t addrlen;
116 : int accepted;
117 : do
118 : {
119 3153 : addrlen = sizeof(peer_storage);
120 : accepted =
121 3153 : ::accept(fd_, reinterpret_cast<sockaddr*>(&peer_storage), &addrlen);
122 : }
123 3153 : while (accepted < 0 && errno == EINTR);
124 :
125 3153 : if (accepted >= 0)
126 : {
127 3 : if (accepted >= FD_SETSIZE)
128 : {
129 MIS 0 : ::close(accepted);
130 0 : op.complete(EINVAL, 0);
131 0 : op.impl_ptr = shared_from_this();
132 0 : svc_.post(&op);
133 0 : return std::noop_coroutine();
134 : }
135 :
136 HIT 3 : int flags = ::fcntl(accepted, F_GETFL, 0);
137 3 : if (flags == -1)
138 : {
139 MIS 0 : int err = errno;
140 0 : ::close(accepted);
141 0 : op.complete(err, 0);
142 0 : op.impl_ptr = shared_from_this();
143 0 : svc_.post(&op);
144 0 : return std::noop_coroutine();
145 : }
146 :
147 HIT 3 : if (::fcntl(accepted, F_SETFL, flags | O_NONBLOCK) == -1)
148 : {
149 MIS 0 : int err = errno;
150 0 : ::close(accepted);
151 0 : op.complete(err, 0);
152 0 : op.impl_ptr = shared_from_this();
153 0 : svc_.post(&op);
154 0 : return std::noop_coroutine();
155 : }
156 :
157 HIT 3 : if (::fcntl(accepted, F_SETFD, FD_CLOEXEC) == -1)
158 : {
159 MIS 0 : int err = errno;
160 0 : ::close(accepted);
161 0 : op.complete(err, 0);
162 0 : op.impl_ptr = shared_from_this();
163 0 : svc_.post(&op);
164 0 : return std::noop_coroutine();
165 : }
166 :
167 : #ifdef SO_NOSIGPIPE
168 : {
169 : int one = 1;
170 : ::setsockopt(
171 : accepted, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
172 : }
173 : #endif
174 :
175 : {
176 HIT 3 : std::lock_guard lock(desc_state_.mutex);
177 3 : desc_state_.read_ready = false;
178 3 : }
179 :
180 3 : if (svc_.scheduler().try_consume_inline_budget())
181 : {
182 MIS 0 : auto* socket_svc = svc_.stream_service();
183 0 : if (socket_svc)
184 : {
185 : auto& impl =
186 0 : static_cast<select_tcp_socket&>(*socket_svc->construct());
187 0 : impl.set_socket(accepted);
188 :
189 0 : impl.desc_state_.fd = accepted;
190 : {
191 0 : std::lock_guard lock(impl.desc_state_.mutex);
192 0 : impl.desc_state_.read_op = nullptr;
193 0 : impl.desc_state_.write_op = nullptr;
194 0 : impl.desc_state_.connect_op = nullptr;
195 0 : }
196 0 : socket_svc->scheduler().register_descriptor(
197 : accepted, &impl.desc_state_);
198 :
199 0 : impl.set_endpoints(
200 : local_endpoint_, from_sockaddr(peer_storage));
201 :
202 0 : *ec = {};
203 0 : if (impl_out)
204 0 : *impl_out = &impl;
205 : }
206 : else
207 : {
208 0 : ::close(accepted);
209 0 : *ec = make_err(ENOENT);
210 0 : if (impl_out)
211 0 : *impl_out = nullptr;
212 : }
213 0 : op.cont_op.cont.h = h;
214 0 : return dispatch_coro(ex, op.cont_op.cont);
215 : }
216 :
217 HIT 3 : op.accepted_fd = accepted;
218 3 : op.peer_storage = peer_storage;
219 3 : op.peer_addrlen = addrlen;
220 3 : op.complete(0, 0);
221 3 : op.impl_ptr = shared_from_this();
222 3 : svc_.post(&op);
223 3 : return std::noop_coroutine();
224 : }
225 :
226 3150 : if (errno == EAGAIN || errno == EWOULDBLOCK)
227 : {
228 3150 : op.impl_ptr = shared_from_this();
229 3150 : svc_.work_started();
230 :
231 3150 : std::lock_guard lock(desc_state_.mutex);
232 3150 : bool io_done = false;
233 3150 : if (desc_state_.read_ready)
234 : {
235 MIS 0 : desc_state_.read_ready = false;
236 0 : op.perform_io();
237 0 : io_done = (op.errn != EAGAIN && op.errn != EWOULDBLOCK);
238 0 : if (!io_done)
239 0 : op.errn = 0;
240 : }
241 :
242 HIT 3150 : if (io_done || op.cancelled.load(std::memory_order_acquire))
243 : {
244 MIS 0 : svc_.post(&op);
245 0 : svc_.work_finished();
246 : }
247 : else
248 : {
249 HIT 3150 : desc_state_.read_op = &op;
250 : }
251 3150 : return std::noop_coroutine();
252 3150 : }
253 :
254 MIS 0 : op.complete(errno, 0);
255 0 : op.impl_ptr = shared_from_this();
256 0 : svc_.post(&op);
257 0 : return std::noop_coroutine();
258 : }
259 :
260 : inline void
261 HIT 2 : select_tcp_acceptor::cancel() noexcept
262 : {
263 2 : do_cancel();
264 2 : }
265 :
266 : inline void
267 254 : select_tcp_acceptor::close_socket() noexcept
268 : {
269 254 : do_close_socket();
270 254 : }
271 :
272 229 : inline select_tcp_acceptor_service::select_tcp_acceptor_service(
273 229 : capy::execution_context& ctx)
274 229 : : base_type(ctx)
275 : {
276 229 : auto* svc = ctx_.find_service<detail::tcp_service>();
277 229 : stream_svc_ = svc ? dynamic_cast<select_tcp_service*>(svc) : nullptr;
278 229 : }
279 :
280 458 : inline select_tcp_acceptor_service::~select_tcp_acceptor_service() {}
281 :
282 : inline std::error_code
283 62 : select_tcp_acceptor_service::open_acceptor_socket(
284 : tcp_acceptor::implementation& impl, int family, int type, int protocol)
285 : {
286 62 : auto* select_impl = static_cast<select_tcp_acceptor*>(&impl);
287 62 : select_impl->close_socket();
288 :
289 62 : int fd = ::socket(family, type, protocol);
290 62 : if (fd < 0)
291 MIS 0 : return make_err(errno);
292 :
293 HIT 62 : int flags = ::fcntl(fd, F_GETFL, 0);
294 62 : if (flags == -1)
295 : {
296 MIS 0 : int errn = errno;
297 0 : ::close(fd);
298 0 : return make_err(errn);
299 : }
300 HIT 62 : if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
301 : {
302 MIS 0 : int errn = errno;
303 0 : ::close(fd);
304 0 : return make_err(errn);
305 : }
306 HIT 62 : if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
307 : {
308 MIS 0 : int errn = errno;
309 0 : ::close(fd);
310 0 : return make_err(errn);
311 : }
312 :
313 HIT 62 : if (fd >= FD_SETSIZE)
314 : {
315 MIS 0 : ::close(fd);
316 0 : return make_err(EMFILE);
317 : }
318 :
319 HIT 62 : if (family == AF_INET6)
320 : {
321 8 : int val = 0; // dual-stack default
322 8 : ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
323 : }
324 :
325 : #ifdef SO_NOSIGPIPE
326 : {
327 : int nosig = 1;
328 : ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &nosig, sizeof(nosig));
329 : }
330 : #endif
331 :
332 62 : select_impl->fd_ = fd;
333 :
334 : // Set up descriptor state but do NOT register with reactor yet
335 : // (registration happens in do_listen via reactor_acceptor base)
336 62 : select_impl->desc_state_.fd = fd;
337 : {
338 62 : std::lock_guard lock(select_impl->desc_state_.mutex);
339 62 : select_impl->desc_state_.read_op = nullptr;
340 62 : }
341 :
342 62 : return {};
343 : }
344 :
345 : inline std::error_code
346 61 : select_tcp_acceptor_service::bind_acceptor(
347 : tcp_acceptor::implementation& impl, endpoint ep)
348 : {
349 61 : return static_cast<select_tcp_acceptor*>(&impl)->do_bind(ep);
350 : }
351 :
352 : inline std::error_code
353 58 : select_tcp_acceptor_service::listen_acceptor(
354 : tcp_acceptor::implementation& impl, int backlog)
355 : {
356 58 : return static_cast<select_tcp_acceptor*>(&impl)->do_listen(backlog);
357 : }
358 :
359 : } // namespace boost::corosio::detail
360 :
361 : #endif // BOOST_COROSIO_HAS_SELECT
362 :
363 : #endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_ACCEPTOR_SERVICE_HPP
|