include/boost/corosio/tcp_acceptor.hpp

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