TLA Line data 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/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 HIT 31 : wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
73 62 : : acc_(acc)
74 31 : , w_(w)
75 : {
76 31 : }
77 :
78 : friend detail::void_op_base<wait_awaitable>;
79 :
80 : tcp_acceptor& acc_;
81 : wait_type w_;
82 :
83 : std::coroutine_handle<>
84 27 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
85 : {
86 27 : 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 4654 : accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
101 9308 : : acc_(acc)
102 4654 : , peer_(peer)
103 : {
104 4654 : }
105 :
106 : std::coroutine_handle<>
107 4650 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
108 : {
109 13950 : return acc_.get().accept(
110 13950 : h, ex, this->token_, &this->ec_, &peer_impl_);
111 : }
112 :
113 : public:
114 4644 : [[nodiscard]] capy::io_result<> await_resume() const noexcept
115 : {
116 4644 : if (!this->ec_ && peer_impl_)
117 4549 : peer_.h_.reset(peer_impl_);
118 4644 : 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 31 : explicit accept_value_awaitable(tcp_acceptor& acc) noexcept : acc_(acc)
132 : {
133 31 : }
134 :
135 : std::coroutine_handle<>
136 27 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
137 : {
138 81 : return acc_.get().accept(
139 81 : h, ex, this->token_, &this->ec_, &peer_impl_);
140 : }
141 :
142 : public:
143 31 : [[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 31 : if (this->ec_ || !peer_impl_)
148 6 : return {this->ec_, tcp_socket()};
149 :
150 25 : tcp_socket peer(acc_.context());
151 25 : peer.h_.reset(peer_impl_);
152 25 : return {this->ec_, std::move(peer)};
153 25 : }
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 1 : explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
202 : {
203 1 : }
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 9 : 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 3 : tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
258 : {
259 3 : if (this != &other)
260 : {
261 3 : close();
262 3 : h_ = std::move(other.h_);
263 : }
264 3 : 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 9063 : bool is_open() const noexcept
343 : {
344 9063 : 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 4654 : [[nodiscard]] auto accept(tcp_socket& peer)
380 : {
381 4654 : accept_awaitable aw(*this, peer);
382 4654 : if (!is_open())
383 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
384 4654 : 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 31 : [[nodiscard]] auto accept()
420 : {
421 31 : accept_value_awaitable aw(*this);
422 31 : if (!is_open())
423 4 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
424 31 : 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 31 : [[nodiscard]] auto wait(wait_type w)
450 : {
451 31 : wait_awaitable aw(*this, w);
452 31 : if (!is_open())
453 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
454 31 : 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 615 : void set_option(Option const& opt)
559 : {
560 615 : if (!is_open())
561 2 : detail::throw_system_error(
562 4 : make_error_code(std::errc::bad_file_descriptor),
563 : "tcp_acceptor::set_option");
564 613 : auto const fam = get().family();
565 613 : std::error_code ec = get().set_option(
566 : opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
567 613 : if (ec)
568 8 : detail::throw_system_error(ec, "tcp_acceptor::set_option");
569 605 : }
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 23 : Option get_option() const
585 : {
586 23 : if (!is_open())
587 2 : detail::throw_system_error(
588 4 : make_error_code(std::errc::bad_file_descriptor),
589 : "tcp_acceptor::get_option");
590 21 : Option opt{};
591 21 : auto const fam = get().family();
592 21 : std::size_t sz = opt.size(fam);
593 : std::error_code ec =
594 21 : get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
595 21 : if (ec)
596 8 : detail::throw_system_error(ec, "tcp_acceptor::get_option");
597 13 : opt.resize(fam, sz);
598 13 : 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 35 : 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 17 : reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
730 : {
731 17 : if (impl)
732 17 : peer.h_.reset(impl);
733 17 : }
734 :
735 : private:
736 15629 : inline implementation& get() const noexcept
737 : {
738 15629 : return *static_cast<implementation*>(h_.get());
739 : }
740 : };
741 :
742 : } // namespace boost::corosio
743 :
744 : #endif
|