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_TCP_SOCKET_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
11  
#define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
11  
#define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
12  

12  

13  
#include <boost/corosio/tcp_socket.hpp>
13  
#include <boost/corosio/tcp_socket.hpp>
14  
#include <boost/corosio/backend.hpp>
14  
#include <boost/corosio/backend.hpp>
15  

15  

16  
#ifndef BOOST_COROSIO_MRDOCS
16  
#ifndef BOOST_COROSIO_MRDOCS
17  
#if BOOST_COROSIO_HAS_EPOLL
17  
#if BOOST_COROSIO_HAS_EPOLL
18  
#include <boost/corosio/native/detail/epoll/epoll_socket_service.hpp>
18  
#include <boost/corosio/native/detail/epoll/epoll_socket_service.hpp>
19  
#endif
19  
#endif
20  

20  

21  
#if BOOST_COROSIO_HAS_SELECT
21  
#if BOOST_COROSIO_HAS_SELECT
22  
#include <boost/corosio/native/detail/select/select_socket_service.hpp>
22  
#include <boost/corosio/native/detail/select/select_socket_service.hpp>
23  
#endif
23  
#endif
24  

24  

25  
#if BOOST_COROSIO_HAS_KQUEUE
25  
#if BOOST_COROSIO_HAS_KQUEUE
26  
#include <boost/corosio/native/detail/kqueue/kqueue_socket_service.hpp>
26  
#include <boost/corosio/native/detail/kqueue/kqueue_socket_service.hpp>
27  
#endif
27  
#endif
28  

28  

29  
#if BOOST_COROSIO_HAS_IOCP
29  
#if BOOST_COROSIO_HAS_IOCP
30  
#include <boost/corosio/native/detail/iocp/win_acceptor_service.hpp>
30  
#include <boost/corosio/native/detail/iocp/win_acceptor_service.hpp>
31  
#endif
31  
#endif
32  
#endif // !BOOST_COROSIO_MRDOCS
32  
#endif // !BOOST_COROSIO_MRDOCS
33  

33  

34  
namespace boost::corosio {
34  
namespace boost::corosio {
35  

35  

36  
/** An asynchronous TCP socket with devirtualized I/O operations.
36  
/** An asynchronous TCP socket with devirtualized I/O operations.
37  

37  

38  
    This class template inherits from @ref tcp_socket and shadows
38  
    This class template inherits from @ref tcp_socket and shadows
39  
    the async operations (`read_some`, `write_some`, `connect`) with
39  
    the async operations (`read_some`, `write_some`, `connect`) with
40  
    versions that call the backend implementation directly, allowing
40  
    versions that call the backend implementation directly, allowing
41  
    the compiler to inline through the entire call chain.
41  
    the compiler to inline through the entire call chain.
42  

42  

43  
    Non-async operations (`open`, `close`, `cancel`, socket options)
43  
    Non-async operations (`open`, `close`, `cancel`, socket options)
44  
    remain unchanged and dispatch through the compiled library.
44  
    remain unchanged and dispatch through the compiled library.
45  

45  

46  
    A `native_tcp_socket` IS-A `tcp_socket` and can be passed to
46  
    A `native_tcp_socket` IS-A `tcp_socket` and can be passed to
47  
    any function expecting `tcp_socket&` or `io_stream&`, in which
47  
    any function expecting `tcp_socket&` or `io_stream&`, in which
48  
    case virtual dispatch is used transparently.
48  
    case virtual dispatch is used transparently.
49  

49  

50  
    @tparam Backend A backend tag value (e.g., `epoll`,
50  
    @tparam Backend A backend tag value (e.g., `epoll`,
51  
        `iocp`) whose type provides the concrete implementation
51  
        `iocp`) whose type provides the concrete implementation
52  
        types.
52  
        types.
53  

53  

54  
    @par Thread Safety
54  
    @par Thread Safety
55  
    Same as @ref tcp_socket.
55  
    Same as @ref tcp_socket.
56  

56  

57  
    @par Example
57  
    @par Example
58  
    @code
58  
    @code
59  
    #include <boost/corosio/native/native_tcp_socket.hpp>
59  
    #include <boost/corosio/native/native_tcp_socket.hpp>
60  

60  

61  
    native_io_context<epoll> ctx;
61  
    native_io_context<epoll> ctx;
62  
    native_tcp_socket<epoll> s(ctx);
62  
    native_tcp_socket<epoll> s(ctx);
63  
    s.open();
63  
    s.open();
64  
    auto [ec] = co_await s.connect(ep);
64  
    auto [ec] = co_await s.connect(ep);
65  
    auto [ec2, n] = co_await s.read_some(buf);
65  
    auto [ec2, n] = co_await s.read_some(buf);
66  
    @endcode
66  
    @endcode
67  

67  

68  
    @see tcp_socket, epoll_t, iocp_t
68  
    @see tcp_socket, epoll_t, iocp_t
69  
*/
69  
*/
70  
template<auto Backend>
70  
template<auto Backend>
71  
class native_tcp_socket : public tcp_socket
71  
class native_tcp_socket : public tcp_socket
72  
{
72  
{
73  
    using backend_type = decltype(Backend);
73  
    using backend_type = decltype(Backend);
74  
    using impl_type    = typename backend_type::socket_type;
74  
    using impl_type    = typename backend_type::socket_type;
75  
    using service_type = typename backend_type::socket_service_type;
75  
    using service_type = typename backend_type::socket_service_type;
76  

76  

77  
    impl_type& get_impl() noexcept
77  
    impl_type& get_impl() noexcept
78  
    {
78  
    {
79  
        return *static_cast<impl_type*>(h_.get());
79  
        return *static_cast<impl_type*>(h_.get());
80  
    }
80  
    }
81  

81  

82  
    template<class MutableBufferSequence>
82  
    template<class MutableBufferSequence>
83  
    struct native_read_awaitable
83  
    struct native_read_awaitable
84  
    {
84  
    {
85  
        native_tcp_socket& self_;
85  
        native_tcp_socket& self_;
86  
        MutableBufferSequence buffers_;
86  
        MutableBufferSequence buffers_;
87  
        std::stop_token token_;
87  
        std::stop_token token_;
88  
        mutable std::error_code ec_;
88  
        mutable std::error_code ec_;
89  
        mutable std::size_t bytes_transferred_ = 0;
89  
        mutable std::size_t bytes_transferred_ = 0;
90  

90  

91  
        native_read_awaitable(
91  
        native_read_awaitable(
92  
            native_tcp_socket& self, MutableBufferSequence buffers) noexcept
92  
            native_tcp_socket& self, MutableBufferSequence buffers) noexcept
93  
            : self_(self)
93  
            : self_(self)
94  
            , buffers_(std::move(buffers))
94  
            , buffers_(std::move(buffers))
95  
        {
95  
        {
96  
        }
96  
        }
97  

97  

98  
        bool await_ready() const noexcept
98  
        bool await_ready() const noexcept
99  
        {
99  
        {
100  
            return token_.stop_requested();
100  
            return token_.stop_requested();
101  
        }
101  
        }
102  

102  

103  
        capy::io_result<std::size_t> await_resume() const noexcept
103  
        capy::io_result<std::size_t> await_resume() const noexcept
104  
        {
104  
        {
105  
            if (token_.stop_requested())
105  
            if (token_.stop_requested())
106  
                return {make_error_code(std::errc::operation_canceled), 0};
106  
                return {make_error_code(std::errc::operation_canceled), 0};
107  
            return {ec_, bytes_transferred_};
107  
            return {ec_, bytes_transferred_};
108  
        }
108  
        }
109  

109  

110  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
110  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
111  
            -> std::coroutine_handle<>
111  
            -> std::coroutine_handle<>
112  
        {
112  
        {
113  
            token_ = env->stop_token;
113  
            token_ = env->stop_token;
114  
            return self_.get_impl().read_some(
114  
            return self_.get_impl().read_some(
115  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
115  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
116  
        }
116  
        }
117  
    };
117  
    };
118  

118  

119  
    template<class ConstBufferSequence>
119  
    template<class ConstBufferSequence>
120  
    struct native_write_awaitable
120  
    struct native_write_awaitable
121  
    {
121  
    {
122  
        native_tcp_socket& self_;
122  
        native_tcp_socket& self_;
123  
        ConstBufferSequence buffers_;
123  
        ConstBufferSequence buffers_;
124  
        std::stop_token token_;
124  
        std::stop_token token_;
125  
        mutable std::error_code ec_;
125  
        mutable std::error_code ec_;
126  
        mutable std::size_t bytes_transferred_ = 0;
126  
        mutable std::size_t bytes_transferred_ = 0;
127  

127  

128  
        native_write_awaitable(
128  
        native_write_awaitable(
129  
            native_tcp_socket& self, ConstBufferSequence buffers) noexcept
129  
            native_tcp_socket& self, ConstBufferSequence buffers) noexcept
130  
            : self_(self)
130  
            : self_(self)
131  
            , buffers_(std::move(buffers))
131  
            , buffers_(std::move(buffers))
132  
        {
132  
        {
133  
        }
133  
        }
134  

134  

135  
        bool await_ready() const noexcept
135  
        bool await_ready() const noexcept
136  
        {
136  
        {
137  
            return token_.stop_requested();
137  
            return token_.stop_requested();
138  
        }
138  
        }
139  

139  

140  
        capy::io_result<std::size_t> await_resume() const noexcept
140  
        capy::io_result<std::size_t> await_resume() const noexcept
141  
        {
141  
        {
142  
            if (token_.stop_requested())
142  
            if (token_.stop_requested())
143  
                return {make_error_code(std::errc::operation_canceled), 0};
143  
                return {make_error_code(std::errc::operation_canceled), 0};
144  
            return {ec_, bytes_transferred_};
144  
            return {ec_, bytes_transferred_};
145  
        }
145  
        }
146  

146  

147  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
147  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
148  
            -> std::coroutine_handle<>
148  
            -> std::coroutine_handle<>
149  
        {
149  
        {
150  
            token_ = env->stop_token;
150  
            token_ = env->stop_token;
151  
            return self_.get_impl().write_some(
151  
            return self_.get_impl().write_some(
152  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
152  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
153  
        }
153  
        }
154  
    };
154  
    };
155  

155  

156  
    struct native_connect_awaitable
156  
    struct native_connect_awaitable
157  
    {
157  
    {
158  
        native_tcp_socket& self_;
158  
        native_tcp_socket& self_;
159  
        endpoint endpoint_;
159  
        endpoint endpoint_;
160  
        std::stop_token token_;
160  
        std::stop_token token_;
161  
        mutable std::error_code ec_;
161  
        mutable std::error_code ec_;
162  

162  

163  
        native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
163  
        native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
164  
            : self_(self)
164  
            : self_(self)
165  
            , endpoint_(ep)
165  
            , endpoint_(ep)
166  
        {
166  
        {
167  
        }
167  
        }
168  

168  

169  
        bool await_ready() const noexcept
169  
        bool await_ready() const noexcept
170  
        {
170  
        {
171  
            return token_.stop_requested();
171  
            return token_.stop_requested();
172  
        }
172  
        }
173  

173  

174  
        capy::io_result<> await_resume() const noexcept
174  
        capy::io_result<> await_resume() const noexcept
175  
        {
175  
        {
176  
            if (token_.stop_requested())
176  
            if (token_.stop_requested())
177  
                return {make_error_code(std::errc::operation_canceled)};
177  
                return {make_error_code(std::errc::operation_canceled)};
178  
            return {ec_};
178  
            return {ec_};
179  
        }
179  
        }
180  

180  

181  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
181  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
182  
            -> std::coroutine_handle<>
182  
            -> std::coroutine_handle<>
183  
        {
183  
        {
184  
            token_ = env->stop_token;
184  
            token_ = env->stop_token;
185  
            return self_.get_impl().connect(
185  
            return self_.get_impl().connect(
186  
                h, env->executor, endpoint_, token_, &ec_);
186  
                h, env->executor, endpoint_, token_, &ec_);
187  
        }
187  
        }
188  
    };
188  
    };
189  

189  

190  
public:
190  
public:
191  
    /** Construct a native socket from an execution context.
191  
    /** Construct a native socket from an execution context.
192  

192  

193  
        @param ctx The execution context that will own this socket.
193  
        @param ctx The execution context that will own this socket.
194  
    */
194  
    */
195  
    explicit native_tcp_socket(capy::execution_context& ctx)
195  
    explicit native_tcp_socket(capy::execution_context& ctx)
196  
        : io_object(create_handle<service_type>(ctx))
196  
        : io_object(create_handle<service_type>(ctx))
197  
    {
197  
    {
198  
    }
198  
    }
199  

199  

200  
    /** Construct a native socket from an executor.
200  
    /** Construct a native socket from an executor.
201  

201  

202  
        @param ex The executor whose context will own the socket.
202  
        @param ex The executor whose context will own the socket.
203  
    */
203  
    */
204  
    template<class Ex>
204  
    template<class Ex>
205  
        requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
205  
        requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
206  
        capy::Executor<Ex>
206  
        capy::Executor<Ex>
207  
    explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
207  
    explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
208  
    {
208  
    {
209  
    }
209  
    }
210  

210  

211  
    /** Move construct.
211  
    /** Move construct.
212  

212  

213  
        @param other The socket to move from.
213  
        @param other The socket to move from.
214  

214  

215  
        @pre No awaitables returned by @p other's methods exist.
215  
        @pre No awaitables returned by @p other's methods exist.
216  
        @pre @p other is not referenced as a peer in any outstanding
216  
        @pre @p other is not referenced as a peer in any outstanding
217  
            accept awaitable.
217  
            accept awaitable.
218  
        @pre The execution context associated with @p other must
218  
        @pre The execution context associated with @p other must
219  
            outlive this socket.
219  
            outlive this socket.
220  
    */
220  
    */
221  
    native_tcp_socket(native_tcp_socket&&) noexcept = default;
221  
    native_tcp_socket(native_tcp_socket&&) noexcept = default;
222  

222  

223  
    /** Move assign.
223  
    /** Move assign.
224  

224  

225  
        @param other The socket to move from.
225  
        @param other The socket to move from.
226  

226  

227  
        @pre No awaitables returned by either `*this` or @p other's
227  
        @pre No awaitables returned by either `*this` or @p other's
228  
            methods exist.
228  
            methods exist.
229  
        @pre Neither `*this` nor @p other is referenced as a peer in
229  
        @pre Neither `*this` nor @p other is referenced as a peer in
230  
            any outstanding accept awaitable.
230  
            any outstanding accept awaitable.
231  
        @pre The execution context associated with @p other must
231  
        @pre The execution context associated with @p other must
232  
            outlive this socket.
232  
            outlive this socket.
233  
    */
233  
    */
234  
    native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
234  
    native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
235  

235  

236  
    native_tcp_socket(native_tcp_socket const&)            = delete;
236  
    native_tcp_socket(native_tcp_socket const&)            = delete;
237  
    native_tcp_socket& operator=(native_tcp_socket const&) = delete;
237  
    native_tcp_socket& operator=(native_tcp_socket const&) = delete;
238  

238  

239  
    /** Asynchronously read data from the socket.
239  
    /** Asynchronously read data from the socket.
240  

240  

241  
        Calls the backend implementation directly, bypassing virtual
241  
        Calls the backend implementation directly, bypassing virtual
242  
        dispatch. Otherwise identical to @ref io_stream::read_some.
242  
        dispatch. Otherwise identical to @ref io_stream::read_some.
243  

243  

244  
        @param buffers The buffer sequence to read into.
244  
        @param buffers The buffer sequence to read into.
245  

245  

246  
        @return An awaitable yielding `(error_code, std::size_t)`.
246  
        @return An awaitable yielding `(error_code, std::size_t)`.
247  

247  

248  
        This socket must outlive the returned awaitable. The memory
248  
        This socket must outlive the returned awaitable. The memory
249  
        referenced by @p buffers must remain valid until the operation
249  
        referenced by @p buffers must remain valid until the operation
250  
        completes.
250  
        completes.
251  
    */
251  
    */
252  
    template<capy::MutableBufferSequence MB>
252  
    template<capy::MutableBufferSequence MB>
253  
    auto read_some(MB const& buffers)
253  
    auto read_some(MB const& buffers)
254  
    {
254  
    {
255  
        return native_read_awaitable<MB>(*this, buffers);
255  
        return native_read_awaitable<MB>(*this, buffers);
256  
    }
256  
    }
257  

257  

258  
    /** Asynchronously write data to the socket.
258  
    /** Asynchronously write data to the socket.
259  

259  

260  
        Calls the backend implementation directly, bypassing virtual
260  
        Calls the backend implementation directly, bypassing virtual
261  
        dispatch. Otherwise identical to @ref io_stream::write_some.
261  
        dispatch. Otherwise identical to @ref io_stream::write_some.
262  

262  

263  
        @param buffers The buffer sequence to write from.
263  
        @param buffers The buffer sequence to write from.
264  

264  

265  
        @return An awaitable yielding `(error_code, std::size_t)`.
265  
        @return An awaitable yielding `(error_code, std::size_t)`.
266  

266  

267  
        This socket must outlive the returned awaitable. The memory
267  
        This socket must outlive the returned awaitable. The memory
268  
        referenced by @p buffers must remain valid until the operation
268  
        referenced by @p buffers must remain valid until the operation
269  
        completes.
269  
        completes.
270  
    */
270  
    */
271  
    template<capy::ConstBufferSequence CB>
271  
    template<capy::ConstBufferSequence CB>
272  
    auto write_some(CB const& buffers)
272  
    auto write_some(CB const& buffers)
273  
    {
273  
    {
274  
        return native_write_awaitable<CB>(*this, buffers);
274  
        return native_write_awaitable<CB>(*this, buffers);
275  
    }
275  
    }
276  

276  

277  
    /** Asynchronously connect to a remote endpoint.
277  
    /** Asynchronously connect to a remote endpoint.
278  

278  

279  
        Calls the backend implementation directly, bypassing virtual
279  
        Calls the backend implementation directly, bypassing virtual
280  
        dispatch. Otherwise identical to @ref tcp_socket::connect.
280  
        dispatch. Otherwise identical to @ref tcp_socket::connect.
281  

281  

282  
        @param ep The remote endpoint to connect to.
282  
        @param ep The remote endpoint to connect to.
283  

283  

284  
        @return An awaitable yielding `io_result<>`.
284  
        @return An awaitable yielding `io_result<>`.
285  

285  

286  
        @throws std::logic_error if the socket is not open.
286  
        @throws std::logic_error if the socket is not open.
287  

287  

288  
        This socket must outlive the returned awaitable.
288  
        This socket must outlive the returned awaitable.
289  
    */
289  
    */
290  
    auto connect(endpoint ep)
290  
    auto connect(endpoint ep)
291  
    {
291  
    {
292  
        if (!is_open())
292  
        if (!is_open())
293  
            detail::throw_logic_error("connect: socket not open");
293  
            detail::throw_logic_error("connect: socket not open");
294  
        return native_connect_awaitable(*this, ep);
294  
        return native_connect_awaitable(*this, ep);
295  
    }
295  
    }
296  
};
296  
};
297  

297  

298  
} // namespace boost::corosio
298  
} // namespace boost::corosio
299  

299  

300  
#endif
300  
#endif