TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
11 : #define BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
12 :
13 : #include <boost/corosio/family.hpp>
14 : #include <boost/corosio/detail/config.hpp>
15 : #include <boost/corosio/detail/op_base.hpp>
16 : #include <boost/corosio/error.hpp>
17 : #include <boost/corosio/wait_type.hpp>
18 : #include <boost/corosio/io/io_object.hpp>
19 : #include <boost/capy/results.hpp>
20 : #include <boost/corosio/local_endpoint.hpp>
21 : #include <boost/corosio/local_stream_socket.hpp>
22 : #include <boost/capy/ex/executor_ref.hpp>
23 : #include <boost/capy/ex/execution_context.hpp>
24 : #include <boost/capy/ex/await_context.hpp>
25 : #include <boost/capy/concept/executor.hpp>
26 :
27 : #include <system_error>
28 : #include <utility>
29 :
30 : #include <cassert>
31 : #include <concepts>
32 : #include <boost/capy/continuation.hpp>
33 :
34 : #include <coroutine>
35 : #include <cstddef>
36 : #include <stop_token>
37 : #include <type_traits>
38 :
39 : namespace boost::corosio {
40 :
41 : /** Controls whether @ref local_stream_acceptor::bind() unlinks
42 : an existing socket path before binding.
43 : */
44 : enum class bind_option
45 : {
46 : /// Bind without touching the socket path.
47 : none,
48 : /// Unlink the socket path before binding (ignored for abstract paths).
49 : unlink_existing
50 : };
51 :
52 : /** Accepts inbound Unix domain stream connections, from a coroutine.
53 :
54 : This class provides asynchronous Unix domain stream accept
55 : operations that return awaitable types. The acceptor binds
56 : to a local endpoint (filesystem path or abstract name) and
57 : listens for incoming connections.
58 :
59 : The library does NOT automatically unlink the socket path
60 : on close. Callers are responsible for removing the socket
61 : file before bind (via @ref bind_option::unlink_existing) or
62 : after close.
63 :
64 : @par Thread Safety
65 : Distinct objects: Safe.@n
66 : Shared objects: Unsafe. An acceptor must not have concurrent
67 : accept operations.
68 :
69 : @par Example
70 : @par !example bind_listen_accept
71 : */
72 : class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
73 : {
74 : struct wait_awaitable : detail::void_op_base<wait_awaitable>
75 : {
76 : private:
77 : friend local_stream_acceptor;
78 :
79 HIT 8 : wait_awaitable(local_stream_acceptor& acc, wait_type w) noexcept
80 16 : : acc_(acc)
81 8 : , w_(w)
82 : {
83 8 : }
84 :
85 : friend detail::void_op_base<wait_awaitable>;
86 :
87 : local_stream_acceptor& acc_;
88 : wait_type w_;
89 :
90 : std::coroutine_handle<>
91 6 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
92 : {
93 6 : return acc_.get().wait(cont, ex, w_, token_, &ec_);
94 : }
95 : };
96 :
97 : struct move_accept_awaitable : detail::void_op_base<move_accept_awaitable>
98 : {
99 : private:
100 : friend local_stream_acceptor;
101 : friend detail::void_op_base<move_accept_awaitable>;
102 :
103 : local_stream_acceptor& acc_;
104 : mutable io_object::implementation* peer_impl_ = nullptr;
105 :
106 :
107 8 : explicit move_accept_awaitable(local_stream_acceptor& acc) noexcept
108 8 : : acc_(acc)
109 : {
110 8 : }
111 :
112 : std::coroutine_handle<>
113 6 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
114 : {
115 18 : return acc_.get().accept(
116 18 : cont, ex, this->token_, &this->ec_, &peer_impl_);
117 : }
118 :
119 : public:
120 : // A peer accepted for an awaiter that never resumed is closed
121 : // here, or nothing would own it.
122 16 : ~move_accept_awaitable()
123 : {
124 16 : if (peer_impl_)
125 2 : discard_peer(acc_, peer_impl_);
126 16 : }
127 :
128 : [[nodiscard]] capy::results<local_stream_socket>
129 6 : await_resume() const noexcept
130 : {
131 6 : if (this->ec_ || !peer_impl_)
132 4 : return {this->ec_, local_stream_socket()};
133 :
134 2 : local_stream_socket peer(acc_.ctx_);
135 2 : reset_peer_impl(peer, std::exchange(peer_impl_, nullptr));
136 2 : return {this->ec_, std::move(peer)};
137 2 : }
138 : };
139 :
140 : struct accept_awaitable : detail::void_op_base<accept_awaitable>
141 : {
142 : private:
143 : friend local_stream_acceptor;
144 : friend detail::void_op_base<accept_awaitable>;
145 :
146 : local_stream_acceptor& acc_;
147 : local_stream_socket& peer_;
148 : mutable io_object::implementation* peer_impl_ = nullptr;
149 :
150 :
151 33 : accept_awaitable(
152 : local_stream_acceptor& acc, local_stream_socket& peer) noexcept
153 66 : : acc_(acc)
154 33 : , peer_(peer)
155 : {
156 33 : }
157 :
158 : std::coroutine_handle<>
159 29 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
160 : {
161 87 : return acc_.get().accept(
162 87 : cont, ex, this->token_, &this->ec_, &peer_impl_);
163 : }
164 :
165 : public:
166 : // A peer accepted for an awaiter that never resumed is closed
167 : // here, or nothing would own it.
168 66 : ~accept_awaitable()
169 : {
170 66 : if (peer_impl_)
171 2 : discard_peer(acc_, peer_impl_);
172 66 : }
173 :
174 29 : [[nodiscard]] capy::results<> await_resume() const noexcept
175 : {
176 29 : if (!this->ec_ && peer_impl_)
177 19 : peer_.h_.reset(std::exchange(peer_impl_, nullptr));
178 29 : return {this->ec_};
179 : }
180 : };
181 :
182 : public:
183 : /** Closes the acceptor if open, cancelling any pending operations.
184 : */
185 : ~local_stream_acceptor() override;
186 :
187 : /** Construct an acceptor from an execution context.
188 :
189 : @param ctx The execution context that owns this acceptor.
190 : */
191 : explicit local_stream_acceptor(capy::execution_context& ctx);
192 :
193 : /** Convenience constructor: open + bind + listen.
194 :
195 : Creates a fully-bound listening acceptor in a single
196 : expression, throwing the codes the piecewise `open()` +
197 : `bind()` + `listen()` path returns.
198 :
199 : @param ctx The execution context that owns this acceptor.
200 : @param ep The local endpoint to bind to.
201 : @param backlog The maximum pending connection queue length.
202 :
203 : @throws std::system_error on open, bind, or listen failure.
204 : */
205 : local_stream_acceptor(
206 : capy::execution_context& ctx,
207 : corosio::local_endpoint ep,
208 : int backlog = 128);
209 :
210 : /** Construct an acceptor from an executor.
211 :
212 : The acceptor is associated with the executor's context.
213 :
214 : @param ex The executor whose context owns the acceptor.
215 :
216 : @tparam Ex A type satisfying @ref capy::Executor. Must not
217 : be `local_stream_acceptor` itself (disables implicit
218 : conversion from move).
219 : */
220 : template<class Ex>
221 : requires(!std::
222 : same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
223 : capy::Executor<Ex>
224 : explicit local_stream_acceptor(Ex const& ex)
225 : : local_stream_acceptor(ex.context())
226 : {
227 : }
228 :
229 : /** Convenience constructor from an executor.
230 :
231 : @param ex The executor whose context owns the acceptor.
232 : @param ep The local endpoint to bind to.
233 : @param backlog The maximum pending connection queue length.
234 :
235 : @tparam Ex A type satisfying @ref capy::Executor.
236 :
237 : @throws std::system_error on open, bind, or listen failure.
238 : */
239 : template<class Ex>
240 : requires capy::Executor<Ex>
241 : local_stream_acceptor(
242 : Ex const& ex, corosio::local_endpoint ep, int backlog = 128)
243 : : local_stream_acceptor(ex.context(), std::move(ep), backlog)
244 : {
245 : }
246 :
247 : /** Transfers ownership of the acceptor resources from another
248 : acceptor.
249 :
250 : @param other The acceptor to move from.
251 :
252 : @pre No awaitables returned by @p other's methods exist.
253 : @pre The execution context associated with @p other must
254 : outlive this acceptor.
255 : */
256 2 : local_stream_acceptor(local_stream_acceptor&& other) noexcept
257 2 : : local_stream_acceptor(other.ctx_, std::move(other))
258 : {
259 2 : }
260 :
261 : /** Closes any existing acceptor and transfers ownership from
262 : another acceptor. Both acceptors must share the same
263 : execution context.
264 :
265 : @param other The acceptor to move from.
266 :
267 : @return Reference to this acceptor.
268 :
269 : @pre `&ctx_ == &other.ctx_` (same execution context).
270 : @pre No awaitables returned by either `*this` or @p other's
271 : methods exist.
272 : */
273 : local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
274 : {
275 : assert(
276 : &ctx_ == &other.ctx_ &&
277 : "move-assign requires the same execution_context");
278 : if (this != &other)
279 : {
280 : close();
281 : io_object::operator=(std::move(other));
282 : }
283 : return *this;
284 : }
285 :
286 : /// Copy construction is disabled; the handle is uniquely owned.
287 : local_stream_acceptor(local_stream_acceptor const&) = delete;
288 : /// Copy assignment is disabled; the handle is uniquely owned.
289 : local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
290 :
291 : /** Create the acceptor socket.
292 :
293 : Failures such as descriptor exhaustion are normal runtime
294 : conditions and are reported through the returned error code.
295 :
296 :
297 : @return The error code, empty on success.
298 : */
299 : [[nodiscard]] std::error_code open() noexcept;
300 :
301 : /** Bind to a local endpoint.
302 :
303 : @param ep The local endpoint (path) to bind to.
304 : @param opt Bind options. Pass bind_option::unlink_existing
305 : to unlink the socket path before binding (ignored for
306 : abstract sockets and empty endpoints).
307 :
308 : @return An error code on failure, empty on success.
309 :
310 : A closed acceptor reports `errc::bad_file_descriptor`.
311 : */
312 : [[nodiscard]] std::error_code bind(
313 : corosio::local_endpoint ep,
314 : bind_option opt = bind_option::none) noexcept;
315 :
316 : /** Start listening for incoming connections.
317 :
318 : @param backlog The maximum pending connection queue length.
319 :
320 : @return An error code on failure, empty on success.
321 :
322 : A closed acceptor reports `errc::bad_file_descriptor`.
323 : */
324 : [[nodiscard]] std::error_code listen(int backlog = 128) noexcept;
325 :
326 : /** Close the acceptor.
327 :
328 : Cancels any pending accept operations and releases the
329 : underlying socket. Has no effect if the acceptor is not
330 : open.
331 :
332 : @post is_open() == false
333 : */
334 : void close() noexcept;
335 :
336 : /** Check if the acceptor has an open socket handle.
337 :
338 : @return `true` if the acceptor holds an open handle.
339 : */
340 653 : bool is_open() const noexcept
341 : {
342 653 : return h_ && get().is_open();
343 : }
344 :
345 : /** Initiate an asynchronous accept into an existing socket.
346 :
347 : Completes when a new connection is available. On success
348 : @p peer is reset to the accepted connection. Only one
349 : accept may be in flight at a time.
350 :
351 : @param peer The socket to receive the accepted connection.
352 :
353 : @par Cancellation
354 : Supports cancellation via stop_token or cancel().
355 : On cancellation, yields `capy::cond::canceled` and
356 : @p peer is not modified.
357 :
358 : @return An awaitable that completes with `results<>`.
359 :
360 : A closed acceptor reports `errc::bad_file_descriptor`.
361 : */
362 33 : [[nodiscard]] auto accept(local_stream_socket& peer)
363 : {
364 33 : accept_awaitable aw(*this, peer);
365 33 : if (!is_open())
366 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
367 33 : return aw;
368 : }
369 :
370 : /** Wait for an incoming connection or readiness condition.
371 :
372 : Suspends until the listen socket is ready in the
373 : requested direction. For `wait_type::read`, completion
374 : signals that a subsequent @ref accept succeeds
375 : without blocking. A connection already queued when the
376 : wait begins completes it immediately. No connection is
377 : consumed.
378 :
379 : @note `wait_type::write` is not usable on an acceptor:
380 : writability carries no meaning for a listening socket, so
381 : the wait fails with `errc::operation_not_supported` on
382 : every backend.
383 :
384 : @param w The wait direction.
385 :
386 : @return An awaitable that completes with `results<>`.
387 :
388 : A closed acceptor completes with `errc::bad_file_descriptor`.
389 :
390 : @pre This acceptor must outlive the returned awaitable.
391 : */
392 8 : [[nodiscard]] auto wait(wait_type w)
393 : {
394 8 : wait_awaitable aw(*this, w);
395 8 : if (!is_open())
396 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
397 8 : return aw;
398 : }
399 :
400 : /** Initiate an asynchronous accept, returning the socket.
401 :
402 : Completes when a new connection is available. Only one
403 : accept may be in flight at a time.
404 :
405 : @par Cancellation
406 : Supports cancellation via stop_token or cancel().
407 : On cancellation, yields `capy::cond::canceled` with
408 : a default-constructed socket.
409 :
410 : @return An awaitable that completes with
411 : results<`local_stream_socket`>.
412 :
413 : A closed acceptor reports `errc::bad_file_descriptor`.
414 : On failure the returned socket is default-constructed and not
415 : open. It has no context, so it may only be destroyed, assigned,
416 : or checked with `is_open()`.
417 : */
418 8 : [[nodiscard]] auto accept()
419 : {
420 8 : move_accept_awaitable aw(*this);
421 8 : if (!is_open())
422 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
423 8 : return aw;
424 : }
425 :
426 : /** Cancel pending asynchronous accept operations.
427 :
428 : Outstanding accept operations complete with
429 : @c capy::cond::canceled. Safe to call when no
430 : operations are pending (no-op).
431 : */
432 : void cancel() noexcept;
433 :
434 : /** Release ownership of the native socket handle.
435 :
436 : Deregisters the acceptor from the reactor and cancels
437 : pending operations without closing the descriptor. The
438 : caller takes ownership of the returned handle.
439 :
440 : A closed object reports `errc::bad_file_descriptor` and the
441 : returned handle is the invalid sentinel ( -1, or
442 : `INVALID_SOCKET` on Windows ), never zero.
443 :
444 : @note With the IOCP backend, a socket released while operations
445 : on it are in flight stays bound to this context's completion
446 : port for good. Windows refuses to unbind a handle with I/O
447 : outstanding, so no context, this one included, can assign it
448 : afterwards. Its cancelled operations still complete normally.
449 :
450 : @return The error code and the native handle.
451 :
452 : @post is_open() == false
453 : */
454 : [[nodiscard]] capy::results<native_handle_type> release() noexcept;
455 :
456 : /** Get the native socket handle.
457 :
458 : @return The native socket handle, or -1/INVALID_SOCKET if not
459 : open.
460 :
461 : @pre None. May be called on closed acceptors.
462 : */
463 : native_handle_type native_handle() const noexcept;
464 :
465 : /** Assign an existing native socket to this acceptor.
466 :
467 : Adopts a listening socket created outside the library —
468 : received from a service manager, inherited, or made natively —
469 : and registers it with the backend. The socket must be a
470 : listening stream socket in the local IPC family. Adoption
471 : never alters the descriptor's flags or options: on POSIX the
472 : fd must already be non-blocking, and on Windows the socket
473 : must be overlapped-capable.
474 :
475 : Adoption does not verify listen state; @ref accept reports the
476 : error if the socket is not listening.
477 :
478 : The object must be closed. To replace a held socket, `close()`
479 : or `release()` it first.
480 :
481 : @par Exception Safety
482 : Throws nothing. On failure the object is unchanged and the
483 : caller retains ownership of `fd`.
484 :
485 : @param fd The native socket to adopt. On success the object
486 : owns it and closes it.
487 :
488 : @return `error::already_open` if this object is open.
489 : Otherwise the error code, empty on success. Validation and
490 : registration failures are normal runtime conditions when
491 : adopting foreign descriptors.
492 : */
493 : [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
494 :
495 : /** Return the local endpoint the acceptor is bound to.
496 :
497 : Safe to call in any state.
498 :
499 : @return The bound local endpoint, or a default-constructed
500 : endpoint if the acceptor is not open or not yet bound.
501 : */
502 : corosio::local_endpoint local_endpoint() const noexcept;
503 :
504 : /** Set a socket option on the acceptor.
505 :
506 : Applies a type-safe socket option to the underlying socket.
507 : The option type encodes the protocol level and option name.
508 :
509 : Fails with `errc::bad_file_descriptor` if the acceptor is not
510 : open, or with the error the operating system reports.
511 :
512 : @param opt The option to set.
513 :
514 : @tparam Option A socket option type providing static
515 : `level()` and `name()` members, and `data()` / `size()`
516 : accessors.
517 :
518 : @return The error code, empty on success.
519 : */
520 : template<class Option>
521 6 : [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
522 : {
523 6 : if (!is_open())
524 2 : return make_error_code(std::errc::bad_file_descriptor);
525 4 : auto const fam = get().family();
526 4 : return get().set_option(
527 4 : opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
528 : }
529 :
530 : /** Get a socket option from the acceptor.
531 :
532 : Retrieves the current value of a type-safe socket option.
533 :
534 : Fails with `errc::bad_file_descriptor` if the acceptor is not
535 : open, or with the error the operating system reports. On
536 : failure the option is value-initialized.
537 :
538 : @return The error code and the option value.
539 :
540 : @tparam Option A socket option type providing static
541 : `level()` and `name()` members, and `data()` / `size()`
542 : / `resize()` members.
543 : */
544 : template<class Option>
545 6 : [[nodiscard]] capy::results<Option> get_option() const noexcept
546 : {
547 6 : if (!is_open())
548 2 : return {make_error_code(std::errc::bad_file_descriptor), Option{}};
549 4 : Option opt{};
550 4 : auto const fam = get().family();
551 4 : std::size_t sz = opt.size(fam);
552 4 : if (auto ec = get().get_option(
553 : opt.level(fam), opt.name(fam), opt.data(fam), &sz))
554 2 : return {ec, Option{}};
555 2 : opt.resize(fam, sz);
556 2 : return {std::error_code{}, std::move(opt)};
557 : }
558 :
559 : /** Backends derive from this to implement accept, option, and
560 : lifecycle management.
561 : */
562 : struct implementation : io_object::implementation
563 : {
564 : /** Initiate an asynchronous accept.
565 :
566 : On completion the backend sets @p *ec and, on
567 : success, stores a pointer to the new socket
568 : implementation in @p *impl_out.
569 :
570 : @param cont Continuation to resume on completion; it lives in
571 : the awaiting frame until then.
572 : @param ex Executor for dispatching the completion.
573 : @param token Stop token for cancellation.
574 : @param ec Output error code.
575 : @param impl_out Output pointer for the accepted socket.
576 : @return Coroutine handle to resume immediately.
577 : */
578 : virtual std::coroutine_handle<> accept(
579 : capy::continuation& cont,
580 : capy::executor_ref ex,
581 : std::stop_token token,
582 : std::error_code* ec,
583 : io_object::implementation** impl_out) = 0;
584 :
585 : /** Initiate an asynchronous wait for acceptor readiness.
586 :
587 : Completes when the listen socket becomes ready for
588 : the specified direction. No connection is consumed.
589 :
590 : @param cont Continuation to resume on completion; it lives in
591 : the awaiting frame until then.
592 : @param ex Executor for dispatching the completion.
593 : @param w The direction to wait on.
594 : @param token Stop token for cancellation.
595 : @param ec Output error code.
596 :
597 : @return Coroutine handle to resume immediately.
598 : */
599 : virtual std::coroutine_handle<> wait(
600 : capy::continuation& cont,
601 : capy::executor_ref ex,
602 : wait_type w,
603 : std::stop_token token,
604 : std::error_code* ec) = 0;
605 :
606 : /// Return the cached local endpoint.
607 : virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
608 :
609 : /// Return whether the underlying socket is open.
610 : virtual bool is_open() const noexcept = 0;
611 :
612 : /// Return the native handle, or the platform sentinel if closed.
613 : virtual native_handle_type native_handle() const noexcept = 0;
614 :
615 : /** Return the socket's address family.
616 :
617 : Local sockets have no IP family; implementations return
618 : `v4`, which the family-neutral options applicable to them
619 : ignore.
620 :
621 : @return The address family for option rendering.
622 : */
623 : virtual corosio::family family() const noexcept = 0;
624 :
625 : /// Release and return the native handle without closing.
626 : virtual native_handle_type release_socket() noexcept = 0;
627 :
628 : /// Cancel pending accept operations.
629 : virtual void cancel() noexcept = 0;
630 :
631 : /** Set a raw socket option.
632 :
633 : @param level The protocol level (e.g. `SOL_SOCKET`).
634 : @param optname The option name.
635 : @param data Pointer to the option value.
636 : @param size Size of the option value in bytes.
637 :
638 : @return The error code, empty on success.
639 : */
640 : virtual std::error_code set_option(
641 : int level,
642 : int optname,
643 : void const* data,
644 : std::size_t size) noexcept = 0;
645 :
646 : /** Get a raw socket option.
647 :
648 : @param level The protocol level (e.g. `SOL_SOCKET`).
649 : @param optname The option name.
650 : @param data Pointer to storage for the option value.
651 : @param size In/out size of the storage, in bytes.
652 :
653 : @return The error code, empty on success.
654 : */
655 : virtual std::error_code
656 : get_option(int level, int optname, void* data, std::size_t* size)
657 : const noexcept = 0;
658 : };
659 :
660 : protected:
661 : /** Adopt an existing handle bound to a context.
662 :
663 : @param h The handle the acceptor takes ownership of.
664 :
665 : @param ctx The context the acceptor draws its service from.
666 : */
667 26 : local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
668 26 : : io_object(std::move(h))
669 26 : , ctx_(ctx)
670 : {
671 26 : }
672 :
673 : /** Adopt an existing handle, then open, bind, and listen.
674 :
675 : Lets a derived acceptor offer the convenience constructor while
676 : supplying its own service.
677 :
678 : @param h The handle the acceptor takes ownership of.
679 : @param ctx The context the acceptor draws its service from.
680 : @param ep The local endpoint to bind to.
681 : @param backlog The maximum pending connection queue length.
682 :
683 : @throws std::system_error on open, bind, or listen failure.
684 : */
685 : local_stream_acceptor(
686 : handle h,
687 : capy::execution_context& ctx,
688 : corosio::local_endpoint ep,
689 : int backlog);
690 :
691 : /** Move construct, rebinding to a context.
692 :
693 : @param ctx The context the acceptor draws its service from.
694 :
695 : @param other The acceptor to take the handle from.
696 : */
697 2 : local_stream_acceptor(
698 : capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
699 2 : : io_object(std::move(other))
700 2 : , ctx_(ctx)
701 : {
702 2 : }
703 :
704 : /** Install an accepted implementation into the peer socket.
705 :
706 : Derived acceptors call this to hand the accepted connection to
707 : the caller's socket, which cannot reach @ref io_object::handle
708 : itself.
709 :
710 : @param peer The socket receiving the accepted connection.
711 :
712 : @param impl The accepted implementation, or `nullptr` on failure.
713 : */
714 8 : static void reset_peer_impl(
715 : local_stream_socket& peer, io_object::implementation* impl) noexcept
716 : {
717 8 : if (impl)
718 8 : peer.h_.reset(impl);
719 8 : }
720 :
721 : /// Close and release a non-null accepted peer no socket took over.
722 : static void discard_peer(
723 : local_stream_acceptor& acc, io_object::implementation* impl) noexcept;
724 :
725 : private:
726 : capy::execution_context& ctx_;
727 :
728 748 : inline implementation& get() const noexcept
729 : {
730 748 : return *static_cast<implementation*>(h_.get());
731 : }
732 : };
733 :
734 : } // namespace boost::corosio
735 :
736 : #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
|