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  
#if BOOST_COROSIO_HAS_EPOLL
16  
#if BOOST_COROSIO_HAS_EPOLL
17  
#include <boost/corosio/native/detail/epoll/epoll_socket_service.hpp>
17  
#include <boost/corosio/native/detail/epoll/epoll_socket_service.hpp>
18  
#endif
18  
#endif
19  

19  

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

23  

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

27  

28  
#if BOOST_COROSIO_HAS_IOCP
28  
#if BOOST_COROSIO_HAS_IOCP
29  
#include <boost/corosio/native/detail/iocp/win_acceptor_service.hpp>
29  
#include <boost/corosio/native/detail/iocp/win_acceptor_service.hpp>
30  
#endif
30  
#endif
31  

31  

32  
namespace boost::corosio {
32  
namespace boost::corosio {
33  

33  

34  
/** An asynchronous TCP socket with devirtualized I/O operations.
34  
/** An asynchronous TCP socket with devirtualized I/O operations.
35  

35  

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

40  

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

43  

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

47  

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

51  

52  
    @par Thread Safety
52  
    @par Thread Safety
53  
    Same as @ref tcp_socket.
53  
    Same as @ref tcp_socket.
54  

54  

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

58  

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

65  

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

74  

75  
    impl_type& get_impl() noexcept
75  
    impl_type& get_impl() noexcept
76  
    {
76  
    {
77  
        return *static_cast<impl_type*>(h_.get());
77  
        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_tcp_socket& self_;
83  
        native_tcp_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  

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

95  

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

100  

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

107  

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

116  

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

125  

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

132  

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

137  

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

144  

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

153  

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

160  

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

166  

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

171  

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

178  

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

187  

188  
public:
188  
public:
189  
    /** Construct a native socket from an execution context.
189  
    /** Construct a native socket from an execution context.
190  

190  

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

197  

198  
    /** Construct a native socket from an executor.
198  
    /** Construct a native socket from an executor.
199  

199  

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

208  

209 -
    /// Move construct.
209 +
    /** Move construct.
 
210 +

 
211 +
        @param other The socket to move from.
 
212 +

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

220  

212 -
    /// Move assign.
221 +
    /** Move assign.
 
222 +

 
223 +
        @param other The socket to move from.
 
224 +

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

233  

215  
    native_tcp_socket(native_tcp_socket const&)            = delete;
234  
    native_tcp_socket(native_tcp_socket const&)            = delete;
216  
    native_tcp_socket& operator=(native_tcp_socket const&) = delete;
235  
    native_tcp_socket& operator=(native_tcp_socket const&) = delete;
217  

236  

218  
    /** Asynchronously read data from the socket.
237  
    /** Asynchronously read data from the socket.
219  

238  

220  
        Calls the backend implementation directly, bypassing virtual
239  
        Calls the backend implementation directly, bypassing virtual
221  
        dispatch. Otherwise identical to @ref io_stream::read_some.
240  
        dispatch. Otherwise identical to @ref io_stream::read_some.
222  

241  

223  
        @param buffers The buffer sequence to read into.
242  
        @param buffers The buffer sequence to read into.
224  

243  

225  
        @return An awaitable yielding `(error_code, std::size_t)`.
244  
        @return An awaitable yielding `(error_code, std::size_t)`.
 
245 +

 
246 +
        This socket must outlive the returned awaitable. The memory
 
247 +
        referenced by @p buffers must remain valid until the operation
 
248 +
        completes.
226  
    */
249  
    */
227  
    template<capy::MutableBufferSequence MB>
250  
    template<capy::MutableBufferSequence MB>
228  
    auto read_some(MB const& buffers)
251  
    auto read_some(MB const& buffers)
229  
    {
252  
    {
230  
        return native_read_awaitable<MB>(*this, buffers);
253  
        return native_read_awaitable<MB>(*this, buffers);
231  
    }
254  
    }
232  

255  

233  
    /** Asynchronously write data to the socket.
256  
    /** Asynchronously write data to the socket.
234  

257  

235  
        Calls the backend implementation directly, bypassing virtual
258  
        Calls the backend implementation directly, bypassing virtual
236  
        dispatch. Otherwise identical to @ref io_stream::write_some.
259  
        dispatch. Otherwise identical to @ref io_stream::write_some.
237  

260  

238  
        @param buffers The buffer sequence to write from.
261  
        @param buffers The buffer sequence to write from.
239  

262  

240  
        @return An awaitable yielding `(error_code, std::size_t)`.
263  
        @return An awaitable yielding `(error_code, std::size_t)`.
 
264 +

 
265 +
        This socket must outlive the returned awaitable. The memory
 
266 +
        referenced by @p buffers must remain valid until the operation
 
267 +
        completes.
241  
    */
268  
    */
242  
    template<capy::ConstBufferSequence CB>
269  
    template<capy::ConstBufferSequence CB>
243  
    auto write_some(CB const& buffers)
270  
    auto write_some(CB const& buffers)
244  
    {
271  
    {
245  
        return native_write_awaitable<CB>(*this, buffers);
272  
        return native_write_awaitable<CB>(*this, buffers);
246  
    }
273  
    }
247  

274  

248  
    /** Asynchronously connect to a remote endpoint.
275  
    /** Asynchronously connect to a remote endpoint.
249  

276  

250  
        Calls the backend implementation directly, bypassing virtual
277  
        Calls the backend implementation directly, bypassing virtual
251  
        dispatch. Otherwise identical to @ref tcp_socket::connect.
278  
        dispatch. Otherwise identical to @ref tcp_socket::connect.
252  

279  

253  
        @param ep The remote endpoint to connect to.
280  
        @param ep The remote endpoint to connect to.
254  

281  

255  
        @return An awaitable yielding `io_result<>`.
282  
        @return An awaitable yielding `io_result<>`.
256  

283  

257  
        @throws std::logic_error if the socket is not open.
284  
        @throws std::logic_error if the socket is not open.
 
285 +

 
286 +
        This socket must outlive the returned awaitable.
258  
    */
287  
    */
259  
    auto connect(endpoint ep)
288  
    auto connect(endpoint ep)
260  
    {
289  
    {
261  
        if (!is_open())
290  
        if (!is_open())
262  
            detail::throw_logic_error("connect: socket not open");
291  
            detail::throw_logic_error("connect: socket not open");
263  
        return native_connect_awaitable(*this, ep);
292  
        return native_connect_awaitable(*this, ep);
264  
    }
293  
    }
265  
};
294  
};
266  

295  

267  
} // namespace boost::corosio
296  
} // namespace boost::corosio
268  

297  

269  
#endif
298  
#endif