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