include/boost/corosio/local_stream_acceptor.hpp

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