include/boost/corosio/tcp_acceptor.hpp

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