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 -
#ifndef BOOST_COROSIO_MRDOCS
 
16  

15  

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

19  

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

23  

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

27  

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

31  

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

33  

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

35  

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

40  

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

43  

46  
    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
47  
    any function expecting `tcp_socket&` or `io_stream&`, in which
45  
    any function expecting `tcp_socket&` or `io_stream&`, in which
48  
    case virtual dispatch is used transparently.
46  
    case virtual dispatch is used transparently.
49  

47  

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

51  

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

54  

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

58  

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

65  

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

74  

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

79  

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

88  

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

95  

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

100  

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

107  

110  
        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)
111  
            -> std::coroutine_handle<>
109  
            -> std::coroutine_handle<>
112  
        {
110  
        {
113  
            token_ = env->stop_token;
111  
            token_ = env->stop_token;
114  
            return self_.get_impl().read_some(
112  
            return self_.get_impl().read_some(
115  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
113  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
116  
        }
114  
        }
117  
    };
115  
    };
118  

116  

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

125  

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

132  

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

137  

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

144  

147  
        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)
148  
            -> std::coroutine_handle<>
146  
            -> std::coroutine_handle<>
149  
        {
147  
        {
150  
            token_ = env->stop_token;
148  
            token_ = env->stop_token;
151  
            return self_.get_impl().write_some(
149  
            return self_.get_impl().write_some(
152  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
150  
                h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
153  
        }
151  
        }
154  
    };
152  
    };
155  

153  

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

160  

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

166  

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

171  

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

178  

181  
        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)
182  
            -> std::coroutine_handle<>
180  
            -> std::coroutine_handle<>
183  
        {
181  
        {
184  
            token_ = env->stop_token;
182  
            token_ = env->stop_token;
185  
            return self_.get_impl().connect(
183  
            return self_.get_impl().connect(
186  
                h, env->executor, endpoint_, token_, &ec_);
184  
                h, env->executor, endpoint_, token_, &ec_);
187  
        }
185  
        }
188  
    };
186  
    };
189  

187  

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

190  

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

197  

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

199  

202  
        @param ex The executor whose context will own the socket.
200  
        @param ex The executor whose context will own the socket.
203  
    */
201  
    */
204  
    template<class Ex>
202  
    template<class Ex>
205  
        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>) &&
206  
        capy::Executor<Ex>
204  
        capy::Executor<Ex>
207  
    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())
208  
    {
206  
    {
209  
    }
207  
    }
210  

208  

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

210  

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

212  

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

220  

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

222  

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

224  

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

233  

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

236  

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

238  

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

241  

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

243  

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

245  

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

255  

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

257  

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

260  

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

262  

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

264  

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

274  

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

276  

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

279  

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

281  

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

283  

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

285  

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

295  

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

297  

300  
#endif
298  
#endif