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