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 25x 100.0% 100.0% boost::corosio::tcp_acceptor::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :96 22x 100.0% 80.0% boost::corosio::tcp_acceptor::accept_awaitable::accept_awaitable(boost::corosio::tcp_acceptor&, boost::corosio::tcp_socket&) :111 10119x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_ready() const :117 10119x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_resume() const :124 10117x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :134 10116x 100.0% 82.0% boost::corosio::tcp_acceptor::accept_value_awaitable::accept_value_awaitable(boost::corosio::tcp_acceptor&) :150 46x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_ready() const :155 46x 100.0% 100.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_resume() :162 46x 80.0% 80.0% boost::corosio::tcp_acceptor::accept_value_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :178 40x 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 13x 100.0% 100.0% boost::corosio::tcp_acceptor::operator=(boost::corosio::tcp_acceptor&&) :278 4x 100.0% 100.0% boost::corosio::tcp_acceptor::is_open() const :370 25596x 100.0% 100.0% boost::corosio::tcp_acceptor::accept(boost::corosio::tcp_socket&) :414 10119x 100.0% 100.0% boost::corosio::tcp_acceptor::accept() :460 46x 100.0% 100.0% boost::corosio::tcp_acceptor::wait(boost::corosio::wait_type) :491 25x 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 3x 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 18x 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 2162x 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 3x 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 6x 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 3x 72.7% 78.0% boost::corosio::socket_option::reuse_address boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::reuse_address>() const :636 6x 90.9% 94.0% boost::corosio::socket_option::reuse_port boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::reuse_port>() const :636 3x 72.7% 78.0% boost::corosio::socket_option::v6_only boost::corosio::tcp_acceptor::get_option<boost::corosio::socket_option::v6_only>() const :636 3x 63.6% 67.0% boost::corosio::tcp_acceptor::tcp_acceptor(boost::corosio::io_object::handle) :729 40x 100.0% 100.0% boost::corosio::tcp_acceptor::reset_peer_impl(boost::corosio::tcp_socket&, boost::corosio::io_object::implementation*) :733 16x 100.0% 100.0% boost::corosio::tcp_acceptor::get() const :740 40141x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
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 25x wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
94 25x : acc_(acc), w_(w) {}
95
96 22x std::coroutine_handle<> dispatch(
97 std::coroutine_handle<> h, capy::executor_ref ex) const
98 {
99 22x 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 10119x accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
112 10119x : acc_(acc)
113 10119x , peer_(peer)
114 {
115 10119x }
116
117 10119x bool await_ready() const noexcept
118 {
119 // A pre-set ec_ means the initiator failed before
120 // dispatch (e.g. a closed object).
121 10119x return static_cast<bool>(ec_) || token_.stop_requested();
122 }
123
124 10117x [[nodiscard]] capy::io_result<> await_resume() const noexcept
125 {
126 10117x if (token_.stop_requested())
127 39x return {make_error_code(std::errc::operation_canceled)};
128
129 10078x if (!ec_ && peer_impl_)
130 10063x peer_.h_.reset(peer_impl_);
131 10078x return {ec_};
132 }
133
134 10116x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
135 -> std::coroutine_handle<>
136 {
137 10116x token_ = env->stop_token;
138 30348x return acc_.get().accept(
139 30348x 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 46x explicit accept_value_awaitable(tcp_acceptor& acc) noexcept
151 46x : acc_(acc)
152 {
153 46x }
154
155 46x bool await_ready() const noexcept
156 {
157 // A pre-set ec_ means the initiator failed before
158 // dispatch (e.g. a closed object).
159 46x return static_cast<bool>(ec_) || token_.stop_requested();
160 }
161
162 46x [[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 46x if (token_.stop_requested())
167 return {make_error_code(std::errc::operation_canceled),
168 tcp_socket()};
169
170 46x if (ec_ || !peer_impl_)
171 6x return {ec_, tcp_socket()};
172
173 40x tcp_socket peer(acc_.context());
174 40x peer.h_.reset(peer_impl_);
175 40x return {ec_, std::move(peer)};
176 40x }
177
178 40x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
179 -> std::coroutine_handle<>
180 {
181 40x token_ = env->stop_token;
182 120x return acc_.get().accept(
183 120x 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 13x 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 4x tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
279 {
280 4x if (this != &other)
281 {
282 4x close();
283 4x h_ = std::move(other.h_);
284 }
285 4x 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 25596x bool is_open() const noexcept
371 {
372 25596x 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 10119x [[nodiscard]] auto accept(tcp_socket& peer)
415 {
416 10119x accept_awaitable aw(*this, peer);
417 10119x if (!is_open())
418 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
419 10119x 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 46x [[nodiscard]] auto accept()
461 {
462 46x accept_value_awaitable aw(*this);
463 46x if (!is_open())
464 6x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
465 46x 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 25x [[nodiscard]] auto wait(wait_type w)
492 {
493 25x wait_awaitable aw(*this, w);
494 25x if (!is_open())
495 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
496 25x 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 2192x void set_option(Option const& opt)
610 {
611 2192x if (!is_open())
612 3x detail::throw_system_error(
613 6x make_error_code(std::errc::bad_file_descriptor),
614 "tcp_acceptor::set_option");
615 2189x std::error_code ec = get().set_option(
616 Option::level(), Option::name(), opt.data(), opt.size());
617 2189x if (ec)
618 3x detail::throw_system_error(ec, "tcp_acceptor::set_option");
619 2186x }
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 15x Option get_option() const
637 {
638 15x if (!is_open())
639 3x detail::throw_system_error(
640 6x make_error_code(std::errc::bad_file_descriptor),
641 "tcp_acceptor::get_option");
642 12x Option opt{};
643 12x std::size_t sz = opt.size();
644 std::error_code ec =
645 12x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
646 12x if (ec)
647 3x detail::throw_system_error(ec, "tcp_acceptor::get_option");
648 9x opt.resize(sz);
649 9x 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 40x 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 16x reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
734 {
735 16x if (impl)
736 16x peer.h_.reset(impl);
737 16x }
738
739 private:
740 40141x inline implementation& get() const noexcept
741 {
742 40141x return *static_cast<implementation*>(h_.get());
743 }
744 };
745
746 } // namespace boost::corosio
747
748 #endif
749