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