include/boost/corosio/local_stream_acceptor.hpp

100.0% Lines (88 / 88) 100.0% Functions (23 / 23)
local_stream_acceptor.hpp
f(x) Functions (23)
Function Calls Lines Blocks
boost::corosio::local_stream_acceptor::wait_awaitable::wait_awaitable(boost::corosio::local_stream_acceptor&, boost::corosio::wait_type) :79 8x 100.0% 100.0% boost::corosio::local_stream_acceptor::wait_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :91 6x 100.0% 80.0% boost::corosio::local_stream_acceptor::move_accept_awaitable::move_accept_awaitable(boost::corosio::local_stream_acceptor&) :107 8x 100.0% 100.0% boost::corosio::local_stream_acceptor::move_accept_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :113 6x 100.0% 80.0% boost::corosio::local_stream_acceptor::move_accept_awaitable::~move_accept_awaitable() :122 16x 100.0% 100.0% boost::corosio::local_stream_acceptor::move_accept_awaitable::await_resume() const :129 6x 100.0% 100.0% boost::corosio::local_stream_acceptor::accept_awaitable::accept_awaitable(boost::corosio::local_stream_acceptor&, boost::corosio::local_stream_socket&) :151 33x 100.0% 100.0% boost::corosio::local_stream_acceptor::accept_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :159 29x 100.0% 80.0% boost::corosio::local_stream_acceptor::accept_awaitable::~accept_awaitable() :168 66x 100.0% 100.0% boost::corosio::local_stream_acceptor::accept_awaitable::await_resume() const :174 29x 100.0% 100.0% boost::corosio::local_stream_acceptor::local_stream_acceptor(boost::corosio::local_stream_acceptor&&) :256 2x 100.0% 100.0% boost::corosio::local_stream_acceptor::is_open() const :340 653x 100.0% 100.0% boost::corosio::local_stream_acceptor::accept(boost::corosio::local_stream_socket&) :362 33x 100.0% 100.0% boost::corosio::local_stream_acceptor::wait(boost::corosio::wait_type) :392 8x 100.0% 100.0% boost::corosio::local_stream_acceptor::accept() :418 8x 100.0% 100.0% std::error_code boost::corosio::local_stream_acceptor::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :521 2x 83.3% 92.0% std::error_code boost::corosio::local_stream_acceptor::set_option<boost::corosio::socket_option::reuse_address>(boost::corosio::socket_option::reuse_address const&) :521 4x 100.0% 100.0% std::tuple<std::error_code, boost::corosio::socket_option::no_delay> boost::corosio::local_stream_acceptor::get_option<boost::corosio::socket_option::no_delay>() const :545 2x 70.0% 65.0% std::tuple<std::error_code, boost::corosio::socket_option::reuse_address> boost::corosio::local_stream_acceptor::get_option<boost::corosio::socket_option::reuse_address>() const :545 4x 90.0% 88.0% boost::corosio::local_stream_acceptor::local_stream_acceptor(boost::corosio::io_object::handle, boost::capy::execution_context&) :667 26x 100.0% 100.0% boost::corosio::local_stream_acceptor::local_stream_acceptor(boost::capy::execution_context&, boost::corosio::local_stream_acceptor&&) :697 2x 100.0% 100.0% boost::corosio::local_stream_acceptor::reset_peer_impl(boost::corosio::local_stream_socket&, boost::corosio::io_object::implementation*) :714 8x 100.0% 100.0% boost::corosio::local_stream_acceptor::get() const :728 748x 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/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 8x wait_awaitable(local_stream_acceptor& acc, wait_type w) noexcept
80 16x : acc_(acc)
81 8x , w_(w)
82 {
83 8x }
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 6x dispatch(capy::continuation& cont, capy::executor_ref ex) const
92 {
93 6x 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 8x explicit move_accept_awaitable(local_stream_acceptor& acc) noexcept
108 8x : acc_(acc)
109 {
110 8x }
111
112 std::coroutine_handle<>
113 6x dispatch(capy::continuation& cont, capy::executor_ref ex) const
114 {
115 18x return acc_.get().accept(
116 18x 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 16x ~move_accept_awaitable()
123 {
124 16x if (peer_impl_)
125 2x discard_peer(acc_, peer_impl_);
126 16x }
127
128 [[nodiscard]] capy::results<local_stream_socket>
129 6x await_resume() const noexcept
130 {
131 6x if (this->ec_ || !peer_impl_)
132 4x return {this->ec_, local_stream_socket()};
133
134 2x local_stream_socket peer(acc_.ctx_);
135 2x reset_peer_impl(peer, std::exchange(peer_impl_, nullptr));
136 2x return {this->ec_, std::move(peer)};
137 2x }
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 33x accept_awaitable(
152 local_stream_acceptor& acc, local_stream_socket& peer) noexcept
153 66x : acc_(acc)
154 33x , peer_(peer)
155 {
156 33x }
157
158 std::coroutine_handle<>
159 29x dispatch(capy::continuation& cont, capy::executor_ref ex) const
160 {
161 87x return acc_.get().accept(
162 87x 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 66x ~accept_awaitable()
169 {
170 66x if (peer_impl_)
171 2x discard_peer(acc_, peer_impl_);
172 66x }
173
174 29x [[nodiscard]] capy::results<> await_resume() const noexcept
175 {
176 29x if (!this->ec_ && peer_impl_)
177 19x peer_.h_.reset(std::exchange(peer_impl_, nullptr));
178 29x 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 2x local_stream_acceptor(local_stream_acceptor&& other) noexcept
257 2x : local_stream_acceptor(other.ctx_, std::move(other))
258 {
259 2x }
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 653x bool is_open() const noexcept
341 {
342 653x 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 33x [[nodiscard]] auto accept(local_stream_socket& peer)
363 {
364 33x accept_awaitable aw(*this, peer);
365 33x if (!is_open())
366 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
367 33x 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 8x [[nodiscard]] auto wait(wait_type w)
393 {
394 8x wait_awaitable aw(*this, w);
395 8x if (!is_open())
396 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
397 8x 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 8x [[nodiscard]] auto accept()
419 {
420 8x move_accept_awaitable aw(*this);
421 8x if (!is_open())
422 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
423 8x 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 6x [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
522 {
523 6x if (!is_open())
524 2x return make_error_code(std::errc::bad_file_descriptor);
525 4x auto const fam = get().family();
526 4x return get().set_option(
527 4x 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 6x [[nodiscard]] capy::results<Option> get_option() const noexcept
546 {
547 6x if (!is_open())
548 2x return {make_error_code(std::errc::bad_file_descriptor), Option{}};
549 4x Option opt{};
550 4x auto const fam = get().family();
551 4x std::size_t sz = opt.size(fam);
552 4x if (auto ec = get().get_option(
553 opt.level(fam), opt.name(fam), opt.data(fam), &sz))
554 2x return {ec, Option{}};
555 2x opt.resize(fam, sz);
556 2x 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 26x local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
668 26x : io_object(std::move(h))
669 26x , ctx_(ctx)
670 {
671 26x }
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 2x local_stream_acceptor(
698 capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
699 2x : io_object(std::move(other))
700 2x , ctx_(ctx)
701 {
702 2x }
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 8x static void reset_peer_impl(
715 local_stream_socket& peer, io_object::implementation* impl) noexcept
716 {
717 8x if (impl)
718 8x peer.h_.reset(impl);
719 8x }
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 748x inline implementation& get() const noexcept
729 {
730 748x return *static_cast<implementation*>(h_.get());
731 }
732 };
733
734 } // namespace boost::corosio
735
736 #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
737