include/boost/corosio/tcp_acceptor.hpp

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