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