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_TCP_SOCKET_HPP
12 : #define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
13 :
14 : #include <boost/corosio/tcp_socket.hpp>
15 : #include <boost/corosio/backend.hpp>
16 : #include <boost/corosio/detail/op_base.hpp>
17 :
18 : #ifndef BOOST_COROSIO_MRDOCS
19 : #if BOOST_COROSIO_HAS_EPOLL
20 : #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21 : #endif
22 :
23 : #if BOOST_COROSIO_HAS_SELECT
24 : #include <boost/corosio/native/detail/select/select_types.hpp>
25 : #endif
26 :
27 : #if BOOST_COROSIO_HAS_KQUEUE
28 : #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29 : #endif
30 :
31 : #if BOOST_COROSIO_HAS_IOCP
32 : #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
33 : #endif
34 :
35 : #if BOOST_COROSIO_HAS_URING
36 : #include <boost/corosio/native/detail/uring/uring_types.hpp>
37 : #endif
38 : #endif // !BOOST_COROSIO_MRDOCS
39 :
40 : namespace boost::corosio {
41 :
42 : /** An asynchronous TCP socket with devirtualized I/O operations.
43 :
44 : This class template inherits from @ref tcp_socket and shadows
45 : the async operations (`read_some`, `write_some`, `connect`) with
46 : versions that call the backend implementation directly, allowing
47 : the compiler to inline through the entire 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_tcp_socket` IS-A `tcp_socket` and can be passed to
53 : any function expecting `tcp_socket&` or `io_stream&`, in which
54 : case virtual dispatch is used transparently.
55 :
56 : @tparam Backend A backend tag value (e.g., `epoll`,
57 : `iocp`) whose type provides the concrete implementation
58 : types.
59 :
60 : @par Thread Safety
61 : Same as @ref tcp_socket.
62 :
63 : @par Example
64 : @par !example native_tcp_socket
65 :
66 : @see tcp_socket, epoll_t, iocp_t
67 : */
68 : template<auto Backend>
69 : class native_tcp_socket : public tcp_socket
70 : {
71 : using backend_type = decltype(Backend);
72 : using impl_type = typename backend_type::tcp_socket_type;
73 : using service_type = typename backend_type::tcp_service_type;
74 :
75 HIT 51 : impl_type& get_impl() noexcept
76 : {
77 51 : return *static_cast<impl_type*>(h_.get());
78 : }
79 :
80 : template<class MutableBufferSequence>
81 : struct native_read_awaitable
82 : : detail::bytes_op_base<native_read_awaitable<MutableBufferSequence>>
83 : {
84 : native_tcp_socket& self_;
85 : MutableBufferSequence buffers_;
86 :
87 14 : native_read_awaitable(
88 : native_tcp_socket& self, MutableBufferSequence buffers) noexcept
89 14 : : self_(self)
90 14 : , buffers_(std::move(buffers))
91 : {
92 14 : }
93 :
94 : std::coroutine_handle<>
95 10 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
96 : {
97 30 : return self_.get_impl().read_some(
98 30 : h, ex, buffers_, this->token_, &this->ec_, &this->bytes_);
99 : }
100 : };
101 :
102 : template<class ConstBufferSequence>
103 : struct native_write_awaitable
104 : : detail::bytes_op_base<native_write_awaitable<ConstBufferSequence>>
105 : {
106 : native_tcp_socket& self_;
107 : ConstBufferSequence buffers_;
108 :
109 14 : native_write_awaitable(
110 : native_tcp_socket& self, ConstBufferSequence buffers) noexcept
111 14 : : self_(self)
112 14 : , buffers_(std::move(buffers))
113 : {
114 14 : }
115 :
116 : std::coroutine_handle<>
117 12 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
118 : {
119 36 : return self_.get_impl().write_some(
120 36 : h, ex, buffers_, this->token_, &this->ec_, &this->bytes_);
121 : }
122 : };
123 :
124 : struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
125 : {
126 : native_tcp_socket& self_;
127 : wait_type w_;
128 :
129 10 : native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept
130 10 : : self_(self)
131 10 : , w_(w)
132 : {
133 10 : }
134 :
135 : std::coroutine_handle<>
136 8 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
137 : {
138 8 : return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_);
139 : }
140 : };
141 :
142 : struct native_connect_awaitable
143 : : detail::void_op_base<native_connect_awaitable>
144 : {
145 : native_tcp_socket& self_;
146 : endpoint endpoint_;
147 :
148 23 : native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
149 23 : : self_(self)
150 23 : , endpoint_(ep)
151 : {
152 23 : }
153 :
154 : std::coroutine_handle<>
155 21 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
156 : {
157 63 : return self_.get_impl().connect(
158 63 : h, ex, endpoint_, this->token_, &this->ec_);
159 : }
160 : };
161 :
162 : public:
163 : /** Construct a native socket from an execution context.
164 :
165 : @param ctx The execution context that will own this socket.
166 : */
167 49 : explicit native_tcp_socket(capy::execution_context& ctx)
168 49 : : io_object(create_handle<service_type>(ctx))
169 : {
170 49 : }
171 :
172 : /** Construct a native socket from an executor.
173 :
174 : @param ex The executor whose context will own the socket.
175 : */
176 : template<class Ex>
177 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
178 : capy::Executor<Ex>
179 : explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
180 : {
181 : }
182 :
183 : /** Move construct.
184 :
185 : @param other The socket to move from.
186 :
187 : @pre No awaitables returned by @p other's methods exist.
188 : @pre @p other is not referenced as a peer in any outstanding
189 : accept awaitable.
190 : @pre The execution context associated with @p other must
191 : outlive this socket.
192 : */
193 28 : native_tcp_socket(native_tcp_socket&&) noexcept = default;
194 :
195 : /** Move assign.
196 :
197 : @param other The socket to move from.
198 :
199 : @pre No awaitables returned by either `*this` or @p other's
200 : methods exist.
201 : @pre Neither `*this` nor @p other is referenced as a peer in
202 : any outstanding accept awaitable.
203 : @pre The execution context associated with @p other must
204 : outlive this socket.
205 : */
206 3 : native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
207 :
208 : native_tcp_socket(native_tcp_socket const&) = delete;
209 : native_tcp_socket& operator=(native_tcp_socket const&) = delete;
210 :
211 : /** Asynchronously read data from the socket.
212 :
213 : Calls the backend implementation directly, bypassing virtual
214 : dispatch. Otherwise identical to @ref io_stream::read_some.
215 :
216 : @param buffers The buffer sequence to read into.
217 :
218 : @return An awaitable yielding `(error_code, std::size_t)`.
219 :
220 : This socket must outlive the returned awaitable. The memory
221 : referenced by @p buffers must remain valid until the operation
222 : completes.
223 : */
224 : template<capy::MutableBufferSequence MB>
225 14 : [[nodiscard]] auto read_some(MB const& buffers)
226 : {
227 14 : return native_read_awaitable<MB>(*this, buffers);
228 : }
229 :
230 : /** Asynchronously write data to the socket.
231 :
232 : Calls the backend implementation directly, bypassing virtual
233 : dispatch. Otherwise identical to @ref io_stream::write_some.
234 :
235 : @param buffers The buffer sequence to write from.
236 :
237 : @return An awaitable yielding `(error_code, std::size_t)`.
238 :
239 : This socket must outlive the returned awaitable. The memory
240 : referenced by @p buffers must remain valid until the operation
241 : completes.
242 : */
243 : template<capy::ConstBufferSequence CB>
244 14 : [[nodiscard]] auto write_some(CB const& buffers)
245 : {
246 14 : return native_write_awaitable<CB>(*this, buffers);
247 : }
248 :
249 : /** Asynchronously connect to a remote endpoint.
250 :
251 : Calls the backend implementation directly, bypassing virtual
252 : dispatch. Otherwise identical to @ref tcp_socket::connect.
253 :
254 : If the socket is not open, it is opened automatically using
255 : the protocol matching the endpoint's address family. An open
256 : failure surfaces through the connect completion.
257 :
258 : @param ep The remote endpoint to connect to.
259 :
260 : @return An awaitable yielding `io_result<>`.
261 :
262 : This socket must outlive the returned awaitable.
263 : */
264 23 : [[nodiscard]] auto connect(endpoint ep)
265 : {
266 23 : native_connect_awaitable aw(*this, ep);
267 23 : if (!is_open())
268 2 : aw.ec_ = open(ep.address().family());
269 23 : return aw;
270 : }
271 :
272 : /** Asynchronously wait for the socket to be ready.
273 :
274 : Calls the backend implementation directly, bypassing virtual
275 : dispatch. Otherwise identical to @ref tcp_socket::wait.
276 :
277 : @param w The wait direction (read, write, or error).
278 :
279 : @return An awaitable yielding `io_result<>`.
280 : */
281 10 : [[nodiscard]] auto wait(wait_type w)
282 : {
283 10 : return native_wait_awaitable(*this, w);
284 : }
285 : };
286 :
287 : } // namespace boost::corosio
288 :
289 : #endif
|