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

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

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

 
28  
#ifndef BOOST_COROSIO_MRDOCS
16  
#ifndef BOOST_COROSIO_MRDOCS
29  
#if BOOST_COROSIO_HAS_IOCP
17  
#if BOOST_COROSIO_HAS_IOCP
30  
#include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
18  
#include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
31  
#endif
19  
#endif
32  
#endif // !BOOST_COROSIO_MRDOCS
20  
#endif // !BOOST_COROSIO_MRDOCS
33  

21  

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

23  

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

25  

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

30  

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

33  

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

37  

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

41  

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

44  

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

48  

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

55  

68  
    @see tcp_socket, epoll_t, iocp_t
56  
    @see tcp_socket, epoll_t, iocp_t
69  
*/
57  
*/
70  
template<auto Backend>
58  
template<auto Backend>
71  
class native_tcp_socket : public tcp_socket
59  
class native_tcp_socket : public tcp_socket
72  
{
60  
{
73  
    using backend_type = decltype(Backend);
61  
    using backend_type = decltype(Backend);
74  
    using impl_type    = typename backend_type::tcp_socket_type;
62  
    using impl_type    = typename backend_type::tcp_socket_type;
75  
    using service_type = typename backend_type::tcp_service_type;
63  
    using service_type = typename backend_type::tcp_service_type;
76  

64  

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

69  

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

78  

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

85  

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

90  

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

97  

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

106  

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

115  

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

122  

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

127  

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

134  

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

143  

156  
    struct native_connect_awaitable
144  
    struct native_connect_awaitable
157  
    {
145  
    {
158  
        native_tcp_socket& self_;
146  
        native_tcp_socket& self_;
159  
        endpoint endpoint_;
147  
        endpoint endpoint_;
160  
        std::stop_token token_;
148  
        std::stop_token token_;
161  
        mutable std::error_code ec_;
149  
        mutable std::error_code ec_;
162  

150  

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

156  

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

161  

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

168  

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

177  

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

180  

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

187  

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

189  

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

198  

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

200  

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

202  

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

210  

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

212  

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

214  

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

223  

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

226  

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

228  

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

231  

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

233  

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

235  

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

245  

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

247  

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

250  

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

252  

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

254  

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

264  

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

266  

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

269  

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

271  

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

273  

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

275  

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

285  

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

287  

300  
#endif
288  
#endif