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