include/boost/corosio/tcp_acceptor.hpp

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