100.00% Lines (68/68) 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_URING
31   #include <boost/corosio/native/detail/uring/uring_types.hpp> 31   #include <boost/corosio/native/detail/uring/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   6 return acc_.get_impl().wait(h, env->executor, w_, token_, &ec_);
111   } 111   }
112   }; 112   };
113   113  
114   struct native_accept_awaitable 114   struct native_accept_awaitable
115   { 115   {
116   native_local_stream_acceptor& acc_; 116   native_local_stream_acceptor& acc_;
117   local_stream_socket& peer_; 117   local_stream_socket& peer_;
118   std::stop_token token_; 118   std::stop_token token_;
119   mutable std::error_code ec_; 119   mutable std::error_code ec_;
120   mutable io_object::implementation* peer_impl_ = nullptr; 120   mutable io_object::implementation* peer_impl_ = nullptr;
121   121  
HITCBC 122   8 native_accept_awaitable( 122   8 native_accept_awaitable(
123   native_local_stream_acceptor& acc, 123   native_local_stream_acceptor& acc,
124   local_stream_socket& peer) noexcept 124   local_stream_socket& peer) noexcept
HITCBC 125   8 : acc_(acc) 125   8 : acc_(acc)
HITCBC 126   8 , peer_(peer) 126   8 , peer_(peer)
127   { 127   {
HITCBC 128   8 } 128   8 }
129   129  
HITCBC 130   8 bool await_ready() const noexcept 130   8 bool await_ready() const noexcept
131   { 131   {
132   // A pre-set ec_ means the initiator failed before 132   // A pre-set ec_ means the initiator failed before
133   // dispatch (e.g. a closed object). 133   // dispatch (e.g. a closed object).
HITCBC 134   8 return static_cast<bool>(ec_) || token_.stop_requested(); 134   8 return static_cast<bool>(ec_) || token_.stop_requested();
135   } 135   }
136   136  
HITCBC 137   8 [[nodiscard]] capy::io_result<> await_resume() const noexcept 137   8 [[nodiscard]] capy::io_result<> await_resume() const noexcept
138   { 138   {
HITCBC 139   8 if (token_.stop_requested()) 139   8 if (token_.stop_requested())
HITCBC 140   2 return {make_error_code(std::errc::operation_canceled)}; 140   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 141   6 if (!ec_) 141   6 if (!ec_)
HITCBC 142   4 acc_.reset_peer_impl(peer_, peer_impl_); 142   4 acc_.reset_peer_impl(peer_, peer_impl_);
HITCBC 143   6 return {ec_}; 143   6 return {ec_};
144   } 144   }
145   145  
HITCBC 146   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 146   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
147   -> std::coroutine_handle<> 147   -> std::coroutine_handle<>
148   { 148   {
HITCBC 149   6 token_ = env->stop_token; 149   6 token_ = env->stop_token;
HITCBC 150   18 return acc_.get_impl().accept( 150   18 return acc_.get_impl().accept(
HITCBC 151   18 h, env->executor, token_, &ec_, &peer_impl_); 151   18 h, env->executor, token_, &ec_, &peer_impl_);
152   } 152   }
153   }; 153   };
154   154  
155   struct native_move_accept_awaitable 155   struct native_move_accept_awaitable
156   { 156   {
157   native_local_stream_acceptor& acc_; 157   native_local_stream_acceptor& acc_;
158   std::stop_token token_; 158   std::stop_token token_;
159   mutable std::error_code ec_; 159   mutable std::error_code ec_;
160   mutable io_object::implementation* peer_impl_ = nullptr; 160   mutable io_object::implementation* peer_impl_ = nullptr;
161   161  
HITCBC 162   6 explicit native_move_accept_awaitable( 162   6 explicit native_move_accept_awaitable(
163   native_local_stream_acceptor& acc) noexcept 163   native_local_stream_acceptor& acc) noexcept
HITCBC 164   6 : acc_(acc) 164   6 : acc_(acc)
165   { 165   {
HITCBC 166   6 } 166   6 }
167   167  
HITCBC 168   6 bool await_ready() const noexcept 168   6 bool await_ready() const noexcept
169   { 169   {
170   // A pre-set ec_ means the initiator failed before 170   // A pre-set ec_ means the initiator failed before
171   // dispatch (e.g. a closed object). 171   // dispatch (e.g. a closed object).
HITCBC 172   6 return static_cast<bool>(ec_) || token_.stop_requested(); 172   6 return static_cast<bool>(ec_) || token_.stop_requested();
173   } 173   }
174   174  
175   [[nodiscard]] capy::io_result<native_local_stream_socket<Backend>> 175   [[nodiscard]] capy::io_result<native_local_stream_socket<Backend>>
HITCBC 176   6 await_resume() const noexcept 176   6 await_resume() const noexcept
177   { 177   {
HITCBC 178   6 if (token_.stop_requested()) 178   6 if (token_.stop_requested())
179   return { 179   return {
HITCBC 180   2 make_error_code(std::errc::operation_canceled), 180   2 make_error_code(std::errc::operation_canceled),
HITCBC 181   2 native_local_stream_socket<Backend>(acc_.context())}; 181   2 native_local_stream_socket<Backend>(acc_.context())};
HITCBC 182   4 if (ec_ || !peer_impl_) 182   4 if (ec_ || !peer_impl_)
183   return { 183   return {
HITCBC 184   2 ec_, native_local_stream_socket<Backend>(acc_.context())}; 184   2 ec_, native_local_stream_socket<Backend>(acc_.context())};
185   185  
HITCBC 186   2 native_local_stream_socket<Backend> peer(acc_.context()); 186   2 native_local_stream_socket<Backend> peer(acc_.context());
HITCBC 187   2 acc_.reset_peer_impl(peer, peer_impl_); 187   2 acc_.reset_peer_impl(peer, peer_impl_);
HITCBC 188   2 return {ec_, std::move(peer)}; 188   2 return {ec_, std::move(peer)};
HITCBC 189   2 } 189   2 }
190   190  
HITCBC 191   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 191   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
192   -> std::coroutine_handle<> 192   -> std::coroutine_handle<>
193   { 193   {
HITCBC 194   4 token_ = env->stop_token; 194   4 token_ = env->stop_token;
HITCBC 195   12 return acc_.get_impl().accept( 195   12 return acc_.get_impl().accept(
HITCBC 196   12 h, env->executor, token_, &ec_, &peer_impl_); 196   12 h, env->executor, token_, &ec_, &peer_impl_);
197   } 197   }
198   }; 198   };
199   199  
200   public: 200   public:
201   /** Construct a native acceptor from an execution context. 201   /** Construct a native acceptor from an execution context.
202   202  
203   @param ctx The execution context that will own this acceptor. 203   @param ctx The execution context that will own this acceptor.
204   */ 204   */
HITCBC 205   18 explicit native_local_stream_acceptor(capy::execution_context& ctx) 205   18 explicit native_local_stream_acceptor(capy::execution_context& ctx)
HITCBC 206   18 : local_stream_acceptor(create_handle<service_type>(ctx), ctx) 206   18 : local_stream_acceptor(create_handle<service_type>(ctx), ctx)
207   { 207   {
HITCBC 208   18 } 208   18 }
209   209  
210   /** Construct a native acceptor from an executor. 210   /** Construct a native acceptor from an executor.
211   211  
212   @param ex The executor whose context will own the acceptor. 212   @param ex The executor whose context will own the acceptor.
213   */ 213   */
214   template<class Ex> 214   template<class Ex>
215   requires(!std::same_as< 215   requires(!std::same_as<
216   std::remove_cvref_t<Ex>, 216   std::remove_cvref_t<Ex>,
217   native_local_stream_acceptor>) && 217   native_local_stream_acceptor>) &&
218   capy::Executor<Ex> 218   capy::Executor<Ex>
219   explicit native_local_stream_acceptor(Ex const& ex) 219   explicit native_local_stream_acceptor(Ex const& ex)
220   : native_local_stream_acceptor(ex.context()) 220   : native_local_stream_acceptor(ex.context())
221   { 221   {
222   } 222   }
223   223  
224   /// Move construct. 224   /// Move construct.
HITCBC 225   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept = 225   2 native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept =
226   default; 226   default;
227   227  
228   /// Move assign. 228   /// Move assign.
229   native_local_stream_acceptor& 229   native_local_stream_acceptor&
230   operator=(native_local_stream_acceptor&&) noexcept = default; 230   operator=(native_local_stream_acceptor&&) noexcept = default;
231   231  
232   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete; 232   native_local_stream_acceptor(native_local_stream_acceptor const&) = delete;
233   native_local_stream_acceptor& 233   native_local_stream_acceptor&
234   operator=(native_local_stream_acceptor const&) = delete; 234   operator=(native_local_stream_acceptor const&) = delete;
235   235  
236   /** Asynchronously accept an incoming connection. 236   /** Asynchronously accept an incoming connection.
237   237  
238   Calls the backend implementation directly, bypassing virtual 238   Calls the backend implementation directly, bypassing virtual
239   dispatch. Otherwise identical to @ref local_stream_acceptor::accept. 239   dispatch. Otherwise identical to @ref local_stream_acceptor::accept.
240   240  
241   @param peer The socket to receive the accepted connection. 241   @param peer The socket to receive the accepted connection.
242   242  
243   @return An awaitable yielding `io_result<>`. 243   @return An awaitable yielding `io_result<>`.
244   244  
245   A closed acceptor reports `errc::bad_file_descriptor`. 245   A closed acceptor reports `errc::bad_file_descriptor`.
246   246  
247   Both this acceptor and @p peer must outlive the returned 247   Both this acceptor and @p peer must outlive the returned
248   awaitable. 248   awaitable.
249   */ 249   */
HITCBC 250   8 [[nodiscard]] auto accept(local_stream_socket& peer) 250   8 [[nodiscard]] auto accept(local_stream_socket& peer)
251   { 251   {
HITCBC 252   8 native_accept_awaitable aw(*this, peer); 252   8 native_accept_awaitable aw(*this, peer);
HITCBC 253   8 if (!is_open()) 253   8 if (!is_open())
HITCBC 254   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 254   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 255   8 return aw; 255   8 return aw;
256   } 256   }
257   257  
258   /** Asynchronously accept an incoming connection, returning the peer. 258   /** Asynchronously accept an incoming connection, returning the peer.
259   259  
260   Calls the backend implementation directly, bypassing virtual 260   Calls the backend implementation directly, bypassing virtual
261   dispatch. The accepted peer is returned as a 261   dispatch. The accepted peer is returned as a
262   @ref native_local_stream_socket so that subsequent I/O on it 262   @ref native_local_stream_socket so that subsequent I/O on it
263   is also devirtualized. 263   is also devirtualized.
264   264  
265   @return An awaitable yielding 265   @return An awaitable yielding
266   `io_result<native_local_stream_socket<Backend>>`. 266   `io_result<native_local_stream_socket<Backend>>`.
267   267  
268   A closed acceptor reports `errc::bad_file_descriptor`. 268   A closed acceptor reports `errc::bad_file_descriptor`.
269   269  
270   @throws std::logic_error If the acceptor has been moved from. 270   @throws std::logic_error If the acceptor has been moved from.
271   271  
272   This acceptor must outlive the returned awaitable. 272   This acceptor must outlive the returned awaitable.
273   */ 273   */
HITCBC 274   8 [[nodiscard]] auto accept() 274   8 [[nodiscard]] auto accept()
275   { 275   {
276   // The awaitable builds the peer from context(), which a 276   // The awaitable builds the peer from context(), which a
277   // moved-from acceptor no longer has. 277   // moved-from acceptor no longer has.
HITCBC 278   8 if (!h_) 278   8 if (!h_)
HITCBC 279   2 detail::throw_logic_error("accept: acceptor moved-from"); 279   2 detail::throw_logic_error("accept: acceptor moved-from");
HITCBC 280   6 native_move_accept_awaitable aw(*this); 280   6 native_move_accept_awaitable aw(*this);
HITCBC 281   6 if (!is_open()) 281   6 if (!is_open())
HITCBC 282   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 282   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 283   6 return aw; 283   6 return aw;
284   } 284   }
285   285  
286   /** Asynchronously wait for the acceptor to be ready. 286   /** Asynchronously wait for the acceptor to be ready.
287   287  
288   Calls the backend implementation directly, bypassing virtual 288   Calls the backend implementation directly, bypassing virtual
289   dispatch. Otherwise identical to @ref local_stream_acceptor::wait. 289   dispatch. Otherwise identical to @ref local_stream_acceptor::wait.
290   290  
291   @param w The wait direction (typically `wait_type::read`). 291   @param w The wait direction (typically `wait_type::read`).
292   292  
293   @return An awaitable yielding `io_result<>`. 293   @return An awaitable yielding `io_result<>`.
294   */ 294   */
HITCBC 295   6 [[nodiscard]] auto wait(wait_type w) 295   6 [[nodiscard]] auto wait(wait_type w)
296   { 296   {
HITCBC 297   6 return native_wait_awaitable(*this, w); 297   6 return native_wait_awaitable(*this, w);
298   } 298   }
299   }; 299   };
300   300  
301   } // namespace boost::corosio 301   } // namespace boost::corosio
302   302  
303   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP 303   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP