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   14 impl_type& get_impl() noexcept 75   14 impl_type& get_impl() noexcept
76   { 76   {
HITCBC 77   14 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   10 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   10 : acc_(acc) 109   10 : acc_(acc)
HITCBC 110   10 , peer_(peer) 110   10 , peer_(peer)
111   { 111   {
HITCBC 112   10 } 112   10 }
113   113  
114   // A peer accepted for an awaiter that never resumed is closed 114   // A peer accepted for an awaiter that never resumed is closed
115   // here, or nothing would own it. 115   // here, or nothing would own it.
HITCBC 116   20 ~native_accept_awaitable() 116   20 ~native_accept_awaitable()
117   { 117   {
HITCBC 118   20 if (peer_impl_) 118   20 if (peer_impl_)
HITCBC 119   2 discard_peer(acc_, peer_impl_); 119   2 discard_peer(acc_, peer_impl_);
HITCBC 120   20 } 120   20 }
121   121  
HITCBC 122   8 [[nodiscard]] capy::results<> await_resume() const noexcept 122   8 [[nodiscard]] capy::results<> await_resume() const noexcept
123   { 123   {
HITCBC 124   8 if (!this->ec_) 124   8 if (!this->ec_)
HITCBC 125   4 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr)); 125   4 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr));
HITCBC 126   8 return {this->ec_}; 126   8 return {this->ec_};
127   } 127   }
128   128  
129   std::coroutine_handle<> 129   std::coroutine_handle<>
HITCBC 130   6 dispatch(capy::continuation& cont, capy::executor_ref ex) const 130   6 dispatch(capy::continuation& cont, capy::executor_ref ex) const
131   { 131   {
HITCBC 132   18 return acc_.get_impl().accept( 132   18 return acc_.get_impl().accept(
HITCBC 133   18 cont, ex, this->token_, &this->ec_, &peer_impl_); 133   18 cont, ex, this->token_, &this->ec_, &peer_impl_);
134   } 134   }
135   }; 135   };
136   136  
137   struct native_move_accept_awaitable 137   struct native_move_accept_awaitable
138   : detail::void_op_base<native_move_accept_awaitable> 138   : detail::void_op_base<native_move_accept_awaitable>
139   { 139   {
140   native_local_stream_acceptor& acc_; 140   native_local_stream_acceptor& acc_;
141   mutable io_object::implementation* peer_impl_ = nullptr; 141   mutable io_object::implementation* peer_impl_ = nullptr;
142   142  
HITCBC 143   10 explicit native_move_accept_awaitable( 143   10 explicit native_move_accept_awaitable(
144   native_local_stream_acceptor& acc) noexcept 144   native_local_stream_acceptor& acc) noexcept
HITCBC 145   10 : acc_(acc) 145   10 : acc_(acc)
146   { 146   {
HITCBC 147   10 } 147   10 }
148   148  
149   // A peer accepted for an awaiter that never resumed is closed 149   // A peer accepted for an awaiter that never resumed is closed
150   // here, or nothing would own it. 150   // here, or nothing would own it.
HITCBC 151   20 ~native_move_accept_awaitable() 151   20 ~native_move_accept_awaitable()
152   { 152   {
HITCBC 153   20 if (peer_impl_) 153   20 if (peer_impl_)
HITCBC 154   2 discard_peer(acc_, peer_impl_); 154   2 discard_peer(acc_, peer_impl_);
HITCBC 155   20 } 155   20 }
156   156  
157   [[nodiscard]] capy::results<native_local_stream_socket<Backend>> 157   [[nodiscard]] capy::results<native_local_stream_socket<Backend>>
HITCBC 158   8 await_resume() const noexcept 158   8 await_resume() const noexcept
159   { 159   {
HITCBC 160   8 if (this->ec_ || !peer_impl_) 160   8 if (this->ec_ || !peer_impl_)
HITCBC 161   6 return {this->ec_, native_local_stream_socket<Backend>()}; 161   6 return {this->ec_, native_local_stream_socket<Backend>()};
162   162  
HITCBC 163   2 native_local_stream_socket<Backend> peer(acc_.context()); 163   2 native_local_stream_socket<Backend> peer(acc_.context());
HITCBC 164   2 acc_.reset_peer_impl(peer, std::exchange(peer_impl_, nullptr)); 164   2 acc_.reset_peer_impl(peer, std::exchange(peer_impl_, nullptr));
HITCBC 165   2 return {this->ec_, std::move(peer)}; 165   2 return {this->ec_, std::move(peer)};
HITCBC 166   2 } 166   2 }
167   167  
168   std::coroutine_handle<> 168   std::coroutine_handle<>
HITCBC 169   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const 169   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
170   { 170   {
HITCBC 171   12 return acc_.get_impl().accept( 171   12 return acc_.get_impl().accept(
HITCBC 172   12 cont, ex, this->token_, &this->ec_, &peer_impl_); 172   12 cont, ex, this->token_, &this->ec_, &peer_impl_);
173   } 173   }
174   }; 174   };
175   175  
176   public: 176   public:
177   /** Construct a native acceptor from an execution context. 177   /** Construct a native acceptor from an execution context.
178   178  
179   @param ctx The execution context that owns this acceptor. 179   @param ctx The execution context that owns this acceptor.
180   */ 180   */
HITCBC 181   22 explicit native_local_stream_acceptor(capy::execution_context& ctx) 181   22 explicit native_local_stream_acceptor(capy::execution_context& ctx)
HITCBC 182   22 : local_stream_acceptor(handle(ctx, ctx.use_service<service_type>()), ctx) 182   22 : local_stream_acceptor(handle(ctx, ctx.use_service<service_type>()), ctx)
183   { 183   {
HITCBC 184   22 } 184   22 }
185   185  
186   /** Construct a native acceptor from an executor. 186   /** Construct a native acceptor from an executor.
187   187  
188   @param ex The executor whose context owns the acceptor. 188   @param ex The executor whose context owns the acceptor.
189   */ 189   */
190   template<class Ex> 190   template<class Ex>
191   requires(!std::same_as< 191   requires(!std::same_as<
192   std::remove_cvref_t<Ex>, 192   std::remove_cvref_t<Ex>,
193   native_local_stream_acceptor>) && 193   native_local_stream_acceptor>) &&
194   capy::Executor<Ex> 194   capy::Executor<Ex>
195   explicit native_local_stream_acceptor(Ex const& ex) 195   explicit native_local_stream_acceptor(Ex const& ex)
196   : native_local_stream_acceptor(ex.context()) 196   : native_local_stream_acceptor(ex.context())
197   { 197   {
198   } 198   }
199   199  
200   /** Construct a native acceptor that is open, bound, and listening. 200   /** Construct a native acceptor that is open, bound, and listening.
201   201  
202   Creates a fully bound listening acceptor in a single 202   Creates a fully bound listening acceptor in a single
203   expression, throwing the codes the piecewise `open()` + 203   expression, throwing the codes the piecewise `open()` +
204   `bind()` + `listen()` path returns. 204   `bind()` + `listen()` path returns.
205   205  
206   @param ctx The execution context that owns this acceptor. 206   @param ctx The execution context that owns this acceptor.
207   @param ep The local endpoint to bind to. 207   @param ep The local endpoint to bind to.
208   @param backlog The maximum pending connection queue length. 208   @param backlog The maximum pending connection queue length.
209   209  
210   @throws std::system_error on open, bind, or listen failure. 210   @throws std::system_error on open, bind, or listen failure.
211   */ 211   */
HITCBC 212   4 native_local_stream_acceptor( 212   4 native_local_stream_acceptor(
213   capy::execution_context& ctx, 213   capy::execution_context& ctx,
214   corosio::local_endpoint ep, 214   corosio::local_endpoint ep,
215   int backlog = 128) 215   int backlog = 128)
216   : local_stream_acceptor( 216   : local_stream_acceptor(
HITCBC 217   8 handle(ctx, ctx.use_service<service_type>()), 217   8 handle(ctx, ctx.use_service<service_type>()),
218   ctx, 218   ctx,
HITCBC 219   4 std::move(ep), 219   4 std::move(ep),
HITCBC 220   8 backlog) 220   8 backlog)
221   { 221   {
HITCBC 222   2 } 222   2 }
223   223  
224   /** Construct a native acceptor that is open, bound, and listening. 224   /** Construct a native acceptor that is open, bound, and listening.
225   225  
226   @param ex The executor whose context owns the acceptor. 226   @param ex The executor whose context owns the acceptor.
227   @param ep The local endpoint to bind to. 227   @param ep The local endpoint to bind to.
228   @param backlog The maximum pending connection queue length. 228   @param backlog The maximum pending connection queue length.
229   229  
230   @tparam Ex A type satisfying @ref capy::Executor. 230   @tparam Ex A type satisfying @ref capy::Executor.
231   231  
232   @throws std::system_error on open, bind, or listen failure. 232   @throws std::system_error on open, bind, or listen failure.
233   */ 233   */
234   template<class Ex> 234   template<class Ex>
235   requires capy::Executor<Ex> 235   requires capy::Executor<Ex>
HITCBC 236   2 native_local_stream_acceptor( 236   2 native_local_stream_acceptor(
237   Ex const& ex, corosio::local_endpoint ep, int backlog = 128) 237   Ex const& ex, corosio::local_endpoint ep, int backlog = 128)
HITCBC 238   2 : native_local_stream_acceptor(ex.context(), std::move(ep), backlog) 238   2 : native_local_stream_acceptor(ex.context(), std::move(ep), backlog)
239   { 239   {
MISUBC 240   ✗ } 240   ✗ }
241   241  
242   /// Move construct. 242   /// Move construct.
HITCBC 243   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept = 243   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept =
244   default; 244   default;
245   245  
246   /// Move assign. 246   /// Move assign.
247   native_local_stream_acceptor& 247   native_local_stream_acceptor&
248   operator=(native_local_stream_acceptor&&) noexcept = default; 248   operator=(native_local_stream_acceptor&&) noexcept = default;
249   249  
250   /// Copy construction is disabled; the handle is uniquely owned. 250   /// Copy construction is disabled; the handle is uniquely owned.
251   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete; 251   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete;
252   /// Copy assignment is disabled; the handle is uniquely owned. 252   /// Copy assignment is disabled; the handle is uniquely owned.
253   native_local_stream_acceptor& 253   native_local_stream_acceptor&
254   operator=(native_local_stream_acceptor const&) = delete; 254   operator=(native_local_stream_acceptor const&) = delete;
255   255  
256   /** Asynchronously accept an incoming connection. 256   /** Asynchronously accept an incoming connection.
257   257  
258   Calls the backend implementation directly, bypassing virtual 258   Calls the backend implementation directly, bypassing virtual
259   dispatch. Otherwise identical to @ref local_stream_acceptor::accept. 259   dispatch. Otherwise identical to @ref local_stream_acceptor::accept.
260   260  
261   @param peer The socket to receive the accepted connection. 261   @param peer The socket to receive the accepted connection.
262   262  
263   @return An awaitable yielding `results<>`. 263   @return An awaitable yielding `results<>`.
264   264  
265   A closed or moved-from acceptor reports 265   A closed or moved-from acceptor reports
266   `errc::bad_file_descriptor`. 266   `errc::bad_file_descriptor`.
267   267  
268   Both this acceptor and @p peer must outlive the returned 268   Both this acceptor and @p peer must outlive the returned
269   awaitable. 269   awaitable.
270   */ 270   */
HITCBC 271   10 [[nodiscard]] auto accept(local_stream_socket& peer) 271   10 [[nodiscard]] auto accept(local_stream_socket& peer)
272   { 272   {
HITCBC 273   10 native_accept_awaitable aw(*this, peer); 273   10 native_accept_awaitable aw(*this, peer);
HITCBC 274   10 if (!is_open()) 274   10 if (!is_open())
HITCBC 275   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 275   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 276   10 return aw; 276   10 return aw;
277   } 277   }
278   278  
279   /** Asynchronously accept an incoming connection, returning the peer. 279   /** Asynchronously accept an incoming connection, returning the peer.
280   280  
281   Calls the backend implementation directly, bypassing virtual 281   Calls the backend implementation directly, bypassing virtual
282   dispatch. The accepted peer is returned as a 282   dispatch. The accepted peer is returned as a
283   @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
284   is also devirtualized. 284   is also devirtualized.
285   285  
286   @return An awaitable yielding 286   @return An awaitable yielding
287   `results<native_local_stream_socket<Backend>>`. 287   `results<native_local_stream_socket<Backend>>`.
288   288  
289   A closed or moved-from acceptor reports 289   A closed or moved-from acceptor reports
290   `errc::bad_file_descriptor`. 290   `errc::bad_file_descriptor`.
291   On failure the returned socket is default-constructed and not 291   On failure the returned socket is default-constructed and not
292   open. It has no context, so it may only be destroyed, assigned, 292   open. It has no context, so it may only be destroyed, assigned,
293   or checked with `is_open()`. 293   or checked with `is_open()`.
294   294  
295   This acceptor must outlive the returned awaitable. 295   This acceptor must outlive the returned awaitable.
296   */ 296   */
HITCBC 297   10 [[nodiscard]] auto accept() 297   10 [[nodiscard]] auto accept()
298   { 298   {
HITCBC 299   10 native_move_accept_awaitable aw(*this); 299   10 native_move_accept_awaitable aw(*this);
HITCBC 300   10 if (!is_open()) 300   10 if (!is_open())
HITCBC 301   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 301   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 302   10 return aw; 302   10 return aw;
303   } 303   }
304   304  
305   /** Asynchronously wait for the acceptor to be ready. 305   /** Asynchronously wait for the acceptor to be ready.
306   306  
307   Calls the backend implementation directly, bypassing virtual 307   Calls the backend implementation directly, bypassing virtual
308   dispatch. Otherwise identical to @ref local_stream_acceptor::wait. 308   dispatch. Otherwise identical to @ref local_stream_acceptor::wait.
309   309  
310   @param w The wait direction (typically `wait_type::read`). 310   @param w The wait direction (typically `wait_type::read`).
311   311  
312   @return An awaitable yielding `results<>`. 312   @return An awaitable yielding `results<>`.
313   */ 313   */
HITCBC 314   6 [[nodiscard]] auto wait(wait_type w) 314   6 [[nodiscard]] auto wait(wait_type w)
315   { 315   {
HITCBC 316   6 return native_wait_awaitable(*this, w); 316   6 return native_wait_awaitable(*this, w);
317   } 317   }
318   }; 318   };
319   319  
320   } // namespace boost::corosio 320   } // namespace boost::corosio
321   321  
322   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 322   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP