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