100.00% Lines (73/73) 100.00% Functions (23/23)
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_SOCKET_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_SOCKET_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_SOCKET_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_SOCKET_HPP
13   13  
14   #include <boost/corosio/local_stream_socket.hpp> 14   #include <boost/corosio/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_service.hpp> 35   #include <boost/corosio/native/detail/iocp/win_local_stream_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 socket with devirtualized I/O operations. 41   /** An asynchronous Unix stream socket with devirtualized I/O operations.
42   42  
43   This class template inherits from @ref local_stream_socket and 43   This class template inherits from @ref local_stream_socket and
44   shadows the async operations (`read_some`, `write_some`, 44   shadows the async operations (`read_some`, `write_some`,
45   `connect`) with versions that call the backend implementation 45   `connect`) with versions that call the backend implementation
46   directly, allowing the compiler to inline through the entire 46   directly, allowing the compiler to inline through the entire
47   call chain. 47   call chain.
48   48  
49   Non-async operations (`open`, `close`, `cancel`, socket options) 49   Non-async operations (`open`, `close`, `cancel`, socket options)
50   remain unchanged and dispatch through the compiled library. 50   remain unchanged and dispatch through the compiled library.
51   51  
52   A `native_local_stream_socket` IS-A `local_stream_socket` and 52   A `native_local_stream_socket` IS-A `local_stream_socket` and
53   can be passed to any function expecting `local_stream_socket&` 53   can be passed to any function expecting `local_stream_socket&`
54   or `io_stream&`, in which case virtual dispatch is used 54   or `io_stream&`, in which case virtual dispatch is used
55   transparently. 55   transparently.
56   56  
57   @tparam Backend A backend tag value (e.g., `epoll`) whose type 57   @tparam Backend A backend tag value (e.g., `epoll`) whose type
58   provides the concrete implementation types. 58   provides the concrete implementation types.
59   59  
60   @par Thread Safety 60   @par Thread Safety
61   Same as @ref local_stream_socket. 61   Same as @ref local_stream_socket.
62   62  
63   @par Example 63   @par Example
64   @par !example connect 64   @par !example connect
65   65  
66   @see local_stream_socket, epoll_t, iocp_t 66   @see local_stream_socket, epoll_t, iocp_t
67   */ 67   */
68   template<auto Backend> 68   template<auto Backend>
69   class native_local_stream_socket : public local_stream_socket 69   class native_local_stream_socket : public local_stream_socket
70   { 70   {
71   using backend_type = decltype(Backend); 71   using backend_type = decltype(Backend);
72   using impl_type = typename backend_type::local_stream_socket_type; 72   using impl_type = typename backend_type::local_stream_socket_type;
73   using service_type = typename backend_type::local_stream_service_type; 73   using service_type = typename backend_type::local_stream_service_type;
74   74  
HITCBC 75   34 impl_type& get_impl() noexcept 75   34 impl_type& get_impl() noexcept
76   { 76   {
HITCBC 77   34 return *static_cast<impl_type*>(h_.get()); 77   34 return *static_cast<impl_type*>(h_.get());
78   } 78   }
79   79  
80   template<class MutableBufferSequence> 80   template<class MutableBufferSequence>
81   struct native_read_awaitable 81   struct native_read_awaitable
82   { 82   {
83   native_local_stream_socket& self_; 83   native_local_stream_socket& self_;
84   MutableBufferSequence buffers_; 84   MutableBufferSequence buffers_;
85   std::stop_token token_; 85   std::stop_token token_;
86   mutable std::error_code ec_; 86   mutable std::error_code ec_;
87   mutable std::size_t bytes_transferred_ = 0; 87   mutable std::size_t bytes_transferred_ = 0;
88   88  
HITCBC 89   8 native_read_awaitable( 89   8 native_read_awaitable(
90   native_local_stream_socket& self, 90   native_local_stream_socket& self,
91   MutableBufferSequence buffers) noexcept 91   MutableBufferSequence buffers) noexcept
HITCBC 92   8 : self_(self) 92   8 : self_(self)
HITCBC 93   8 , buffers_(std::move(buffers)) 93   8 , buffers_(std::move(buffers))
94   { 94   {
HITCBC 95   8 } 95   8 }
96   96  
HITCBC 97   8 bool await_ready() const noexcept 97   8 bool await_ready() const noexcept
98   { 98   {
99   // A pre-set ec_ means the initiator failed before 99   // A pre-set ec_ means the initiator failed before
100   // dispatch (e.g. a closed object). 100   // dispatch (e.g. a closed object).
HITCBC 101   8 return static_cast<bool>(ec_) || token_.stop_requested(); 101   8 return static_cast<bool>(ec_) || token_.stop_requested();
102   } 102   }
103   103  
HITCBC 104   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 104   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
105   { 105   {
HITCBC 106   8 if (token_.stop_requested()) 106   8 if (token_.stop_requested())
HITCBC 107   2 return {make_error_code(std::errc::operation_canceled), 0}; 107   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 108   6 return {ec_, bytes_transferred_}; 108   6 return {ec_, bytes_transferred_};
109   } 109   }
110   110  
HITCBC 111   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 111   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
112   -> std::coroutine_handle<> 112   -> std::coroutine_handle<>
113   { 113   {
HITCBC 114   8 token_ = env->stop_token; 114   8 token_ = env->stop_token;
HITCBC 115   24 return self_.get_impl().read_some( 115   24 return self_.get_impl().read_some(
HITCBC 116   24 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 116   24 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
117   } 117   }
118   }; 118   };
119   119  
120   template<class ConstBufferSequence> 120   template<class ConstBufferSequence>
121   struct native_write_awaitable 121   struct native_write_awaitable
122   { 122   {
123   native_local_stream_socket& self_; 123   native_local_stream_socket& self_;
124   ConstBufferSequence buffers_; 124   ConstBufferSequence buffers_;
125   std::stop_token token_; 125   std::stop_token token_;
126   mutable std::error_code ec_; 126   mutable std::error_code ec_;
127   mutable std::size_t bytes_transferred_ = 0; 127   mutable std::size_t bytes_transferred_ = 0;
128   128  
HITCBC 129   8 native_write_awaitable( 129   8 native_write_awaitable(
130   native_local_stream_socket& self, 130   native_local_stream_socket& self,
131   ConstBufferSequence buffers) noexcept 131   ConstBufferSequence buffers) noexcept
HITCBC 132   8 : self_(self) 132   8 : self_(self)
HITCBC 133   8 , buffers_(std::move(buffers)) 133   8 , buffers_(std::move(buffers))
134   { 134   {
HITCBC 135   8 } 135   8 }
136   136  
HITCBC 137   8 bool await_ready() const noexcept 137   8 bool await_ready() const noexcept
138   { 138   {
139   // A pre-set ec_ means the initiator failed before 139   // A pre-set ec_ means the initiator failed before
140   // dispatch (e.g. a closed object). 140   // dispatch (e.g. a closed object).
HITCBC 141   8 return static_cast<bool>(ec_) || token_.stop_requested(); 141   8 return static_cast<bool>(ec_) || token_.stop_requested();
142   } 142   }
143   143  
HITCBC 144   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 144   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
145   { 145   {
HITCBC 146   8 if (token_.stop_requested()) 146   8 if (token_.stop_requested())
HITCBC 147   2 return {make_error_code(std::errc::operation_canceled), 0}; 147   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 148   6 return {ec_, bytes_transferred_}; 148   6 return {ec_, bytes_transferred_};
149   } 149   }
150   150  
HITCBC 151   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 151   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
152   -> std::coroutine_handle<> 152   -> std::coroutine_handle<>
153   { 153   {
HITCBC 154   8 token_ = env->stop_token; 154   8 token_ = env->stop_token;
HITCBC 155   24 return self_.get_impl().write_some( 155   24 return self_.get_impl().write_some(
HITCBC 156   24 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 156   24 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
157   } 157   }
158   }; 158   };
159   159  
160   struct native_wait_awaitable 160   struct native_wait_awaitable
161   { 161   {
162   native_local_stream_socket& self_; 162   native_local_stream_socket& self_;
163   wait_type w_; 163   wait_type w_;
164   std::stop_token token_; 164   std::stop_token token_;
165   mutable std::error_code ec_; 165   mutable std::error_code ec_;
166   166  
HITCBC 167   6 native_wait_awaitable( 167   6 native_wait_awaitable(
168   native_local_stream_socket& self, wait_type w) noexcept 168   native_local_stream_socket& self, wait_type w) noexcept
HITCBC 169   6 : self_(self) 169   6 : self_(self)
HITCBC 170   6 , w_(w) 170   6 , w_(w)
171   { 171   {
HITCBC 172   6 } 172   6 }
173   173  
HITCBC 174   6 bool await_ready() const noexcept 174   6 bool await_ready() const noexcept
175   { 175   {
176   // A pre-set ec_ means the initiator failed before 176   // A pre-set ec_ means the initiator failed before
177   // dispatch (e.g. auto-open). 177   // dispatch (e.g. auto-open).
HITCBC 178   6 return static_cast<bool>(ec_) || token_.stop_requested(); 178   6 return static_cast<bool>(ec_) || token_.stop_requested();
179   } 179   }
180   180  
HITCBC 181   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept 181   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept
182   { 182   {
HITCBC 183   6 if (token_.stop_requested()) 183   6 if (token_.stop_requested())
HITCBC 184   2 return {make_error_code(std::errc::operation_canceled)}; 184   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 185   4 return {ec_}; 185   4 return {ec_};
186   } 186   }
187   187  
HITCBC 188   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 188   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
189   -> std::coroutine_handle<> 189   -> std::coroutine_handle<>
190   { 190   {
HITCBC 191   6 token_ = env->stop_token; 191   6 token_ = env->stop_token;
HITCBC 192 - 6 return self_.get_impl().wait(h, env->executor, w_, token_, &ec_); 192 + 18 return self_.get_impl().wait(
HITGNC   193 + 18 h, env->executor, w_, token_, &ec_);
193   } 194   }
194   }; 195   };
195   196  
196   struct native_connect_awaitable 197   struct native_connect_awaitable
197   { 198   {
198   native_local_stream_socket& self_; 199   native_local_stream_socket& self_;
199   corosio::local_endpoint endpoint_; 200   corosio::local_endpoint endpoint_;
200   std::stop_token token_; 201   std::stop_token token_;
201   mutable std::error_code ec_; 202   mutable std::error_code ec_;
202   203  
HITCBC 203   12 native_connect_awaitable( 204   12 native_connect_awaitable(
204   native_local_stream_socket& self, 205   native_local_stream_socket& self,
205   corosio::local_endpoint ep) noexcept 206   corosio::local_endpoint ep) noexcept
HITCBC 206   12 : self_(self) 207   12 : self_(self)
HITCBC 207   12 , endpoint_(ep) 208   12 , endpoint_(ep)
208   { 209   {
HITCBC 209   12 } 210   12 }
210   211  
HITCBC 211   12 bool await_ready() const noexcept 212   12 bool await_ready() const noexcept
212   { 213   {
213   // A pre-set ec_ means the initiator failed before 214   // A pre-set ec_ means the initiator failed before
214   // dispatch (e.g. a closed object). 215   // dispatch (e.g. a closed object).
HITCBC 215   12 return static_cast<bool>(ec_) || token_.stop_requested(); 216   12 return static_cast<bool>(ec_) || token_.stop_requested();
216   } 217   }
217   218  
HITCBC 218   12 [[nodiscard]] capy::io_result<> await_resume() const noexcept 219   12 [[nodiscard]] capy::io_result<> await_resume() const noexcept
219   { 220   {
HITCBC 220   12 if (token_.stop_requested()) 221   12 if (token_.stop_requested())
HITCBC 221   2 return {make_error_code(std::errc::operation_canceled)}; 222   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 222   10 return {ec_}; 223   10 return {ec_};
223   } 224   }
224   225  
HITCBC 225   12 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 226   12 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
226   -> std::coroutine_handle<> 227   -> std::coroutine_handle<>
227   { 228   {
HITCBC 228   12 token_ = env->stop_token; 229   12 token_ = env->stop_token;
HITCBC 229   36 return self_.get_impl().connect( 230   36 return self_.get_impl().connect(
HITCBC 230   36 h, env->executor, endpoint_, token_, &ec_); 231   36 h, env->executor, endpoint_, token_, &ec_);
231   } 232   }
232   }; 233   };
233   234  
234   public: 235   public:
235   /** Construct a native socket from an execution context. 236   /** Construct a native socket from an execution context.
236   237  
237   @param ctx The execution context that will own this socket. 238   @param ctx The execution context that will own this socket.
238   */ 239   */
HITCBC 239   40 explicit native_local_stream_socket(capy::execution_context& ctx) 240   40 explicit native_local_stream_socket(capy::execution_context& ctx)
HITCBC 240   40 : io_object(create_handle<service_type>(ctx)) 241   40 : io_object(create_handle<service_type>(ctx))
241   { 242   {
HITCBC 242   40 } 243   40 }
243   244  
244   /** Construct a native socket from an executor. 245   /** Construct a native socket from an executor.
245   246  
246   @param ex The executor whose context will own the socket. 247   @param ex The executor whose context will own the socket.
247   */ 248   */
248   template<class Ex> 249   template<class Ex>
249   requires(!std::same_as< 250   requires(!std::same_as<
250 - std::remove_cvref_t<Ex>, 251 + std::remove_cvref_t<Ex>,
251 - native_local_stream_socket>) && 252 + native_local_stream_socket>) &&
252   capy::Executor<Ex> 253   capy::Executor<Ex>
253   explicit native_local_stream_socket(Ex const& ex) 254   explicit native_local_stream_socket(Ex const& ex)
254   : native_local_stream_socket(ex.context()) 255   : native_local_stream_socket(ex.context())
255   { 256   {
256   } 257   }
257   258  
258   /// Move construct. 259   /// Move construct.
HITCBC 259   6 native_local_stream_socket(native_local_stream_socket&&) noexcept = default; 260   6 native_local_stream_socket(native_local_stream_socket&&) noexcept = default;
260   261  
261   /// Move assign. 262   /// Move assign.
262   native_local_stream_socket& 263   native_local_stream_socket&
263   operator=(native_local_stream_socket&&) noexcept = default; 264   operator=(native_local_stream_socket&&) noexcept = default;
264   265  
265   native_local_stream_socket(native_local_stream_socket const&) = delete; 266   native_local_stream_socket(native_local_stream_socket const&) = delete;
266   native_local_stream_socket& 267   native_local_stream_socket&
267   operator=(native_local_stream_socket const&) = delete; 268   operator=(native_local_stream_socket const&) = delete;
268   269  
269   /** Asynchronously read data from the socket. 270   /** Asynchronously read data from the socket.
270   271  
271   Calls the backend implementation directly, bypassing virtual 272   Calls the backend implementation directly, bypassing virtual
272   dispatch. Otherwise identical to @ref io_stream::read_some. 273   dispatch. Otherwise identical to @ref io_stream::read_some.
273   274  
274   @param buffers The buffer sequence to read into. 275   @param buffers The buffer sequence to read into.
275   276  
276   @return An awaitable yielding `(error_code, std::size_t)`. 277   @return An awaitable yielding `(error_code, std::size_t)`.
277   */ 278   */
278   template<capy::MutableBufferSequence MB> 279   template<capy::MutableBufferSequence MB>
HITCBC 279   8 [[nodiscard]] auto read_some(MB const& buffers) 280   8 [[nodiscard]] auto read_some(MB const& buffers)
280   { 281   {
HITCBC 281   8 return native_read_awaitable<MB>(*this, buffers); 282   8 return native_read_awaitable<MB>(*this, buffers);
282   } 283   }
283   284  
284   /** Asynchronously write data to the socket. 285   /** Asynchronously write data to the socket.
285   286  
286   Calls the backend implementation directly, bypassing virtual 287   Calls the backend implementation directly, bypassing virtual
287   dispatch. Otherwise identical to @ref io_stream::write_some. 288   dispatch. Otherwise identical to @ref io_stream::write_some.
288   289  
289   @param buffers The buffer sequence to write from. 290   @param buffers The buffer sequence to write from.
290   291  
291   @return An awaitable yielding `(error_code, std::size_t)`. 292   @return An awaitable yielding `(error_code, std::size_t)`.
292   */ 293   */
293   template<capy::ConstBufferSequence CB> 294   template<capy::ConstBufferSequence CB>
HITCBC 294   8 [[nodiscard]] auto write_some(CB const& buffers) 295   8 [[nodiscard]] auto write_some(CB const& buffers)
295   { 296   {
HITCBC 296   8 return native_write_awaitable<CB>(*this, buffers); 297   8 return native_write_awaitable<CB>(*this, buffers);
297   } 298   }
298   299  
299   /** Asynchronously connect to a remote endpoint. 300   /** Asynchronously connect to a remote endpoint.
300   301  
301   Calls the backend implementation directly, bypassing virtual 302   Calls the backend implementation directly, bypassing virtual
302   dispatch. Otherwise identical to @ref local_stream_socket::connect. 303   dispatch. Otherwise identical to @ref local_stream_socket::connect.
303   304  
304   If the socket is not already open, it is opened automatically. 305   If the socket is not already open, it is opened automatically.
305   306  
306   @param ep The local endpoint (path) to connect to. 307   @param ep The local endpoint (path) to connect to.
307   308  
308   @return An awaitable yielding `io_result<>`. 309   @return An awaitable yielding `io_result<>`.
309   310  
310   If the socket needs to be opened and the open fails, the 311   If the socket needs to be opened and the open fails, the
311   awaitable completes immediately with that error. 312   awaitable completes immediately with that error.
312   */ 313   */
HITCBC 313   12 [[nodiscard]] auto connect(corosio::local_endpoint ep) 314   12 [[nodiscard]] auto connect(corosio::local_endpoint ep)
314   { 315   {
HITCBC 315   12 native_connect_awaitable aw(*this, ep); 316   12 native_connect_awaitable aw(*this, ep);
HITCBC 316   12 if (!is_open()) 317   12 if (!is_open())
HITCBC 317   10 aw.ec_ = open(); 318   10 aw.ec_ = open();
HITCBC 318   12 return aw; 319   12 return aw;
319   } 320   }
320   321  
321   /** Asynchronously wait for the socket to be ready. 322   /** Asynchronously wait for the socket to be ready.
322   323  
323   Calls the backend implementation directly, bypassing virtual 324   Calls the backend implementation directly, bypassing virtual
324   dispatch. Otherwise identical to @ref local_stream_socket::wait. 325   dispatch. Otherwise identical to @ref local_stream_socket::wait.
325   326  
326   @param w The wait direction (read, write, or error). 327   @param w The wait direction (read, write, or error).
327   328  
328   @return An awaitable yielding `io_result<>`. 329   @return An awaitable yielding `io_result<>`.
329   */ 330   */
HITCBC 330   6 [[nodiscard]] auto wait(wait_type w) 331   6 [[nodiscard]] auto wait(wait_type w)
331   { 332   {
HITCBC 332   6 return native_wait_awaitable(*this, w); 333   6 return native_wait_awaitable(*this, w);
333   } 334   }
334   }; 335   };
335   336  
336   } // namespace boost::corosio 337   } // namespace boost::corosio
337   338  
338   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_SOCKET_HPP 339   #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_SOCKET_HPP