88.24% Lines (45/51) 100.00% Functions (13/13)
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/io/io_object.hpp> 15   #include <boost/corosio/io/io_object.hpp>
16   #include <boost/capy/io_result.hpp> 16   #include <boost/capy/io_result.hpp>
17   #include <boost/corosio/local_endpoint.hpp> 17   #include <boost/corosio/local_endpoint.hpp>
18   #include <boost/corosio/local_stream.hpp> 18   #include <boost/corosio/local_stream.hpp>
19   #include <boost/corosio/local_stream_socket.hpp> 19   #include <boost/corosio/local_stream_socket.hpp>
20   #include <boost/capy/ex/executor_ref.hpp> 20   #include <boost/capy/ex/executor_ref.hpp>
21   #include <boost/capy/ex/execution_context.hpp> 21   #include <boost/capy/ex/execution_context.hpp>
22   #include <boost/capy/ex/io_env.hpp> 22   #include <boost/capy/ex/io_env.hpp>
23   #include <boost/capy/concept/executor.hpp> 23   #include <boost/capy/concept/executor.hpp>
24   24  
25   #include <system_error> 25   #include <system_error>
26   26  
27   #include <cassert> 27   #include <cassert>
28   #include <concepts> 28   #include <concepts>
29   #include <coroutine> 29   #include <coroutine>
30   #include <cstddef> 30   #include <cstddef>
31   #include <stop_token> 31   #include <stop_token>
32   #include <type_traits> 32   #include <type_traits>
33   33  
34   namespace boost::corosio { 34   namespace boost::corosio {
35   35  
36   /** Options for @ref local_stream_acceptor::bind(). 36   /** Options for @ref local_stream_acceptor::bind().
37   37  
38   Controls filesystem cleanup behavior before binding 38   Controls filesystem cleanup behavior before binding
39   to a Unix domain socket path. 39   to a Unix domain socket path.
40   */ 40   */
41   enum class bind_option 41   enum class bind_option
42   { 42   {
43   none, 43   none,
44   /// Unlink the socket path before binding (ignored for abstract paths). 44   /// Unlink the socket path before binding (ignored for abstract paths).
45   unlink_existing 45   unlink_existing
46   }; 46   };
47   47  
48   /** An asynchronous Unix domain stream acceptor for coroutine I/O. 48   /** An asynchronous Unix domain stream acceptor for coroutine I/O.
49   49  
50   This class provides asynchronous Unix domain stream accept 50   This class provides asynchronous Unix domain stream accept
51   operations that return awaitable types. The acceptor binds 51   operations that return awaitable types. The acceptor binds
52   to a local endpoint (filesystem path or abstract name) and 52   to a local endpoint (filesystem path or abstract name) and
53   listens for incoming connections. 53   listens for incoming connections.
54   54  
55   The library does NOT automatically unlink the socket path 55   The library does NOT automatically unlink the socket path
56   on close. Callers are responsible for removing the socket 56   on close. Callers are responsible for removing the socket
57   file before bind (via @ref bind_option::unlink_existing) or 57   file before bind (via @ref bind_option::unlink_existing) or
58   after close. 58   after close.
59   59  
60   @par Thread Safety 60   @par Thread Safety
61   Distinct objects: Safe.@n 61   Distinct objects: Safe.@n
62   Shared objects: Unsafe. An acceptor must not have concurrent 62   Shared objects: Unsafe. An acceptor must not have concurrent
63   accept operations. 63   accept operations.
64   64  
65   @par Example 65   @par Example
66   @code 66   @code
67   io_context ioc; 67   io_context ioc;
68   local_stream_acceptor acc(ioc); 68   local_stream_acceptor acc(ioc);
69   acc.open(); 69   acc.open();
70   acc.bind(local_endpoint("/tmp/my.sock"), 70   acc.bind(local_endpoint("/tmp/my.sock"),
71   bind_option::unlink_existing); 71   bind_option::unlink_existing);
72   acc.listen(); 72   acc.listen();
73   auto [ec, peer] = co_await acc.accept(); 73   auto [ec, peer] = co_await acc.accept();
74   @endcode 74   @endcode
75   */ 75   */
76   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object 76   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
77   { 77   {
78   struct move_accept_awaitable 78   struct move_accept_awaitable
79   { 79   {
80   local_stream_acceptor& acc_; 80   local_stream_acceptor& acc_;
81   std::stop_token token_; 81   std::stop_token token_;
82   mutable std::error_code ec_; 82   mutable std::error_code ec_;
83   mutable io_object::implementation* peer_impl_ = nullptr; 83   mutable io_object::implementation* peer_impl_ = nullptr;
84   84  
HITCBC 85   2 explicit move_accept_awaitable( 85   2 explicit move_accept_awaitable(
86   local_stream_acceptor& acc) noexcept 86   local_stream_acceptor& acc) noexcept
HITCBC 87   2 : acc_(acc) 87   2 : acc_(acc)
88   { 88   {
HITCBC 89   2 } 89   2 }
90   90  
HITCBC 91   2 bool await_ready() const noexcept 91   2 bool await_ready() const noexcept
92   { 92   {
HITCBC 93   2 return token_.stop_requested(); 93   2 return token_.stop_requested();
94   } 94   }
95   95  
HITCBC 96   2 capy::io_result<local_stream_socket> await_resume() const noexcept 96   2 capy::io_result<local_stream_socket> await_resume() const noexcept
97   { 97   {
HITCBC 98   2 if (token_.stop_requested()) 98   2 if (token_.stop_requested())
MISUBC 99   return {make_error_code(std::errc::operation_canceled), 99   return {make_error_code(std::errc::operation_canceled),
MISUBC 100   local_stream_socket()}; 100   local_stream_socket()};
101   101  
HITCBC 102   2 if (ec_ || !peer_impl_) 102   2 if (ec_ || !peer_impl_)
MISUBC 103   return {ec_, local_stream_socket()}; 103   return {ec_, local_stream_socket()};
104   104  
HITCBC 105   2 local_stream_socket peer(acc_.ctx_); 105   2 local_stream_socket peer(acc_.ctx_);
HITCBC 106   2 reset_peer_impl(peer, peer_impl_); 106   2 reset_peer_impl(peer, peer_impl_);
HITCBC 107   2 return {ec_, std::move(peer)}; 107   2 return {ec_, std::move(peer)};
HITCBC 108   2 } 108   2 }
109   109  
HITCBC 110   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 110   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
111   -> std::coroutine_handle<> 111   -> std::coroutine_handle<>
112   { 112   {
HITCBC 113   2 token_ = env->stop_token; 113   2 token_ = env->stop_token;
HITCBC 114   6 return acc_.get().accept( 114   6 return acc_.get().accept(
HITCBC 115   6 h, env->executor, token_, &ec_, &peer_impl_); 115   6 h, env->executor, token_, &ec_, &peer_impl_);
116   } 116   }
117   }; 117   };
118   118  
119   struct accept_awaitable 119   struct accept_awaitable
120   { 120   {
121   local_stream_acceptor& acc_; 121   local_stream_acceptor& acc_;
122   local_stream_socket& peer_; 122   local_stream_socket& peer_;
123   std::stop_token token_; 123   std::stop_token token_;
124   mutable std::error_code ec_; 124   mutable std::error_code ec_;
125   mutable io_object::implementation* peer_impl_ = nullptr; 125   mutable io_object::implementation* peer_impl_ = nullptr;
126   126  
HITCBC 127   2 accept_awaitable( 127   2 accept_awaitable(
128   local_stream_acceptor& acc, local_stream_socket& peer) noexcept 128   local_stream_acceptor& acc, local_stream_socket& peer) noexcept
HITCBC 129   2 : acc_(acc) 129   2 : acc_(acc)
HITCBC 130   2 , peer_(peer) 130   2 , peer_(peer)
131   { 131   {
HITCBC 132   2 } 132   2 }
133   133  
HITCBC 134   2 bool await_ready() const noexcept 134   2 bool await_ready() const noexcept
135   { 135   {
HITCBC 136   2 return token_.stop_requested(); 136   2 return token_.stop_requested();
137   } 137   }
138   138  
HITCBC 139   2 capy::io_result<> await_resume() const noexcept 139   2 capy::io_result<> await_resume() const noexcept
140   { 140   {
HITCBC 141   2 if (token_.stop_requested()) 141   2 if (token_.stop_requested())
MISUBC 142   return {make_error_code(std::errc::operation_canceled)}; 142   return {make_error_code(std::errc::operation_canceled)};
143   143  
HITCBC 144   2 if (!ec_ && peer_impl_) 144   2 if (!ec_ && peer_impl_)
HITCBC 145   2 peer_.h_.reset(peer_impl_); 145   2 peer_.h_.reset(peer_impl_);
HITCBC 146   2 return {ec_}; 146   2 return {ec_};
147   } 147   }
148   148  
HITCBC 149   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 149   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
150   -> std::coroutine_handle<> 150   -> std::coroutine_handle<>
151   { 151   {
HITCBC 152   2 token_ = env->stop_token; 152   2 token_ = env->stop_token;
HITCBC 153   6 return acc_.get().accept( 153   6 return acc_.get().accept(
HITCBC 154   6 h, env->executor, token_, &ec_, &peer_impl_); 154   6 h, env->executor, token_, &ec_, &peer_impl_);
155   } 155   }
156   }; 156   };
157   157  
158   public: 158   public:
159   /** Destructor. 159   /** Destructor.
160   160  
161   Closes the acceptor if open, cancelling any pending operations. 161   Closes the acceptor if open, cancelling any pending operations.
162   */ 162   */
163   ~local_stream_acceptor() override; 163   ~local_stream_acceptor() override;
164   164  
165   /** Construct an acceptor from an execution context. 165   /** Construct an acceptor from an execution context.
166   166  
167   @param ctx The execution context that will own this acceptor. 167   @param ctx The execution context that will own this acceptor.
168   */ 168   */
169   explicit local_stream_acceptor(capy::execution_context& ctx); 169   explicit local_stream_acceptor(capy::execution_context& ctx);
170   170  
171   /** Construct an acceptor from an executor. 171   /** Construct an acceptor from an executor.
172   172  
173   The acceptor is associated with the executor's context. 173   The acceptor is associated with the executor's context.
174   174  
175   @param ex The executor whose context will own the acceptor. 175   @param ex The executor whose context will own the acceptor.
176   176  
177   @tparam Ex A type satisfying @ref capy::Executor. Must not 177   @tparam Ex A type satisfying @ref capy::Executor. Must not
178   be `local_stream_acceptor` itself (disables implicit 178   be `local_stream_acceptor` itself (disables implicit
179   conversion from move). 179   conversion from move).
180   */ 180   */
181   template<class Ex> 181   template<class Ex>
182   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) && 182   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
183   capy::Executor<Ex> 183   capy::Executor<Ex>
184   explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context()) 184   explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context())
185   { 185   {
186   } 186   }
187   187  
188   /** Move constructor. 188   /** Move constructor.
189   189  
190   Transfers ownership of the acceptor resources. 190   Transfers ownership of the acceptor resources.
191   191  
192   @param other The acceptor to move from. 192   @param other The acceptor to move from.
193   193  
194   @pre No awaitables returned by @p other's methods exist. 194   @pre No awaitables returned by @p other's methods exist.
195   @pre The execution context associated with @p other must 195   @pre The execution context associated with @p other must
196   outlive this acceptor. 196   outlive this acceptor.
197   */ 197   */
198   local_stream_acceptor(local_stream_acceptor&& other) noexcept 198   local_stream_acceptor(local_stream_acceptor&& other) noexcept
199   : local_stream_acceptor(other.ctx_, std::move(other)) 199   : local_stream_acceptor(other.ctx_, std::move(other))
200   { 200   {
201   } 201   }
202   202  
203   /** Move assignment operator. 203   /** Move assignment operator.
204   204  
205   Closes any existing acceptor and transfers ownership. 205   Closes any existing acceptor and transfers ownership.
206   Both acceptors must share the same execution context. 206   Both acceptors must share the same execution context.
207   207  
208   @param other The acceptor to move from. 208   @param other The acceptor to move from.
209   209  
210   @return Reference to this acceptor. 210   @return Reference to this acceptor.
211   211  
212   @pre `&ctx_ == &other.ctx_` (same execution context). 212   @pre `&ctx_ == &other.ctx_` (same execution context).
213   @pre No awaitables returned by either `*this` or @p other's 213   @pre No awaitables returned by either `*this` or @p other's
214   methods exist. 214   methods exist.
215   */ 215   */
216   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept 216   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
217   { 217   {
218   assert(&ctx_ == &other.ctx_ && 218   assert(&ctx_ == &other.ctx_ &&
219   "move-assign requires the same execution_context"); 219   "move-assign requires the same execution_context");
220   if (this != &other) 220   if (this != &other)
221   { 221   {
222   close(); 222   close();
223   io_object::operator=(std::move(other)); 223   io_object::operator=(std::move(other));
224   } 224   }
225   return *this; 225   return *this;
226   } 226   }
227   227  
228   local_stream_acceptor(local_stream_acceptor const&) = delete; 228   local_stream_acceptor(local_stream_acceptor const&) = delete;
229   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete; 229   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
230   230  
231   /** Create the acceptor socket. 231   /** Create the acceptor socket.
232   232  
233   @param proto The protocol. Defaults to local_stream{}. 233   @param proto The protocol. Defaults to local_stream{}.
234   234  
235   @throws std::system_error on failure. 235   @throws std::system_error on failure.
236   */ 236   */
237   void open(local_stream proto = {}); 237   void open(local_stream proto = {});
238   238  
239   /** Bind to a local endpoint. 239   /** Bind to a local endpoint.
240   240  
241   @param ep The local endpoint (path) to bind to. 241   @param ep The local endpoint (path) to bind to.
242   @param opt Bind options. Pass bind_option::unlink_existing 242   @param opt Bind options. Pass bind_option::unlink_existing
243   to unlink the socket path before binding (ignored for 243   to unlink the socket path before binding (ignored for
244   abstract sockets and empty endpoints). 244   abstract sockets and empty endpoints).
245   245  
246   @return An error code on failure, empty on success. 246   @return An error code on failure, empty on success.
247   247  
248   @throws std::logic_error if the acceptor is not open. 248   @throws std::logic_error if the acceptor is not open.
249   */ 249   */
250   [[nodiscard]] std::error_code 250   [[nodiscard]] std::error_code
251   bind(corosio::local_endpoint ep, 251   bind(corosio::local_endpoint ep,
252   bind_option opt = bind_option::none); 252   bind_option opt = bind_option::none);
253   253  
254   /** Start listening for incoming connections. 254   /** Start listening for incoming connections.
255   255  
256   @param backlog The maximum pending connection queue length. 256   @param backlog The maximum pending connection queue length.
257   257  
258   @return An error code on failure, empty on success. 258   @return An error code on failure, empty on success.
259   259  
260   @throws std::logic_error if the acceptor is not open. 260   @throws std::logic_error if the acceptor is not open.
261   */ 261   */
262   [[nodiscard]] std::error_code listen(int backlog = 128); 262   [[nodiscard]] std::error_code listen(int backlog = 128);
263   263  
264   /** Close the acceptor. 264   /** Close the acceptor.
265   265  
266   Cancels any pending accept operations and releases the 266   Cancels any pending accept operations and releases the
267   underlying socket. Has no effect if the acceptor is not 267   underlying socket. Has no effect if the acceptor is not
268   open. 268   open.
269   269  
270   @post is_open() == false 270   @post is_open() == false
271   */ 271   */
272   void close(); 272   void close();
273   273  
274   /// Check if the acceptor has an open socket handle. 274   /// Check if the acceptor has an open socket handle.
HITCBC 275   44 bool is_open() const noexcept 275   44 bool is_open() const noexcept
276   { 276   {
HITCBC 277   44 return h_ && get().is_open(); 277   44 return h_ && get().is_open();
278   } 278   }
279   279  
280   /** Initiate an asynchronous accept into an existing socket. 280   /** Initiate an asynchronous accept into an existing socket.
281   281  
282   Completes when a new connection is available. On success 282   Completes when a new connection is available. On success
283   @p peer is reset to the accepted connection. Only one 283   @p peer is reset to the accepted connection. Only one
284   accept may be in flight at a time. 284   accept may be in flight at a time.
285   285  
286   @param peer The socket to receive the accepted connection. 286   @param peer The socket to receive the accepted connection.
287   287  
288   @par Cancellation 288   @par Cancellation
289   Supports cancellation via stop_token or cancel(). 289   Supports cancellation via stop_token or cancel().
290   On cancellation, yields `capy::cond::canceled` and 290   On cancellation, yields `capy::cond::canceled` and
291   @p peer is not modified. 291   @p peer is not modified.
292   292  
293   @return An awaitable that completes with io_result<>. 293   @return An awaitable that completes with io_result<>.
294   294  
295   @throws std::logic_error if the acceptor is not open. 295   @throws std::logic_error if the acceptor is not open.
296   */ 296   */
HITCBC 297   2 auto accept(local_stream_socket& peer) 297   2 auto accept(local_stream_socket& peer)
298   { 298   {
HITCBC 299   2 if (!is_open()) 299   2 if (!is_open())
MISUBC 300   detail::throw_logic_error("accept: acceptor not listening"); 300   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 301   2 return accept_awaitable(*this, peer); 301   2 return accept_awaitable(*this, peer);
302   } 302   }
303   303  
304   /** Initiate an asynchronous accept, returning the socket. 304   /** Initiate an asynchronous accept, returning the socket.
305   305  
306   Completes when a new connection is available. Only one 306   Completes when a new connection is available. Only one
307   accept may be in flight at a time. 307   accept may be in flight at a time.
308   308  
309   @par Cancellation 309   @par Cancellation
310   Supports cancellation via stop_token or cancel(). 310   Supports cancellation via stop_token or cancel().
311   On cancellation, yields `capy::cond::canceled` with 311   On cancellation, yields `capy::cond::canceled` with
312   a default-constructed socket. 312   a default-constructed socket.
313   313  
314   @return An awaitable that completes with 314   @return An awaitable that completes with
315   io_result<local_stream_socket>. 315   io_result<local_stream_socket>.
316   316  
317   @throws std::logic_error if the acceptor is not open. 317   @throws std::logic_error if the acceptor is not open.
318   */ 318   */
HITCBC 319   2 auto accept() 319   2 auto accept()
320   { 320   {
HITCBC 321   2 if (!is_open()) 321   2 if (!is_open())
MISUBC 322   detail::throw_logic_error("accept: acceptor not listening"); 322   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 323   2 return move_accept_awaitable(*this); 323   2 return move_accept_awaitable(*this);
324   } 324   }
325   325  
326   /** Cancel pending asynchronous accept operations. 326   /** Cancel pending asynchronous accept operations.
327   327  
328   Outstanding accept operations complete with 328   Outstanding accept operations complete with
329   @c capy::cond::canceled. Safe to call when no 329   @c capy::cond::canceled. Safe to call when no
330   operations are pending (no-op). 330   operations are pending (no-op).
331   */ 331   */
332   void cancel(); 332   void cancel();
333   333  
334   /** Release ownership of the native socket handle. 334   /** Release ownership of the native socket handle.
335   335  
336   Deregisters the acceptor from the reactor and cancels 336   Deregisters the acceptor from the reactor and cancels
337   pending operations without closing the descriptor. The 337   pending operations without closing the descriptor. The
338   caller takes ownership of the returned handle. 338   caller takes ownership of the returned handle.
339   339  
340   @return The native handle. 340   @return The native handle.
341   341  
342   @throws std::logic_error if the acceptor is not open. 342   @throws std::logic_error if the acceptor is not open.
343   343  
344   @post is_open() == false 344   @post is_open() == false
345   */ 345   */
346   native_handle_type release(); 346   native_handle_type release();
347   347  
348   /** Return the local endpoint the acceptor is bound to. 348   /** Return the local endpoint the acceptor is bound to.
349   349  
350   Returns a default-constructed (empty) endpoint if the 350   Returns a default-constructed (empty) endpoint if the
351   acceptor is not open or not yet bound. Safe to call in 351   acceptor is not open or not yet bound. Safe to call in
352   any state. 352   any state.
353   */ 353   */
354   corosio::local_endpoint local_endpoint() const noexcept; 354   corosio::local_endpoint local_endpoint() const noexcept;
355   355  
356   /** Set a socket option on the acceptor. 356   /** Set a socket option on the acceptor.
357   357  
358   Applies a type-safe socket option to the underlying socket. 358   Applies a type-safe socket option to the underlying socket.
359   The option type encodes the protocol level and option name. 359   The option type encodes the protocol level and option name.
360   360  
361   @param opt The option to set. 361   @param opt The option to set.
362   362  
363   @tparam Option A socket option type providing static 363   @tparam Option A socket option type providing static
364   `level()` and `name()` members, and `data()` / `size()` 364   `level()` and `name()` members, and `data()` / `size()`
365   accessors. 365   accessors.
366   366  
367   @throws std::logic_error if the acceptor is not open. 367   @throws std::logic_error if the acceptor is not open.
368   @throws std::system_error on failure. 368   @throws std::system_error on failure.
369   */ 369   */
370   template<class Option> 370   template<class Option>
371   void set_option(Option const& opt) 371   void set_option(Option const& opt)
372   { 372   {
373   if (!is_open()) 373   if (!is_open())
374   detail::throw_logic_error("set_option: acceptor not open"); 374   detail::throw_logic_error("set_option: acceptor not open");
375   std::error_code ec = get().set_option( 375   std::error_code ec = get().set_option(
376   Option::level(), Option::name(), opt.data(), opt.size()); 376   Option::level(), Option::name(), opt.data(), opt.size());
377   if (ec) 377   if (ec)
378   detail::throw_system_error(ec, "local_stream_acceptor::set_option"); 378   detail::throw_system_error(ec, "local_stream_acceptor::set_option");
379   } 379   }
380   380  
381   /** Get a socket option from the acceptor. 381   /** Get a socket option from the acceptor.
382   382  
383   Retrieves the current value of a type-safe socket option. 383   Retrieves the current value of a type-safe socket option.
384   384  
385   @return The current option value. 385   @return The current option value.
386   386  
387   @tparam Option A socket option type providing static 387   @tparam Option A socket option type providing static
388   `level()` and `name()` members, and `data()` / `size()` 388   `level()` and `name()` members, and `data()` / `size()`
389   / `resize()` members. 389   / `resize()` members.
390   390  
391   @throws std::logic_error if the acceptor is not open. 391   @throws std::logic_error if the acceptor is not open.
392   @throws std::system_error on failure. 392   @throws std::system_error on failure.
393   */ 393   */
394   template<class Option> 394   template<class Option>
395   Option get_option() const 395   Option get_option() const
396   { 396   {
397   if (!is_open()) 397   if (!is_open())
398   detail::throw_logic_error("get_option: acceptor not open"); 398   detail::throw_logic_error("get_option: acceptor not open");
399   Option opt{}; 399   Option opt{};
400   std::size_t sz = opt.size(); 400   std::size_t sz = opt.size();
401   std::error_code ec = 401   std::error_code ec =
402   get().get_option(Option::level(), Option::name(), opt.data(), &sz); 402   get().get_option(Option::level(), Option::name(), opt.data(), &sz);
403   if (ec) 403   if (ec)
404   detail::throw_system_error(ec, "local_stream_acceptor::get_option"); 404   detail::throw_system_error(ec, "local_stream_acceptor::get_option");
405   opt.resize(sz); 405   opt.resize(sz);
406   return opt; 406   return opt;
407   } 407   }
408   408  
409   /** Backend hooks for local stream acceptor operations. 409   /** Backend hooks for local stream acceptor operations.
410   410  
411   Platform backends derive from this to implement 411   Platform backends derive from this to implement
412   accept, option, and lifecycle management. 412   accept, option, and lifecycle management.
413   */ 413   */
414   struct implementation : io_object::implementation 414   struct implementation : io_object::implementation
415   { 415   {
416   /** Initiate an asynchronous accept. 416   /** Initiate an asynchronous accept.
417   417  
418   On completion the backend sets @p *ec and, on 418   On completion the backend sets @p *ec and, on
419   success, stores a pointer to the new socket 419   success, stores a pointer to the new socket
420   implementation in @p *impl_out. 420   implementation in @p *impl_out.
421   421  
422   @param h Coroutine handle to resume. 422   @param h Coroutine handle to resume.
423   @param ex Executor for dispatching the completion. 423   @param ex Executor for dispatching the completion.
424   @param token Stop token for cancellation. 424   @param token Stop token for cancellation.
425   @param ec Output error code. 425   @param ec Output error code.
426   @param impl_out Output pointer for the accepted socket. 426   @param impl_out Output pointer for the accepted socket.
427   @return Coroutine handle to resume immediately. 427   @return Coroutine handle to resume immediately.
428   */ 428   */
429   virtual std::coroutine_handle<> accept( 429   virtual std::coroutine_handle<> accept(
430   std::coroutine_handle<>, 430   std::coroutine_handle<>,
431   capy::executor_ref, 431   capy::executor_ref,
432   std::stop_token, 432   std::stop_token,
433   std::error_code*, 433   std::error_code*,
434   io_object::implementation**) = 0; 434   io_object::implementation**) = 0;
435   435  
436   /// Return the cached local endpoint. 436   /// Return the cached local endpoint.
437   virtual corosio::local_endpoint local_endpoint() const noexcept = 0; 437   virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
438   438  
439   /// Return whether the underlying socket is open. 439   /// Return whether the underlying socket is open.
440   virtual bool is_open() const noexcept = 0; 440   virtual bool is_open() const noexcept = 0;
441   441  
442   /// Release and return the native handle without closing. 442   /// Release and return the native handle without closing.
443   virtual native_handle_type release_socket() noexcept = 0; 443   virtual native_handle_type release_socket() noexcept = 0;
444   444  
445   /// Cancel pending accept operations. 445   /// Cancel pending accept operations.
446   virtual void cancel() noexcept = 0; 446   virtual void cancel() noexcept = 0;
447   447  
448   /// Set a raw socket option. 448   /// Set a raw socket option.
449   virtual std::error_code set_option( 449   virtual std::error_code set_option(
450   int level, 450   int level,
451   int optname, 451   int optname,
452   void const* data, 452   void const* data,
453   std::size_t size) noexcept = 0; 453   std::size_t size) noexcept = 0;
454   454  
455   /// Get a raw socket option. 455   /// Get a raw socket option.
456   virtual std::error_code 456   virtual std::error_code
457   get_option(int level, int optname, void* data, std::size_t* size) 457   get_option(int level, int optname, void* data, std::size_t* size)
458   const noexcept = 0; 458   const noexcept = 0;
459   }; 459   };
460   460  
461   protected: 461   protected:
462   local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept 462   local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
463   : io_object(std::move(h)) 463   : io_object(std::move(h))
464   , ctx_(ctx) 464   , ctx_(ctx)
465   { 465   {
466   } 466   }
467   467  
468   local_stream_acceptor( 468   local_stream_acceptor(
469   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept 469   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
470   : io_object(std::move(other)) 470   : io_object(std::move(other))
471   , ctx_(ctx) 471   , ctx_(ctx)
472   { 472   {
473   } 473   }
474   474  
HITCBC 475   2 static void reset_peer_impl( 475   2 static void reset_peer_impl(
476   local_stream_socket& peer, io_object::implementation* impl) noexcept 476   local_stream_socket& peer, io_object::implementation* impl) noexcept
477   { 477   {
HITCBC 478   2 if (impl) 478   2 if (impl)
HITCBC 479   2 peer.h_.reset(impl); 479   2 peer.h_.reset(impl);
HITCBC 480   2 } 480   2 }
481   481  
482   private: 482   private:
483   capy::execution_context& ctx_; 483   capy::execution_context& ctx_;
484   484  
HITCBC 485   48 inline implementation& get() const noexcept 485   48 inline implementation& get() const noexcept
486   { 486   {
HITCBC 487   48 return *static_cast<implementation*>(h_.get()); 487   48 return *static_cast<implementation*>(h_.get());
488   } 488   }
489   }; 489   };
490   490  
491   } // namespace boost::corosio 491   } // namespace boost::corosio
492   492  
493   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP 493   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP