TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
12 : #define BOOST_COROSIO_TCP_ACCEPTOR_HPP
13 :
14 : #include <boost/corosio/detail/config.hpp>
15 : #include <boost/corosio/detail/except.hpp>
16 : #include <boost/corosio/detail/native_handle.hpp>
17 : #include <boost/corosio/detail/op_base.hpp>
18 : #include <boost/corosio/wait_type.hpp>
19 : #include <boost/corosio/io/io_object.hpp>
20 : #include <boost/capy/io_result.hpp>
21 : #include <boost/corosio/endpoint.hpp>
22 : #include <boost/corosio/tcp.hpp>
23 : #include <boost/corosio/tcp_socket.hpp>
24 : #include <boost/capy/ex/executor_ref.hpp>
25 : #include <boost/capy/ex/execution_context.hpp>
26 : #include <boost/capy/ex/io_env.hpp>
27 : #include <boost/capy/concept/executor.hpp>
28 :
29 : #include <system_error>
30 :
31 : #include <concepts>
32 : #include <coroutine>
33 : #include <cstddef>
34 : #include <stop_token>
35 : #include <type_traits>
36 :
37 : namespace boost::corosio {
38 :
39 : /** An asynchronous TCP acceptor for coroutine I/O.
40 :
41 : This class provides asynchronous TCP accept operations that return
42 : awaitable types. The acceptor binds to a local endpoint and listens
43 : for incoming connections.
44 :
45 : Each accept operation participates in the affine awaitable protocol,
46 : ensuring coroutines resume on the correct executor.
47 :
48 : @par Thread Safety
49 : Distinct objects: Safe.@n
50 : Shared objects: Unsafe. An acceptor must not have concurrent accept
51 : operations.
52 :
53 : @par Semantics
54 : Wraps the platform TCP listener. Operations dispatch to
55 : OS accept APIs via the io_context reactor.
56 :
57 : @par Example
58 : @code
59 : // Convenience constructor: open + configure + bind + listen
60 : io_context ioc;
61 : tcp_acceptor acc( ioc, endpoint( 8080 ) );
62 :
63 : tcp_socket peer( ioc );
64 : auto [ec] = co_await acc.accept( peer );
65 : if ( !ec ) {
66 : // peer is now a connected socket
67 : auto [ec2, n] = co_await peer.read_some( buf );
68 : }
69 : @endcode
70 :
71 : @par Example
72 : @code
73 : // Fine-grained setup
74 : tcp_acceptor acc( ioc );
75 : if ( auto ec = acc.open( tcp::v6() ) )
76 : return ec;
77 : acc.set_option( socket_option::reuse_address( true ) );
78 : acc.set_option( socket_option::v6_only( true ) );
79 : if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
80 : return ec;
81 : if ( auto ec = acc.listen() )
82 : return ec;
83 : @endcode
84 : */
85 : class BOOST_COROSIO_DECL tcp_acceptor : public io_object
86 : {
87 : struct wait_awaitable
88 : : detail::void_op_base<wait_awaitable>
89 : {
90 : tcp_acceptor& acc_;
91 : wait_type w_;
92 :
93 HIT 19 : wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
94 19 : : acc_(acc), w_(w) {}
95 :
96 17 : std::coroutine_handle<> dispatch(
97 : std::coroutine_handle<> h, capy::executor_ref ex) const
98 : {
99 17 : 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 7002 : accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
112 7002 : : acc_(acc)
113 7002 : , peer_(peer)
114 : {
115 7002 : }
116 :
117 7002 : bool await_ready() const noexcept
118 : {
119 : // A pre-set ec_ means the initiator failed before
120 : // dispatch (e.g. a closed object).
121 7002 : return static_cast<bool>(ec_) || token_.stop_requested();
122 : }
123 :
124 7000 : [[nodiscard]] capy::io_result<> await_resume() const noexcept
125 : {
126 7000 : if (token_.stop_requested())
127 27 : return {make_error_code(std::errc::operation_canceled)};
128 :
129 6973 : if (!ec_ && peer_impl_)
130 6962 : peer_.h_.reset(peer_impl_);
131 6973 : return {ec_};
132 : }
133 :
134 7000 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
135 : -> std::coroutine_handle<>
136 : {
137 7000 : token_ = env->stop_token;
138 21000 : return acc_.get().accept(
139 21000 : 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 31 : explicit accept_value_awaitable(tcp_acceptor& acc) noexcept
151 31 : : acc_(acc)
152 : {
153 31 : }
154 :
155 31 : bool await_ready() const noexcept
156 : {
157 : // A pre-set ec_ means the initiator failed before
158 : // dispatch (e.g. a closed object).
159 31 : return static_cast<bool>(ec_) || token_.stop_requested();
160 : }
161 :
162 31 : [[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 31 : if (token_.stop_requested())
167 MIS 0 : return {make_error_code(std::errc::operation_canceled),
168 0 : tcp_socket()};
169 :
170 HIT 31 : if (ec_ || !peer_impl_)
171 4 : return {ec_, tcp_socket()};
172 :
173 27 : tcp_socket peer(acc_.context());
174 27 : peer.h_.reset(peer_impl_);
175 27 : return {ec_, std::move(peer)};
176 27 : }
177 :
178 27 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
179 : -> std::coroutine_handle<>
180 : {
181 27 : token_ = env->stop_token;
182 81 : return acc_.get().accept(
183 81 : 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 1 : explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
234 : {
235 1 : }
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 9 : 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 3 : tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
279 : {
280 3 : if (this != &other)
281 : {
282 3 : close();
283 3 : h_ = std::move(other.h_);
284 : }
285 3 : 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 9926 : bool is_open() const noexcept
371 : {
372 9926 : 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 7002 : [[nodiscard]] auto accept(tcp_socket& peer)
415 : {
416 7002 : accept_awaitable aw(*this, peer);
417 7002 : if (!is_open())
418 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
419 7002 : 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 31 : [[nodiscard]] auto accept()
461 : {
462 31 : accept_value_awaitable aw(*this);
463 31 : if (!is_open())
464 4 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
465 31 : 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 19 : [[nodiscard]] auto wait(wait_type w)
492 : {
493 19 : wait_awaitable aw(*this, w);
494 19 : if (!is_open())
495 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
496 19 : 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 404 : void set_option(Option const& opt)
610 : {
611 404 : if (!is_open())
612 2 : detail::throw_system_error(
613 4 : make_error_code(std::errc::bad_file_descriptor),
614 : "tcp_acceptor::set_option");
615 402 : std::error_code ec = get().set_option(
616 : Option::level(), Option::name(), opt.data(), opt.size());
617 402 : if (ec)
618 2 : detail::throw_system_error(ec, "tcp_acceptor::set_option");
619 400 : }
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 10 : Option get_option() const
637 : {
638 10 : if (!is_open())
639 2 : detail::throw_system_error(
640 4 : make_error_code(std::errc::bad_file_descriptor),
641 : "tcp_acceptor::get_option");
642 8 : Option opt{};
643 8 : std::size_t sz = opt.size();
644 : std::error_code ec =
645 8 : get().get_option(Option::level(), Option::name(), opt.data(), &sz);
646 8 : if (ec)
647 2 : detail::throw_system_error(ec, "tcp_acceptor::get_option");
648 6 : opt.resize(sz);
649 6 : 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 27 : 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 11 : reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
734 : {
735 11 : if (impl)
736 11 : peer.h_.reset(impl);
737 11 : }
738 :
739 : private:
740 17766 : inline implementation& get() const noexcept
741 : {
742 17766 : return *static_cast<implementation*>(h_.get());
743 : }
744 : };
745 :
746 : } // namespace boost::corosio
747 :
748 : #endif
|