100.00% Lines (70/70) 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   // 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_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
11   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 11   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
12   12  
13   #include <boost/corosio/local_stream_acceptor.hpp> 13   #include <boost/corosio/local_stream_acceptor.hpp>
14   #include <boost/corosio/native/native_local_stream_socket.hpp> 14   #include <boost/corosio/native/native_local_stream_socket.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   16  
17   #ifndef BOOST_COROSIO_MRDOCS 17   #ifndef BOOST_COROSIO_MRDOCS
18   #if BOOST_COROSIO_HAS_EPOLL 18   #if BOOST_COROSIO_HAS_EPOLL
19   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 19   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
20   #endif 20   #endif
21   21  
22   #if BOOST_COROSIO_HAS_SELECT 22   #if BOOST_COROSIO_HAS_SELECT
23   #include <boost/corosio/native/detail/select/select_types.hpp> 23   #include <boost/corosio/native/detail/select/select_types.hpp>
24   #endif 24   #endif
25   25  
26   #if BOOST_COROSIO_HAS_KQUEUE 26   #if BOOST_COROSIO_HAS_KQUEUE
27   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 27   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
28   #endif 28   #endif
29   29  
30 - #if BOOST_COROSIO_HAS_URING 30 + #if BOOST_COROSIO_HAS_IO_URING
31 - #include <boost/corosio/native/detail/uring/uring_types.hpp> 31 + #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp>
32   #endif 32   #endif
33   33  
34   #if BOOST_COROSIO_HAS_IOCP 34   #if BOOST_COROSIO_HAS_IOCP
35   #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp> 35   #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp>
36   #endif 36   #endif
37   #endif // !BOOST_COROSIO_MRDOCS 37   #endif // !BOOST_COROSIO_MRDOCS
38   38  
39   namespace boost::corosio { 39   namespace boost::corosio {
40   40  
41   /** An asynchronous Unix stream acceptor with devirtualized accept. 41   /** An asynchronous Unix stream acceptor with devirtualized accept.
42   42  
43   This class template inherits from @ref local_stream_acceptor 43   This class template inherits from @ref local_stream_acceptor
44   and shadows both `accept` overloads (the peer-reference form 44   and shadows both `accept` overloads (the peer-reference form
45   and the move-return form) with versions that call the backend 45   and the move-return form) with versions that call the backend
46   implementation directly, allowing the compiler to inline 46   implementation directly, allowing the compiler to inline
47   through the entire call chain. The move-return form yields a 47   through the entire call chain. The move-return form yields a
48   @ref native_local_stream_socket so subsequent I/O on the peer 48   @ref native_local_stream_socket so subsequent I/O on the peer
49   is also devirtualized. 49   is also devirtualized.
50   50  
51   Non-async operations (`listen`, `close`, `cancel`) remain 51   Non-async operations (`listen`, `close`, `cancel`) remain
52   unchanged and dispatch through the compiled library. 52   unchanged and dispatch through the compiled library.
53   53  
54   A `native_local_stream_acceptor` IS-A `local_stream_acceptor` 54   A `native_local_stream_acceptor` IS-A `local_stream_acceptor`
55   and can be passed to any function expecting 55   and can be passed to any function expecting
56   `local_stream_acceptor&`. 56   `local_stream_acceptor&`.
57   57  
58   @tparam Backend A backend tag value (e.g., `epoll`). 58   @tparam Backend A backend tag value (e.g., `epoll`).
59   59  
60   @par Thread Safety 60   @par Thread Safety
61   Same as @ref local_stream_acceptor. 61   Same as @ref local_stream_acceptor.
62   62  
63   @see local_stream_acceptor, epoll_t, iocp_t 63   @see local_stream_acceptor, epoll_t, iocp_t
64   */ 64   */
65   template<auto Backend> 65   template<auto Backend>
66   class native_local_stream_acceptor : public local_stream_acceptor 66   class native_local_stream_acceptor : public local_stream_acceptor
67   { 67   {
68   using backend_type = decltype(Backend); 68   using backend_type = decltype(Backend);
69   using impl_type = typename backend_type::local_stream_acceptor_type; 69   using impl_type = typename backend_type::local_stream_acceptor_type;
70   using service_type = 70   using service_type =
71   typename backend_type::local_stream_acceptor_service_type; 71   typename backend_type::local_stream_acceptor_service_type;
72   72  
HITCBC 73   16 impl_type& get_impl() noexcept 73   16 impl_type& get_impl() noexcept
74   { 74   {
HITCBC 75   16 return *static_cast<impl_type*>(h_.get()); 75   16 return *static_cast<impl_type*>(h_.get());
76   } 76   }
77   77  
78   struct native_wait_awaitable 78   struct native_wait_awaitable
79   { 79   {
80   native_local_stream_acceptor& acc_; 80   native_local_stream_acceptor& acc_;
81   wait_type w_; 81   wait_type w_;
82   std::stop_token token_; 82   std::stop_token token_;
83   mutable std::error_code ec_; 83   mutable std::error_code ec_;
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  
HITCBC 92   6 bool await_ready() const noexcept 92   6 bool await_ready() const noexcept
93   { 93   {
94   // A pre-set ec_ means the initiator failed before 94   // A pre-set ec_ means the initiator failed before
95   // dispatch (e.g. a closed object). 95   // dispatch (e.g. a closed object).
HITCBC 96   6 return static_cast<bool>(ec_) || token_.stop_requested(); 96   6 return static_cast<bool>(ec_) || token_.stop_requested();
97   } 97   }
98   98  
HITCBC 99   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept 99   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept
100   { 100   {
HITCBC 101   6 if (token_.stop_requested()) 101   6 if (token_.stop_requested())
HITCBC 102   2 return {make_error_code(std::errc::operation_canceled)}; 102   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 103   4 return {ec_}; 103   4 return {ec_};
104   } 104   }
105   105  
HITCBC 106   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 106   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
107   -> std::coroutine_handle<> 107   -> std::coroutine_handle<>
108   { 108   {
HITCBC 109   6 token_ = env->stop_token; 109   6 token_ = env->stop_token;
HITCBC 110 - 6 return acc_.get_impl().wait(h, env->executor, w_, token_, &ec_); 110 + 18 return acc_.get_impl().wait(
HITGNC   111 + 18 h, env->executor, w_, token_, &ec_);
111   } 112   }
112   }; 113   };
113   114  
114   struct native_accept_awaitable 115   struct native_accept_awaitable
115   { 116   {
116   native_local_stream_acceptor& acc_; 117   native_local_stream_acceptor& acc_;
117   local_stream_socket& peer_; 118   local_stream_socket& peer_;
118   std::stop_token token_; 119   std::stop_token token_;
119   mutable std::error_code ec_; 120   mutable std::error_code ec_;
120   mutable io_object::implementation* peer_impl_ = nullptr; 121   mutable io_object::implementation* peer_impl_ = nullptr;
121   122  
HITCBC 122   8 native_accept_awaitable( 123   8 native_accept_awaitable(
123   native_local_stream_acceptor& acc, 124   native_local_stream_acceptor& acc,
124   local_stream_socket& peer) noexcept 125   local_stream_socket& peer) noexcept
HITCBC 125   8 : acc_(acc) 126   8 : acc_(acc)
HITCBC 126   8 , peer_(peer) 127   8 , peer_(peer)
127   { 128   {
HITCBC 128   8 } 129   8 }
129   130  
HITCBC 130   8 bool await_ready() const noexcept 131   8 bool await_ready() const noexcept
131   { 132   {
132   // A pre-set ec_ means the initiator failed before 133   // A pre-set ec_ means the initiator failed before
133   // dispatch (e.g. a closed object). 134   // dispatch (e.g. a closed object).
HITCBC 134   8 return static_cast<bool>(ec_) || token_.stop_requested(); 135   8 return static_cast<bool>(ec_) || token_.stop_requested();
135   } 136   }
136   137  
HITCBC 137   8 [[nodiscard]] capy::io_result<> await_resume() const noexcept 138   8 [[nodiscard]] capy::io_result<> await_resume() const noexcept
138   { 139   {
HITCBC 139   8 if (token_.stop_requested()) 140   8 if (token_.stop_requested())
HITCBC 140   2 return {make_error_code(std::errc::operation_canceled)}; 141   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 141   6 if (!ec_) 142   6 if (!ec_)
HITCBC 142   4 acc_.reset_peer_impl(peer_, peer_impl_); 143   4 acc_.reset_peer_impl(peer_, peer_impl_);
HITCBC 143   6 return {ec_}; 144   6 return {ec_};
144   } 145   }
145   146  
HITCBC 146   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 147   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
147   -> std::coroutine_handle<> 148   -> std::coroutine_handle<>
148   { 149   {
HITCBC 149   6 token_ = env->stop_token; 150   6 token_ = env->stop_token;
HITCBC 150   18 return acc_.get_impl().accept( 151   18 return acc_.get_impl().accept(
HITCBC 151   18 h, env->executor, token_, &ec_, &peer_impl_); 152   18 h, env->executor, token_, &ec_, &peer_impl_);
152   } 153   }
153   }; 154   };
154   155  
155   struct native_move_accept_awaitable 156   struct native_move_accept_awaitable
156   { 157   {
157   native_local_stream_acceptor& acc_; 158   native_local_stream_acceptor& acc_;
158   std::stop_token token_; 159   std::stop_token token_;
159   mutable std::error_code ec_; 160   mutable std::error_code ec_;
160   mutable io_object::implementation* peer_impl_ = nullptr; 161   mutable io_object::implementation* peer_impl_ = nullptr;
161   162  
HITCBC 162   6 explicit native_move_accept_awaitable( 163   6 explicit native_move_accept_awaitable(
163   native_local_stream_acceptor& acc) noexcept 164   native_local_stream_acceptor& acc) noexcept
HITCBC 164   6 : acc_(acc) 165   6 : acc_(acc)
165   { 166   {
HITCBC 166   6 } 167   6 }
167   168  
HITCBC 168   6 bool await_ready() const noexcept 169   6 bool await_ready() const noexcept
169   { 170   {
170   // A pre-set ec_ means the initiator failed before 171   // A pre-set ec_ means the initiator failed before
171   // dispatch (e.g. a closed object). 172   // dispatch (e.g. a closed object).
HITCBC 172   6 return static_cast<bool>(ec_) || token_.stop_requested(); 173   6 return static_cast<bool>(ec_) || token_.stop_requested();
173   } 174   }
174   175  
175   [[nodiscard]] capy::io_result<native_local_stream_socket<Backend>> 176   [[nodiscard]] capy::io_result<native_local_stream_socket<Backend>>
HITCBC 176   6 await_resume() const noexcept 177   6 await_resume() const noexcept
177   { 178   {
HITCBC 178   6 if (token_.stop_requested()) 179   6 if (token_.stop_requested())
179   return { 180   return {
HITCBC 180   2 make_error_code(std::errc::operation_canceled), 181   2 make_error_code(std::errc::operation_canceled),
HITCBC 181   2 native_local_stream_socket<Backend>(acc_.context())}; 182   2 native_local_stream_socket<Backend>(acc_.context())};
HITCBC 182   4 if (ec_ || !peer_impl_) 183   4 if (ec_ || !peer_impl_)
183   return { 184   return {
HITCBC 184 - 2 ec_, native_local_stream_socket<Backend>(acc_.context())}; 185 + 2 ec_,
HITGNC   186 + 2 native_local_stream_socket<Backend>(acc_.context())};
185   187  
HITCBC 186   2 native_local_stream_socket<Backend> peer(acc_.context()); 188   2 native_local_stream_socket<Backend> peer(acc_.context());
HITCBC 187   2 acc_.reset_peer_impl(peer, peer_impl_); 189   2 acc_.reset_peer_impl(peer, peer_impl_);
HITCBC 188   2 return {ec_, std::move(peer)}; 190   2 return {ec_, std::move(peer)};
HITCBC 189   2 } 191   2 }
190   192  
HITCBC 191   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 193   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
192   -> std::coroutine_handle<> 194   -> std::coroutine_handle<>
193   { 195   {
HITCBC 194   4 token_ = env->stop_token; 196   4 token_ = env->stop_token;
HITCBC 195   12 return acc_.get_impl().accept( 197   12 return acc_.get_impl().accept(
HITCBC 196   12 h, env->executor, token_, &ec_, &peer_impl_); 198   12 h, env->executor, token_, &ec_, &peer_impl_);
197   } 199   }
198   }; 200   };
199   201  
200   public: 202   public:
201   /** Construct a native acceptor from an execution context. 203   /** Construct a native acceptor from an execution context.
202   204  
203   @param ctx The execution context that will own this acceptor. 205   @param ctx The execution context that will own this acceptor.
204   */ 206   */
HITCBC 205   18 explicit native_local_stream_acceptor(capy::execution_context& ctx) 207   18 explicit native_local_stream_acceptor(capy::execution_context& ctx)
HITCBC 206   18 : local_stream_acceptor(create_handle<service_type>(ctx), ctx) 208   18 : local_stream_acceptor(create_handle<service_type>(ctx), ctx)
207   { 209   {
HITCBC 208   18 } 210   18 }
209   211  
210   /** Construct a native acceptor from an executor. 212   /** Construct a native acceptor from an executor.
211   213  
212   @param ex The executor whose context will own the acceptor. 214   @param ex The executor whose context will own the acceptor.
213   */ 215   */
214   template<class Ex> 216   template<class Ex>
215   requires(!std::same_as< 217   requires(!std::same_as<
216 - std::remove_cvref_t<Ex>, 218 + std::remove_cvref_t<Ex>,
217 - native_local_stream_acceptor>) && 219 + native_local_stream_acceptor>) &&
218   capy::Executor<Ex> 220   capy::Executor<Ex>
219   explicit native_local_stream_acceptor(Ex const& ex) 221   explicit native_local_stream_acceptor(Ex const& ex)
220   : native_local_stream_acceptor(ex.context()) 222   : native_local_stream_acceptor(ex.context())
221   { 223   {
222   } 224   }
223   225  
224   /// Move construct. 226   /// Move construct.
HITCBC 225   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept = 227   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept =
226   default; 228   default;
227   229  
228   /// Move assign. 230   /// Move assign.
229   native_local_stream_acceptor& 231   native_local_stream_acceptor&
230   operator=(native_local_stream_acceptor&&) noexcept = default; 232   operator=(native_local_stream_acceptor&&) noexcept = default;
231   233  
232   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete; 234   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete;
233   native_local_stream_acceptor& 235   native_local_stream_acceptor&
234   operator=(native_local_stream_acceptor const&) = delete; 236   operator=(native_local_stream_acceptor const&) = delete;
235   237  
236   /** Asynchronously accept an incoming connection. 238   /** Asynchronously accept an incoming connection.
237   239  
238   Calls the backend implementation directly, bypassing virtual 240   Calls the backend implementation directly, bypassing virtual
239   dispatch. Otherwise identical to @ref local_stream_acceptor::accept. 241   dispatch. Otherwise identical to @ref local_stream_acceptor::accept.
240   242  
241   @param peer The socket to receive the accepted connection. 243   @param peer The socket to receive the accepted connection.
242   244  
243   @return An awaitable yielding `io_result<>`. 245   @return An awaitable yielding `io_result<>`.
244   246  
245   A closed acceptor reports `errc::bad_file_descriptor`. 247   A closed acceptor reports `errc::bad_file_descriptor`.
246   248  
247   Both this acceptor and @p peer must outlive the returned 249   Both this acceptor and @p peer must outlive the returned
248   awaitable. 250   awaitable.
249   */ 251   */
HITCBC 250   8 [[nodiscard]] auto accept(local_stream_socket& peer) 252   8 [[nodiscard]] auto accept(local_stream_socket& peer)
251   { 253   {
HITCBC 252   8 native_accept_awaitable aw(*this, peer); 254   8 native_accept_awaitable aw(*this, peer);
HITCBC 253   8 if (!is_open()) 255   8 if (!is_open())
HITCBC 254   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 256   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 255   8 return aw; 257   8 return aw;
256   } 258   }
257   259  
258   /** Asynchronously accept an incoming connection, returning the peer. 260   /** Asynchronously accept an incoming connection, returning the peer.
259   261  
260   Calls the backend implementation directly, bypassing virtual 262   Calls the backend implementation directly, bypassing virtual
261   dispatch. The accepted peer is returned as a 263   dispatch. The accepted peer is returned as a
262   @ref native_local_stream_socket so that subsequent I/O on it 264   @ref native_local_stream_socket so that subsequent I/O on it
263   is also devirtualized. 265   is also devirtualized.
264   266  
265   @return An awaitable yielding 267   @return An awaitable yielding
266   `io_result<native_local_stream_socket<Backend>>`. 268   `io_result<native_local_stream_socket<Backend>>`.
267   269  
268   A closed acceptor reports `errc::bad_file_descriptor`. 270   A closed acceptor reports `errc::bad_file_descriptor`.
269   271  
270   @throws std::logic_error If the acceptor has been moved from. 272   @throws std::logic_error If the acceptor has been moved from.
271   273  
272   This acceptor must outlive the returned awaitable. 274   This acceptor must outlive the returned awaitable.
273   */ 275   */
HITCBC 274   8 [[nodiscard]] auto accept() 276   8 [[nodiscard]] auto accept()
275   { 277   {
276   // The awaitable builds the peer from context(), which a 278   // The awaitable builds the peer from context(), which a
277   // moved-from acceptor no longer has. 279   // moved-from acceptor no longer has.
HITCBC 278   8 if (!h_) 280   8 if (!h_)
HITCBC 279   2 detail::throw_logic_error("accept: acceptor moved-from"); 281   2 detail::throw_logic_error("accept: acceptor moved-from");
HITCBC 280   6 native_move_accept_awaitable aw(*this); 282   6 native_move_accept_awaitable aw(*this);
HITCBC 281   6 if (!is_open()) 283   6 if (!is_open())
HITCBC 282   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 284   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 283   6 return aw; 285   6 return aw;
284   } 286   }
285   287  
286   /** Asynchronously wait for the acceptor to be ready. 288   /** Asynchronously wait for the acceptor to be ready.
287   289  
288   Calls the backend implementation directly, bypassing virtual 290   Calls the backend implementation directly, bypassing virtual
289   dispatch. Otherwise identical to @ref local_stream_acceptor::wait. 291   dispatch. Otherwise identical to @ref local_stream_acceptor::wait.
290   292  
291   @param w The wait direction (typically `wait_type::read`). 293   @param w The wait direction (typically `wait_type::read`).
292   294  
293   @return An awaitable yielding `io_result<>`. 295   @return An awaitable yielding `io_result<>`.
294   */ 296   */
HITCBC 295   6 [[nodiscard]] auto wait(wait_type w) 297   6 [[nodiscard]] auto wait(wait_type w)
296   { 298   {
HITCBC 297   6 return native_wait_awaitable(*this, w); 299   6 return native_wait_awaitable(*this, w);
298   } 300   }
299   }; 301   };
300   302  
301   } // namespace boost::corosio 303   } // namespace boost::corosio
302   304  
303   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 305   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP