include/boost/corosio/local_stream_acceptor.hpp

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