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