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 : /** Connects, reads, and writes over TCP, calling the backend directly.
43 :
44 : This class template inherits from @ref tcp_socket. It shadows the
45 : async operations (`read_some`, `write_some`, `connect`) with
46 : versions that call the backend implementation directly. The compiler
47 : can then 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 that
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 owns this socket.
166 : */
167 49 : explicit native_tcp_socket(capy::execution_context& ctx)
168 49 : : io_object(handle(ctx, ctx.use_service<service_type>()))
169 : {
170 49 : }
171 :
172 : /** Construct a native socket from an executor.
173 :
174 : @param ex The executor whose context owns the socket.
175 :
176 : @tparam Ex A type satisfying @ref capy::Executor. Must not
177 : be `native_tcp_socket` itself (disables implicit
178 : conversion from move).
179 : */
180 : template<class Ex>
181 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
182 : capy::Executor<Ex>
183 : explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
184 : {
185 : }
186 :
187 : /** Move construct.
188 :
189 : @param other The socket to move from.
190 :
191 : @pre No awaitables returned by @p other's methods exist.
192 : @pre @p other is not referenced as a peer in any outstanding
193 : accept awaitable.
194 : @pre The execution context associated with @p other must
195 : outlive this socket.
196 : */
197 28 : native_tcp_socket(native_tcp_socket&&) noexcept = default;
198 :
199 : /** Move assign.
200 :
201 : @param other The socket to move from.
202 :
203 : @pre No awaitables returned by either `*this` or @p other's
204 : methods exist.
205 : @pre Neither `*this` nor @p other is referenced as a peer in
206 : any outstanding accept awaitable.
207 : @pre The execution context associated with @p other must
208 : outlive this socket.
209 : */
210 3 : native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
211 :
212 : /// Copy construction is disabled; the handle is uniquely owned.
213 : native_tcp_socket(native_tcp_socket const&) = delete;
214 : /// Copy assignment is disabled; the handle is uniquely owned.
215 : native_tcp_socket& operator=(native_tcp_socket const&) = delete;
216 :
217 : /** Asynchronously read data from the socket.
218 :
219 : Calls the backend implementation directly, bypassing virtual
220 : dispatch. Otherwise identical to @ref io_stream::read_some.
221 :
222 : @param buffers The buffer sequence to read into.
223 :
224 : @return An awaitable yielding `(error_code, std::size_t)`.
225 :
226 : This socket must outlive the returned awaitable. The memory
227 : referenced by @p buffers must remain valid until the operation
228 : completes.
229 : */
230 : template<capy::MutableBufferSequence MB>
231 14 : [[nodiscard]] auto read_some(MB const& buffers)
232 : {
233 14 : return native_read_awaitable<MB>(*this, buffers);
234 : }
235 :
236 : /** Asynchronously write data to the socket.
237 :
238 : Calls the backend implementation directly, bypassing virtual
239 : dispatch. Otherwise identical to @ref io_stream::write_some.
240 :
241 : @param buffers The buffer sequence to write from.
242 :
243 : @return An awaitable yielding `(error_code, std::size_t)`.
244 :
245 : This socket must outlive the returned awaitable. The memory
246 : referenced by @p buffers must remain valid until the operation
247 : completes.
248 : */
249 : template<capy::ConstBufferSequence CB>
250 14 : [[nodiscard]] auto write_some(CB const& buffers)
251 : {
252 14 : return native_write_awaitable<CB>(*this, buffers);
253 : }
254 :
255 : /** Asynchronously connect to a remote endpoint.
256 :
257 : Calls the backend implementation directly, bypassing virtual
258 : dispatch. Otherwise identical to @ref tcp_socket::connect.
259 :
260 : If the socket is not open, it is opened automatically using
261 : the protocol matching the endpoint's address family. An open
262 : failure surfaces through the connect completion.
263 :
264 : @param ep The remote endpoint to connect to.
265 :
266 : @return An awaitable yielding `io_result<>`.
267 :
268 : This socket must outlive the returned awaitable.
269 : */
270 23 : [[nodiscard]] auto connect(endpoint ep)
271 : {
272 23 : native_connect_awaitable aw(*this, ep);
273 23 : if (!is_open())
274 2 : aw.ec_ = open(ep.address().family());
275 23 : return aw;
276 : }
277 :
278 : /** Asynchronously wait for the socket to be ready.
279 :
280 : Calls the backend implementation directly, bypassing virtual
281 : dispatch. Otherwise identical to @ref tcp_socket::wait.
282 :
283 : @param w The wait direction (read, write, or error).
284 :
285 : @return An awaitable yielding `io_result<>`.
286 : */
287 10 : [[nodiscard]] auto wait(wait_type w)
288 : {
289 10 : return native_wait_awaitable(*this, w);
290 : }
291 : };
292 :
293 : } // namespace boost::corosio
294 :
295 : #endif
|