include/boost/corosio/tcp_acceptor.hpp

100.0% Lines (90/0/90) 100.0% List of functions (31/0/31)
tcp_acceptor.hpp
f(x) Functions (31)
Function Calls Lines Blocks
boost::corosio::tcp_acceptor::wait_awaitable::wait_awaitable(boost::corosio::tcp_acceptor&, boost::corosio::wait_type) :72 28x 100.0% 100.0% boost::corosio::tcp_acceptor::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :75 26x 100.0% 80.0% boost::corosio::tcp_acceptor::accept_awaitable::accept_awaitable(boost::corosio::tcp_acceptor&, boost::corosio::tcp_socket&) :90 4344x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_ready() const :96 4344x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_resume() const :103 4334x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :113 4342x 100.0% 82.0% boost::corosio::tcp_acceptor::accept_value_awaitable::accept_value_awaitable(boost::corosio::tcp_acceptor&) :129 33x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_ready() const :134 33x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_resume() :141 33x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :157 29x 100.0% 82.0% boost::corosio::tcp_acceptor::tcp_acceptor<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :212 1x 100.0% 100.0% boost::corosio::tcp_acceptor::tcp_acceptor(boost::corosio::tcp_acceptor&&) :242 9x 100.0% 100.0% boost::corosio::tcp_acceptor::operator=(boost::corosio::tcp_acceptor&&) :257 3x 100.0% 100.0% boost::corosio::tcp_acceptor::is_open() const :341 8577x 100.0% 100.0% boost::corosio::tcp_acceptor::accept(boost::corosio::tcp_socket&) :379 4344x 100.0% 100.0% boost::corosio::tcp_acceptor::accept() :420 33x 100.0% 100.0% boost::corosio::tcp_acceptor::wait(boost::corosio::wait_type) :451 28x 100.0% 100.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::native_socket_option::boolean<1, 15> >(boost::corosio::native_socket_option::boolean<1, 15> const&) :561 2x 62.5% 75.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::native_socket_option::boolean<1, 2> >(boost::corosio::native_socket_option::boolean<1, 2> const&) :561 12x 62.5% 75.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::socket_option::reuse_address>(boost::corosio::socket_option::reuse_address const&) :561 570x 100.0% 94.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::socket_option::reuse_port>(boost::corosio::socket_option::reuse_port const&) :561 2x 62.5% 75.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :561 7x 62.5% 75.0% void boost::corosio::tcp_acceptor::set_option<boost::corosio::socket_option::v6_only>(boost::corosio::socket_option::v6_only const&) :561 4x 75.0% 81.0% boost::corosio::native_socket_option::boolean<1, 15> boost::corosio::tcp_acceptor::get_option<boost::corosio::native_socket_option::boolean<1, 15> >() const :586 2x 72.7% 78.0% boost::corosio::socket_option::reuse_address boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::reuse_address>() const :586 10x 100.0% 94.0% boost::corosio::socket_option::reuse_port boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::reuse_port>() const :586 2x 72.7% 78.0% boost::corosio::socket_option::send_buffer_size boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::send_buffer_size>() const :586 7x 72.7% 78.0% boost::corosio::socket_option::v6_only boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::v6_only>() const :586 2x 63.6% 67.0% boost::corosio::tcp_acceptor::tcp_acceptor(boost::corosio::io_object::handle) :679 33x 100.0% 100.0% boost::corosio::tcp_acceptor::reset_peer_impl(boost::corosio::tcp_socket&, boost::corosio::io_object::implementation*) :683 15x 100.0% 100.0% boost::corosio::tcp_acceptor::get() const :690 14156x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
13 #define BOOST_COROSIO_TCP_ACCEPTOR_HPP
14
15 #include <boost/corosio/detail/config.hpp>
16 #include <boost/corosio/detail/except.hpp>
17 #include <boost/corosio/detail/native_handle.hpp>
18 #include <boost/corosio/detail/op_base.hpp>
19 #include <boost/corosio/wait_type.hpp>
20 #include <boost/corosio/io/io_object.hpp>
21 #include <boost/capy/io_result.hpp>
22 #include <boost/corosio/endpoint.hpp>
23 #include <boost/corosio/tcp.hpp>
24 #include <boost/corosio/tcp_socket.hpp>
25 #include <boost/capy/ex/executor_ref.hpp>
26 #include <boost/capy/ex/execution_context.hpp>
27 #include <boost/capy/ex/io_env.hpp>
28 #include <boost/capy/concept/executor.hpp>
29
30 #include <system_error>
31
32 #include <concepts>
33 #include <coroutine>
34 #include <cstddef>
35 #include <stop_token>
36 #include <type_traits>
37
38 namespace boost::corosio {
39
40 /** An asynchronous TCP acceptor for coroutine I/O.
41
42 This class provides asynchronous TCP accept operations that return
43 awaitable types. The acceptor binds to a local endpoint and listens
44 for incoming connections.
45
46 Each accept operation participates in the affine awaitable protocol,
47 ensuring coroutines resume on the correct executor.
48
49 @par Thread Safety
50 Distinct objects: Safe.@n
51 Shared objects: Unsafe. An acceptor must not have concurrent accept
52 operations.
53
54 @par Semantics
55 Wraps the platform TCP listener. Operations dispatch to
56 OS accept APIs via the io_context reactor.
57
58 @par Example
59 @par !example convenience_construction
60
61 @par Example
62 @par !example fine_grained_setup
63 */
64 class BOOST_COROSIO_DECL tcp_acceptor : public io_object
65 {
66 struct wait_awaitable
67 : detail::void_op_base<wait_awaitable>
68 {
69 tcp_acceptor& acc_;
70 wait_type w_;
71
72 28x wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
73 28x : acc_(acc), w_(w) {}
74
75 26x std::coroutine_handle<> dispatch(
76 std::coroutine_handle<> h, capy::executor_ref ex) const
77 {
78 26x return acc_.get().wait(h, ex, w_, token_, &ec_);
79 }
80 };
81
82 struct accept_awaitable
83 {
84 tcp_acceptor& acc_;
85 tcp_socket& peer_;
86 std::stop_token token_;
87 mutable std::error_code ec_;
88 mutable io_object::implementation* peer_impl_ = nullptr;
89
90 4344x accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
91 4344x : acc_(acc)
92 4344x , peer_(peer)
93 {
94 4344x }
95
96 4344x bool await_ready() const noexcept
97 {
98 // A pre-set ec_ means the initiator failed before
99 // dispatch (e.g. a closed object).
100 4344x return static_cast<bool>(ec_) || token_.stop_requested();
101 }
102
103 4334x [[nodiscard]] capy::io_result<> await_resume() const noexcept
104 {
105 4334x if (token_.stop_requested())
106 66x return {make_error_code(std::errc::operation_canceled)};
107
108 4268x if (!ec_ && peer_impl_)
109 4239x peer_.h_.reset(peer_impl_);
110 4268x return {ec_};
111 }
112
113 4342x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
114 -> std::coroutine_handle<>
115 {
116 4342x token_ = env->stop_token;
117 13026x return acc_.get().accept(
118 13026x h, env->executor, token_, &ec_, &peer_impl_);
119 }
120 };
121
122 struct accept_value_awaitable
123 {
124 tcp_acceptor& acc_;
125 std::stop_token token_;
126 mutable std::error_code ec_;
127 mutable io_object::implementation* peer_impl_ = nullptr;
128
129 33x explicit accept_value_awaitable(tcp_acceptor& acc) noexcept
130 33x : acc_(acc)
131 {
132 33x }
133
134 33x bool await_ready() const noexcept
135 {
136 // A pre-set ec_ means the initiator failed before
137 // dispatch (e.g. a closed object).
138 33x return static_cast<bool>(ec_) || token_.stop_requested();
139 }
140
141 33x [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept
142 {
143 // The peer is built only on success: error paths must not
144 // touch acc_.context(), which a moved-from acceptor lacks.
145 33x if (token_.stop_requested())
146 2x return {make_error_code(std::errc::operation_canceled),
147 2x tcp_socket()};
148
149 31x if (ec_ || !peer_impl_)
150 4x return {ec_, tcp_socket()};
151
152 27x tcp_socket peer(acc_.context());
153 27x peer.h_.reset(peer_impl_);
154 27x return {ec_, std::move(peer)};
155 27x }
156
157 29x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
158 -> std::coroutine_handle<>
159 {
160 29x token_ = env->stop_token;
161 87x return acc_.get().accept(
162 87x h, env->executor, token_, &ec_, &peer_impl_);
163 }
164 };
165
166 public:
167 /** Destructor.
168
169 Closes the acceptor if open, cancelling any pending operations.
170 */
171 ~tcp_acceptor() override;
172
173 /** Construct an acceptor from an execution context.
174
175 @param ctx The execution context that will own this acceptor.
176 */
177 explicit tcp_acceptor(capy::execution_context& ctx);
178
179 /** Convenience constructor: open + configure + bind + listen.
180
181 Creates a fully-bound listening acceptor in a single
182 expression, throwing the codes the piecewise `open()` +
183 `set_option()` + `bind()` + `listen()` path reports. The
184 address family is deduced from @p ep.
185
186 Before binding, the constructor configures address reuse so
187 a server can rebind its port immediately after a restart:
188 `SO_REUSEADDR` on POSIX, `SO_EXCLUSIVEADDRUSE` on Windows
189 ( where `SO_REUSEADDR` instead grants other sockets
190 bind-over rights ). A second listener on an occupied
191 endpoint therefore throws `errc::address_in_use` on every
192 platform.
193
194 @param ctx The execution context that will own this acceptor.
195 @param ep The local endpoint to bind to.
196 @param backlog The maximum pending connection queue length.
197
198 @throws std::system_error on open, configuration, bind, or
199 listen failure.
200 */
201 tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128);
202
203 /** Construct an acceptor from an executor.
204
205 The acceptor is associated with the executor's context.
206
207 @param ex The executor whose context will own the acceptor.
208 */
209 template<class Ex>
210 requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
211 capy::Executor<Ex>
212 1x explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
213 {
214 1x }
215
216 /** Convenience constructor from an executor.
217
218 @param ex The executor whose context will own the acceptor.
219 @param ep The local endpoint to bind to.
220 @param backlog The maximum pending connection queue length.
221
222 @throws std::system_error on open, configuration, bind, or
223 listen failure.
224 */
225 template<class Ex>
226 requires capy::Executor<Ex>
227 tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
228 : tcp_acceptor(ex.context(), ep, backlog)
229 {
230 }
231
232 /** Move constructor.
233
234 Transfers ownership of the acceptor resources.
235
236 @param other The acceptor to move from.
237
238 @pre No awaitables returned by @p other's methods exist.
239 @pre The execution context associated with @p other must
240 outlive this acceptor.
241 */
242 9x tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
243
244 /** Move assignment operator.
245
246 Closes any existing acceptor and transfers ownership.
247
248 @param other The acceptor to move from.
249
250 @pre No awaitables returned by either `*this` or @p other's
251 methods exist.
252 @pre The execution context associated with @p other must
253 outlive this acceptor.
254
255 @return Reference to this acceptor.
256 */
257 3x tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
258 {
259 3x if (this != &other)
260 {
261 3x close();
262 3x h_ = std::move(other.h_);
263 }
264 3x return *this;
265 }
266
267 tcp_acceptor(tcp_acceptor const&) = delete;
268 tcp_acceptor& operator=(tcp_acceptor const&) = delete;
269
270 /** Create the acceptor socket without binding or listening.
271
272 Creates a TCP socket with dual-stack enabled for IPv6.
273 Does not set SO_REUSEADDR — call `set_option` explicitly
274 if needed.
275
276 If the acceptor is already open, this function is a no-op.
277
278 Failures such as descriptor exhaustion are normal runtime
279 conditions and are reported through the returned error code.
280
281 @param proto The protocol (IPv4 or IPv6). Defaults to
282 `tcp::v4()`.
283
284 @par Example
285 @par !example open
286
287 @see bind, listen
288
289 @return The error code, empty on success.
290 */
291 [[nodiscard]] std::error_code open(tcp proto = tcp::v4()) noexcept;
292
293 /** Bind to a local endpoint.
294
295 The acceptor must be open. Binds the socket to @p ep and
296 caches the resolved local endpoint (useful when port 0 is
297 used to request an ephemeral port).
298
299 @param ep The local endpoint to bind to.
300
301 @return An error code indicating success or the reason for
302 failure.
303
304 @par Error Conditions
305 @li `errc::address_in_use`: The endpoint is already in use.
306 @li `errc::address_not_available`: The address is not available
307 on any local interface.
308 @li `errc::permission_denied`: Insufficient privileges to bind
309 to the endpoint (e.g., privileged port).
310
311 A closed acceptor reports `errc::bad_file_descriptor`.
312 */
313 [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
314
315 /** Start listening for incoming connections.
316
317 The acceptor must be open and bound. Registers the acceptor
318 with the platform reactor.
319
320 @param backlog The maximum length of the queue of pending
321 connections. Defaults to 128.
322
323 @return An error code indicating success or the reason for
324 failure.
325
326 A closed acceptor reports `errc::bad_file_descriptor`.
327 */
328 [[nodiscard]] std::error_code listen(int backlog = 128) noexcept;
329
330 /** Close the acceptor.
331
332 Releases acceptor resources. Any pending operations complete
333 with `errc::operation_canceled`.
334 */
335 void close() noexcept;
336
337 /** Check if the acceptor is listening.
338
339 @return `true` if the acceptor is open and listening.
340 */
341 8577x bool is_open() const noexcept
342 {
343 8577x return h_ && get().is_open();
344 }
345
346 /** Initiate an asynchronous accept operation.
347
348 Accepts an incoming connection and initializes the provided
349 socket with the new connection. The acceptor must be listening
350 before calling this function.
351
352 The operation supports cancellation via `std::stop_token` through
353 the affine awaitable protocol. If the associated stop token is
354 triggered, the operation completes immediately with
355 `errc::operation_canceled`.
356
357 @param peer The socket to receive the accepted connection. Any
358 existing connection on this socket will be closed.
359
360 @return An awaitable that completes with `io_result<>`.
361 Returns success on successful accept, or an error code on
362 failure including:
363 - operation_canceled: Cancelled via stop_token or cancel().
364 Check `ec == cond::canceled` for portable comparison.
365
366 A closed acceptor completes with `errc::bad_file_descriptor`.
367
368 @par Preconditions
369 The peer socket must be associated with the same execution context.
370
371 Both this acceptor and @p peer must outlive the returned
372 awaitable.
373
374 @par Example
375 @par !example accept_into_a_reused_socket
376
377 @see accept()
378 */
379 4344x [[nodiscard]] auto accept(tcp_socket& peer)
380 {
381 4344x accept_awaitable aw(*this, peer);
382 4344x if (!is_open())
383 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
384 4344x return aw;
385 }
386
387 /** Initiate an asynchronous accept operation, returning the peer.
388
389 Accepts an incoming connection and returns a newly constructed
390 socket for it, associated with this acceptor's execution context.
391 The acceptor must be listening before calling this function.
392
393 The caller does not pre-construct the peer socket; the returned
394 socket shares this acceptor's execution context.
395
396 The operation supports cancellation via `std::stop_token` through
397 the affine awaitable protocol. If the associated stop token is
398 triggered, the operation completes immediately with
399 `errc::operation_canceled`.
400
401 @return An awaitable that completes with `io_result<tcp_socket>`.
402 On success the payload is the connected peer socket; on failure
403 (including cancellation) the error code is set and the payload
404 socket is unconnected. Errors include:
405 - operation_canceled: Cancelled via stop_token or cancel().
406 Check `ec == cond::canceled` for portable comparison.
407
408 A closed acceptor completes with `errc::bad_file_descriptor`.
409 On failure the returned socket is default-constructed and
410 may only be destroyed or assigned.
411
412 @par Preconditions
413 This acceptor must outlive the returned awaitable.
414
415 @par Example
416 @par !example accept_returning_a_new_socket
417
418 @see accept(tcp_socket&)
419 */
420 33x [[nodiscard]] auto accept()
421 {
422 33x accept_value_awaitable aw(*this);
423 33x if (!is_open())
424 4x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
425 33x return aw;
426 }
427
428 /** Wait for an incoming connection or readiness condition.
429
430 Suspends until the listen socket is ready in the
431 requested direction, or an error condition is reported.
432 For `wait_type::read`, completion signals that a
433 subsequent @ref accept will succeed without blocking; a
434 connection already queued when the wait begins completes
435 it immediately. No connection is consumed.
436
437 @note `wait_type::write` is not usable on an acceptor:
438 writability carries no meaning for a listening socket, so
439 the wait fails with `errc::operation_not_supported` on
440 every backend.
441
442 @param w The wait direction.
443
444 @return An awaitable that completes with `io_result<>`.
445
446 A closed acceptor completes with `errc::bad_file_descriptor`.
447
448 @par Preconditions
449 This acceptor must outlive the returned awaitable.
450 */
451 28x [[nodiscard]] auto wait(wait_type w)
452 {
453 28x wait_awaitable aw(*this, w);
454 28x if (!is_open())
455 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
456 28x return aw;
457 }
458
459 /** Cancel any pending asynchronous operations.
460
461 All outstanding operations complete with `errc::operation_canceled`.
462 Check `ec == cond::canceled` for portable comparison.
463 */
464 void cancel() noexcept;
465
466 /** Get the native socket handle.
467
468 Returns the underlying platform-specific socket descriptor.
469 On POSIX systems this is an `int` file descriptor.
470 On Windows this is a `SOCKET` handle.
471
472 @return The native socket handle, or -1/INVALID_SOCKET if not open.
473
474 @par Preconditions
475 None. May be called on closed acceptors.
476 */
477 native_handle_type native_handle() const noexcept;
478
479 /** Assign an existing native socket to this acceptor.
480
481 Adopts a listening socket created outside the library —
482 received from a service manager, inherited, or made natively —
483 and registers it with the backend. The socket must be a
484 listening stream socket in the `AF_INET` or `AF_INET6` family.
485 Adoption never alters the descriptor's flags or options: on
486 POSIX the fd must already be non-blocking, and on Windows the
487 socket must be overlapped-capable.
488
489 Adoption does not verify listen state; @ref accept reports the
490 error if the socket is not listening.
491
492 If this object is already open, pending operations complete
493 with `errc::operation_canceled` and the held socket is
494 closed before the new one is adopted.
495
496 @par Exception Safety
497 Strong guarantee on validation failure: the object is
498 unchanged. If backend registration fails, the object either
499 retains its previous socket or is left closed, depending on
500 the backend. In all failure cases the caller retains
501 ownership of `fd`.
502
503 @param fd The native socket to adopt. On success the object
504 owns it and will close it.
505
506 @return The error code, empty on success. Validation and
507 registration failures are normal runtime conditions when
508 adopting foreign descriptors.
509 */
510 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
511
512 /** Release ownership of the native socket handle.
513
514 Deregisters the socket from the backend and cancels pending
515 operations without closing the descriptor. The caller takes
516 ownership of the returned handle.
517
518 @return The native handle.
519
520 @throws std::system_error `errc::bad_file_descriptor` if the
521 acceptor is not open.
522
523 @post is_open() == false
524 */
525 native_handle_type release();
526
527 /** Get the local endpoint of the acceptor.
528
529 Returns the local address and port to which the acceptor is bound.
530 This is useful when binding to port 0 (ephemeral port) to discover
531 the OS-assigned port number. The endpoint is cached when bind()
532 is called.
533
534 @return The local endpoint, or a default endpoint (0.0.0.0:0) if
535 the acceptor is not open.
536
537 @par Thread Safety
538 The cached endpoint value is set during bind() and cleared
539 during close(). This function may be called concurrently with
540 accept operations, but must not be called concurrently with
541 bind() or close().
542 */
543 endpoint local_endpoint() const noexcept;
544
545 /** Set a socket option on the acceptor.
546
547 Applies a type-safe socket option to the underlying listening
548 socket. The socket must be open (via `open()` or `listen()`).
549 This is useful for setting options between `open()` and
550 `listen()`, such as `socket_option::reuse_port`.
551
552 @par Example
553 @par !example set_option
554
555 @param opt The option to set.
556
557 @throws std::system_error `errc::bad_file_descriptor` if the
558 acceptor is not open; otherwise thrown on failure.
559 */
560 template<class Option>
561 597x void set_option(Option const& opt)
562 {
563 597x if (!is_open())
564 2x detail::throw_system_error(
565 4x make_error_code(std::errc::bad_file_descriptor),
566 "tcp_acceptor::set_option");
567 595x std::error_code ec = get().set_option(
568 Option::level(), Option::name(), opt.data(), opt.size());
569 595x if (ec)
570 8x detail::throw_system_error(ec, "tcp_acceptor::set_option");
571 587x }
572
573 /** Get a socket option from the acceptor.
574
575 Retrieves the current value of a type-safe socket option.
576
577 @par Example
578 @par !example get_option
579
580 @return The current option value.
581
582 @throws std::system_error `errc::bad_file_descriptor` if the
583 acceptor is not open; otherwise thrown on failure.
584 */
585 template<class Option>
586 23x Option get_option() const
587 {
588 23x if (!is_open())
589 2x detail::throw_system_error(
590 4x make_error_code(std::errc::bad_file_descriptor),
591 "tcp_acceptor::get_option");
592 21x Option opt{};
593 21x std::size_t sz = opt.size();
594 std::error_code ec =
595 21x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
596 21x if (ec)
597 8x detail::throw_system_error(ec, "tcp_acceptor::get_option");
598 13x opt.resize(sz);
599 13x return opt;
600 }
601
602 /** Define backend hooks for TCP acceptor operations.
603
604 Platform backends derive from this to implement
605 accept, endpoint query, open-state checks, cancellation,
606 and socket-option management.
607 */
608 struct implementation : io_object::implementation
609 {
610 /// Initiate an asynchronous accept operation.
611 virtual std::coroutine_handle<> accept(
612 std::coroutine_handle<>,
613 capy::executor_ref,
614 std::stop_token,
615 std::error_code*,
616 io_object::implementation**) = 0;
617
618 /** Initiate an asynchronous wait for acceptor readiness.
619
620 Completes when the listen socket becomes ready for
621 the specified direction (typically `wait_type::read`
622 for an incoming connection), or an error condition is
623 reported. No connection is consumed.
624 */
625 virtual std::coroutine_handle<> wait(
626 std::coroutine_handle<> h,
627 capy::executor_ref ex,
628 wait_type w,
629 std::stop_token token,
630 std::error_code* ec) = 0;
631
632 /// Returns the cached local endpoint.
633 virtual endpoint local_endpoint() const noexcept = 0;
634
635 /// Return true if the acceptor has a kernel resource open.
636 virtual bool is_open() const noexcept = 0;
637
638 /// Return the native handle, or the platform sentinel if closed.
639 virtual native_handle_type native_handle() const noexcept = 0;
640
641 /// Release and return the native handle without closing.
642 virtual native_handle_type release_socket() noexcept = 0;
643
644 /** Cancel any pending asynchronous operations.
645
646 All outstanding operations complete with operation_canceled error.
647 */
648 virtual void cancel() noexcept = 0;
649
650 /** Set a socket option.
651
652 @param level The protocol level.
653 @param optname The option name.
654 @param data Pointer to the option value.
655 @param size Size of the option value in bytes.
656 @return Error code on failure, empty on success.
657 */
658 virtual std::error_code set_option(
659 int level,
660 int optname,
661 void const* data,
662 std::size_t size) noexcept = 0;
663
664 /** Get a socket option.
665
666 @param level The protocol level.
667 @param optname The option name.
668 @param data Pointer to receive the option value.
669 @param size On entry, the size of the buffer. On exit,
670 the size of the option value.
671 @return Error code on failure, empty on success.
672 */
673 virtual std::error_code
674 get_option(int level, int optname, void* data, std::size_t* size)
675 const noexcept = 0;
676 };
677
678 protected:
679 33x explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
680
681 /// Transfer accepted peer impl to the peer socket.
682 static void
683 15x reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
684 {
685 15x if (impl)
686 15x peer.h_.reset(impl);
687 15x }
688
689 private:
690 14156x inline implementation& get() const noexcept
691 {
692 14156x return *static_cast<implementation*>(h_.get());
693 }
694 };
695
696 } // namespace boost::corosio
697
698 #endif
699