98.44% Lines (63/64) 100.00% Functions (18/18)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
13   13  
14   #include <boost/corosio/local_stream_acceptor.hpp> 14   #include <boost/corosio/local_stream_acceptor.hpp>
15   #include <boost/corosio/native/native_local_stream_socket.hpp> 15   #include <boost/corosio/native/native_local_stream_socket.hpp>
16   #include <boost/corosio/backend.hpp> 16   #include <boost/corosio/backend.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 17   #include <boost/corosio/detail/op_base.hpp>
18   18  
19   #ifndef BOOST_COROSIO_MRDOCS 19   #ifndef BOOST_COROSIO_MRDOCS
20   #if BOOST_COROSIO_HAS_EPOLL 20   #if BOOST_COROSIO_HAS_EPOLL
21   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 21   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
22   #endif 22   #endif
23   23  
24   #if BOOST_COROSIO_HAS_SELECT 24   #if BOOST_COROSIO_HAS_SELECT
25   #include <boost/corosio/native/detail/select/select_types.hpp> 25   #include <boost/corosio/native/detail/select/select_types.hpp>
26   #endif 26   #endif
27   27  
28   #if BOOST_COROSIO_HAS_KQUEUE 28   #if BOOST_COROSIO_HAS_KQUEUE
29   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 29   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
30   #endif 30   #endif
31   31  
32   #if BOOST_COROSIO_HAS_URING 32   #if BOOST_COROSIO_HAS_URING
33   #include <boost/corosio/native/detail/uring/uring_types.hpp> 33   #include <boost/corosio/native/detail/uring/uring_types.hpp>
34   #endif 34   #endif
35   35  
36   #if BOOST_COROSIO_HAS_IOCP 36   #if BOOST_COROSIO_HAS_IOCP
37   #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp> 37   #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp>
38   #endif 38   #endif
39   #endif // !BOOST_COROSIO_MRDOCS 39   #endif // !BOOST_COROSIO_MRDOCS
40   40  
41   namespace boost::corosio { 41   namespace boost::corosio {
42   42  
43   /** Accepts Unix domain stream connections, calling the backend directly. 43   /** Accepts Unix domain stream connections, calling the backend directly.
44   44  
45   This class template inherits from @ref local_stream_acceptor. It 45   This class template inherits from @ref local_stream_acceptor. It
46   shadows both `accept` overloads (the peer-reference form and the 46   shadows both `accept` overloads (the peer-reference form and the
47   move-return form) with versions that call the backend implementation 47   move-return form) with versions that call the backend implementation
48   directly. The compiler can then inline through the entire call 48   directly. The compiler can then inline through the entire call
49   chain. The move-return form yields a 49   chain. The move-return form yields a
50   @ref native_local_stream_socket so subsequent I/O on the peer 50   @ref native_local_stream_socket so subsequent I/O on the peer
51   is also devirtualized. 51   is also devirtualized.
52   52  
53   Non-async operations (`listen`, `close`, `cancel`) remain 53   Non-async operations (`listen`, `close`, `cancel`) remain
54   unchanged and dispatch through the compiled library. 54   unchanged and dispatch through the compiled library.
55   55  
56   A `native_local_stream_acceptor` IS-A `local_stream_acceptor` 56   A `native_local_stream_acceptor` IS-A `local_stream_acceptor`
57   and can be passed to any function expecting 57   and can be passed to any function expecting
58   `local_stream_acceptor&`. 58   `local_stream_acceptor&`.
59   59  
60   @tparam Backend A backend tag value (e.g., `epoll`). 60   @tparam Backend A backend tag value (e.g., `epoll`).
61   61  
62   @par Thread Safety 62   @par Thread Safety
63   Same as @ref local_stream_acceptor. 63   Same as @ref local_stream_acceptor.
64   64  
65   @see @ref local_stream_acceptor, @ref epoll_t, @ref iocp_t 65   @see @ref local_stream_acceptor, @ref epoll_t, @ref iocp_t
66   */ 66   */
67   template<auto Backend> 67   template<auto Backend>
68   class native_local_stream_acceptor : public local_stream_acceptor 68   class native_local_stream_acceptor : public local_stream_acceptor
69   { 69   {
70   using backend_type = decltype(Backend); 70   using backend_type = decltype(Backend);
71   using impl_type = typename backend_type::local_stream_acceptor_type; 71   using impl_type = typename backend_type::local_stream_acceptor_type;
72   using service_type = 72   using service_type =
73   typename backend_type::local_stream_acceptor_service_type; 73   typename backend_type::local_stream_acceptor_service_type;
74   74  
HITCBC 75   10 impl_type& get_impl() noexcept 75   14 impl_type& get_impl() noexcept
76   { 76   {
HITCBC 77   10 return *static_cast<impl_type*>(h_.get()); 77   14 return *static_cast<impl_type*>(h_.get());
78   } 78   }
79   79  
80   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable> 80   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
81   { 81   {
82   native_local_stream_acceptor& acc_; 82   native_local_stream_acceptor& acc_;
83   wait_type w_; 83   wait_type w_;
84   84  
HITCBC 85   6 native_wait_awaitable( 85   6 native_wait_awaitable(
86   native_local_stream_acceptor& acc, wait_type w) noexcept 86   native_local_stream_acceptor& acc, wait_type w) noexcept
HITCBC 87   6 : acc_(acc) 87   6 : acc_(acc)
HITCBC 88   6 , w_(w) 88   6 , w_(w)
89   { 89   {
HITCBC 90   6 } 90   6 }
91   91  
92   std::coroutine_handle<> 92   std::coroutine_handle<>
HITCBC 93   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const 93   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
94   { 94   {
HITCBC 95   4 return acc_.get_impl().wait(cont, ex, w_, this->token_, &this->ec_); 95   4 return acc_.get_impl().wait(cont, ex, w_, this->token_, &this->ec_);
96   } 96   }
97   }; 97   };
98   98  
99   struct native_accept_awaitable 99   struct native_accept_awaitable
100   : detail::void_op_base<native_accept_awaitable> 100   : detail::void_op_base<native_accept_awaitable>
101   { 101   {
102   native_local_stream_acceptor& acc_; 102   native_local_stream_acceptor& acc_;
103   local_stream_socket& peer_; 103   local_stream_socket& peer_;
104   mutable io_object::implementation* peer_impl_ = nullptr; 104   mutable io_object::implementation* peer_impl_ = nullptr;
105   105  
HITCBC 106   8 native_accept_awaitable( 106   10 native_accept_awaitable(
107   native_local_stream_acceptor& acc, 107   native_local_stream_acceptor& acc,
108   local_stream_socket& peer) noexcept 108   local_stream_socket& peer) noexcept
HITCBC 109   8 : acc_(acc) 109   10 : acc_(acc)
HITCBC 110   8 , peer_(peer) 110   10 , peer_(peer)
111   { 111   {
HITCBC 112   8 } 112   10 }
113   113  
  114 + // A peer accepted for an awaiter that never resumed is closed
  115 + // here, or nothing would own it.
HITGNC   116 + 20 ~native_accept_awaitable()
  117 + {
HITGNC   118 + 20 if (peer_impl_)
HITGNC   119 + 2 discard_peer(acc_, peer_impl_);
HITGNC   120 + 20 }
  121 +
HITCBC 114   8 [[nodiscard]] capy::results<> await_resume() const noexcept 122   8 [[nodiscard]] capy::results<> await_resume() const noexcept
115   { 123   {
HITCBC 116   8 if (!this->ec_) 124   8 if (!this->ec_)
HITCBC 117 - 4 acc_.reset_peer_impl(peer_, peer_impl_); 125 + 4 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr));
HITCBC 118   8 return {this->ec_}; 126   8 return {this->ec_};
119   } 127   }
120   128  
121   std::coroutine_handle<> 129   std::coroutine_handle<>
HITCBC 122   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const 130   6 dispatch(capy::continuation& cont, capy::executor_ref ex) const
123   { 131   {
HITCBC 124   12 return acc_.get_impl().accept( 132   18 return acc_.get_impl().accept(
HITCBC 125   12 cont, ex, this->token_, &this->ec_, &peer_impl_); 133   18 cont, ex, this->token_, &this->ec_, &peer_impl_);
126   } 134   }
127   }; 135   };
128   136  
129   struct native_move_accept_awaitable 137   struct native_move_accept_awaitable
130   : detail::void_op_base<native_move_accept_awaitable> 138   : detail::void_op_base<native_move_accept_awaitable>
131   { 139   {
132   native_local_stream_acceptor& acc_; 140   native_local_stream_acceptor& acc_;
133   mutable io_object::implementation* peer_impl_ = nullptr; 141   mutable io_object::implementation* peer_impl_ = nullptr;
134   142  
HITCBC 135   6 explicit native_move_accept_awaitable( 143   10 explicit native_move_accept_awaitable(
136   native_local_stream_acceptor& acc) noexcept 144   native_local_stream_acceptor& acc) noexcept
HITCBC 137   6 : acc_(acc) 145   10 : acc_(acc)
138   { 146   {
HITCBC 139   6 } 147   10 }
140   148  
  149 + // A peer accepted for an awaiter that never resumed is closed
  150 + // here, or nothing would own it.
HITGNC   151 + 20 ~native_move_accept_awaitable()
  152 + {
HITGNC   153 + 20 if (peer_impl_)
HITGNC   154 + 2 discard_peer(acc_, peer_impl_);
HITGNC   155 + 20 }
  156 +
141   [[nodiscard]] capy::results<native_local_stream_socket<Backend>> 157   [[nodiscard]] capy::results<native_local_stream_socket<Backend>>
HITCBC 142   6 await_resume() const noexcept 158   8 await_resume() const noexcept
143   { 159   {
HITCBC 144   6 if (this->ec_ || !peer_impl_) 160   8 if (this->ec_ || !peer_impl_)
HITGIC 145 - return { 161 + 6 return {this->ec_, native_local_stream_socket<Backend>()};
DCB 146 - 4 this->ec_,  
DCB 147 - 4 native_local_stream_socket<Backend>(acc_.context())};  
148   162  
HITCBC 149   2 native_local_stream_socket<Backend> peer(acc_.context()); 163   2 native_local_stream_socket<Backend> peer(acc_.context());
HITCBC 150 - 2 acc_.reset_peer_impl(peer, peer_impl_); 164 + 2 acc_.reset_peer_impl(peer, std::exchange(peer_impl_, nullptr));
HITCBC 151   2 return {this->ec_, std::move(peer)}; 165   2 return {this->ec_, std::move(peer)};
HITCBC 152   2 } 166   2 }
153   167  
154   std::coroutine_handle<> 168   std::coroutine_handle<>
HITCBC 155   2 dispatch(capy::continuation& cont, capy::executor_ref ex) const 169   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
156   { 170   {
HITCBC 157   6 return acc_.get_impl().accept( 171   12 return acc_.get_impl().accept(
HITCBC 158   6 cont, ex, this->token_, &this->ec_, &peer_impl_); 172   12 cont, ex, this->token_, &this->ec_, &peer_impl_);
159   } 173   }
160   }; 174   };
161   175  
162   public: 176   public:
163   /** Construct a native acceptor from an execution context. 177   /** Construct a native acceptor from an execution context.
164   178  
165   @param ctx The execution context that owns this acceptor. 179   @param ctx The execution context that owns this acceptor.
166   */ 180   */
HITCBC 167   18 explicit native_local_stream_acceptor(capy::execution_context& ctx) 181   22 explicit native_local_stream_acceptor(capy::execution_context& ctx)
HITCBC 168   18 : local_stream_acceptor(handle(ctx, ctx.use_service<service_type>()), ctx) 182   22 : local_stream_acceptor(handle(ctx, ctx.use_service<service_type>()), ctx)
169   { 183   {
HITCBC 170   18 } 184   22 }
171   185  
172   /** Construct a native acceptor from an executor. 186   /** Construct a native acceptor from an executor.
173   187  
174   @param ex The executor whose context owns the acceptor. 188   @param ex The executor whose context owns the acceptor.
175   */ 189   */
176   template<class Ex> 190   template<class Ex>
177   requires(!std::same_as< 191   requires(!std::same_as<
178   std::remove_cvref_t<Ex>, 192   std::remove_cvref_t<Ex>,
179   native_local_stream_acceptor>) && 193   native_local_stream_acceptor>) &&
180   capy::Executor<Ex> 194   capy::Executor<Ex>
181   explicit native_local_stream_acceptor(Ex const& ex) 195   explicit native_local_stream_acceptor(Ex const& ex)
182   : native_local_stream_acceptor(ex.context()) 196   : native_local_stream_acceptor(ex.context())
183   { 197   {
184   } 198   }
185   199  
  200 + /** Construct a native acceptor that is open, bound, and listening.
  201 +
  202 + Creates a fully bound listening acceptor in a single
  203 + expression, throwing the codes the piecewise `open()` +
  204 + `bind()` + `listen()` path returns.
  205 +
  206 + @param ctx The execution context that owns this acceptor.
  207 + @param ep The local endpoint to bind to.
  208 + @param backlog The maximum pending connection queue length.
  209 +
  210 + @throws std::system_error on open, bind, or listen failure.
  211 + */
HITGNC   212 + 4 native_local_stream_acceptor(
  213 + capy::execution_context& ctx,
  214 + corosio::local_endpoint ep,
  215 + int backlog = 128)
  216 + : local_stream_acceptor(
HITGNC   217 + 8 handle(ctx, ctx.use_service<service_type>()),
  218 + ctx,
HITGNC   219 + 4 std::move(ep),
HITGNC   220 + 8 backlog)
  221 + {
HITGNC   222 + 2 }
  223 +
  224 + /** Construct a native acceptor that is open, bound, and listening.
  225 +
  226 + @param ex The executor whose context owns the acceptor.
  227 + @param ep The local endpoint to bind to.
  228 + @param backlog The maximum pending connection queue length.
  229 +
  230 + @tparam Ex A type satisfying @ref capy::Executor.
  231 +
  232 + @throws std::system_error on open, bind, or listen failure.
  233 + */
  234 + template<class Ex>
  235 + requires capy::Executor<Ex>
HITGNC   236 + 2 native_local_stream_acceptor(
  237 + Ex const& ex, corosio::local_endpoint ep, int backlog = 128)
HITGNC   238 + 2 : native_local_stream_acceptor(ex.context(), std::move(ep), backlog)
  239 + {
MISUNC   240 + ✗ }
  241 +
186   /// Move construct. 242   /// Move construct.
HITCBC 187   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept = 243   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept =
188   default; 244   default;
189   245  
190   /// Move assign. 246   /// Move assign.
191   native_local_stream_acceptor& 247   native_local_stream_acceptor&
192   operator=(native_local_stream_acceptor&&) noexcept = default; 248   operator=(native_local_stream_acceptor&&) noexcept = default;
193   249  
194   /// Copy construction is disabled; the handle is uniquely owned. 250   /// Copy construction is disabled; the handle is uniquely owned.
195   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete; 251   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete;
196   /// Copy assignment is disabled; the handle is uniquely owned. 252   /// Copy assignment is disabled; the handle is uniquely owned.
197   native_local_stream_acceptor& 253   native_local_stream_acceptor&
198   operator=(native_local_stream_acceptor const&) = delete; 254   operator=(native_local_stream_acceptor const&) = delete;
199   255  
200   /** Asynchronously accept an incoming connection. 256   /** Asynchronously accept an incoming connection.
201   257  
202   Calls the backend implementation directly, bypassing virtual 258   Calls the backend implementation directly, bypassing virtual
203   dispatch. Otherwise identical to @ref local_stream_acceptor::accept. 259   dispatch. Otherwise identical to @ref local_stream_acceptor::accept.
204   260  
205   @param peer The socket to receive the accepted connection. 261   @param peer The socket to receive the accepted connection.
206   262  
207   @return An awaitable yielding `results<>`. 263   @return An awaitable yielding `results<>`.
208   264  
209 - A closed acceptor reports `errc::bad_file_descriptor`. 265 + A closed or moved-from acceptor reports
  266 + `errc::bad_file_descriptor`.
210   267  
211   Both this acceptor and @p peer must outlive the returned 268   Both this acceptor and @p peer must outlive the returned
212   awaitable. 269   awaitable.
213   */ 270   */
HITCBC 214   8 [[nodiscard]] auto accept(local_stream_socket& peer) 271   10 [[nodiscard]] auto accept(local_stream_socket& peer)
215   { 272   {
HITCBC 216   8 native_accept_awaitable aw(*this, peer); 273   10 native_accept_awaitable aw(*this, peer);
HITCBC 217   8 if (!is_open()) 274   10 if (!is_open())
HITCBC 218   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 275   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 219   8 return aw; 276   10 return aw;
220   } 277   }
221   278  
222   /** Asynchronously accept an incoming connection, returning the peer. 279   /** Asynchronously accept an incoming connection, returning the peer.
223   280  
224   Calls the backend implementation directly, bypassing virtual 281   Calls the backend implementation directly, bypassing virtual
225   dispatch. The accepted peer is returned as a 282   dispatch. The accepted peer is returned as a
226   @ref native_local_stream_socket so that subsequent I/O on it 283   @ref native_local_stream_socket so that subsequent I/O on it
227   is also devirtualized. 284   is also devirtualized.
228   285  
229   @return An awaitable yielding 286   @return An awaitable yielding
230   `results<native_local_stream_socket<Backend>>`. 287   `results<native_local_stream_socket<Backend>>`.
231   288  
232 - A closed acceptor reports `errc::bad_file_descriptor`. 289 + A closed or moved-from acceptor reports
233 - 290 + `errc::bad_file_descriptor`.
234 - @throws std::logic_error If the acceptor is moved-from. 291 + On failure the returned socket is default-constructed and not
  292 + open. It has no context, so it may only be destroyed, assigned,
  293 + or checked with `is_open()`.
235   294  
236   This acceptor must outlive the returned awaitable. 295   This acceptor must outlive the returned awaitable.
237   */ 296   */
HITCBC 238   8 [[nodiscard]] auto accept() 297   10 [[nodiscard]] auto accept()
239 - // The awaitable builds the peer from context(), which a  
240 - // moved-from acceptor no longer has.  
241 - if (!h_)  
DCB 242 - 8 detail::throw_logic_error("accept: acceptor moved-from");  
ECB 243   2 { 298   {
HITCBC 244   6 native_move_accept_awaitable aw(*this); 299   10 native_move_accept_awaitable aw(*this);
HITCBC 245   6 if (!is_open()) 300   10 if (!is_open())
HITCBC 246   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 301   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 247   6 return aw; 302   10 return aw;
248   } 303   }
249   304  
250   /** Asynchronously wait for the acceptor to be ready. 305   /** Asynchronously wait for the acceptor to be ready.
251   306  
252   Calls the backend implementation directly, bypassing virtual 307   Calls the backend implementation directly, bypassing virtual
253   dispatch. Otherwise identical to @ref local_stream_acceptor::wait. 308   dispatch. Otherwise identical to @ref local_stream_acceptor::wait.
254   309  
255   @param w The wait direction (typically `wait_type::read`). 310   @param w The wait direction (typically `wait_type::read`).
256   311  
257   @return An awaitable yielding `results<>`. 312   @return An awaitable yielding `results<>`.
258   */ 313   */
HITCBC 259   6 [[nodiscard]] auto wait(wait_type w) 314   6 [[nodiscard]] auto wait(wait_type w)
260   { 315   {
HITCBC 261   6 return native_wait_awaitable(*this, w); 316   6 return native_wait_awaitable(*this, w);
262   } 317   }
263   }; 318   };
264   319  
265   } // namespace boost::corosio 320   } // namespace boost::corosio
266   321  
267   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 322   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP