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

20  

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

24  

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

28  

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

33  

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

35  

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

37  

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

42  

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

45  

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

49  

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

53  

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

56  

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

60  

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

67  

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

76  

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

81  

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

90  

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

97  

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

102  

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

109  

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

118  

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

127  

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

134  

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

139  

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

146  

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

155  

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

162  

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

168  

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

173  

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

180  

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

189  

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

192  

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

199  

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

201  

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

210  

209  
    /** Move construct.
211  
    /** Move construct.
210  

212  

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

214  

213  
        @pre No awaitables returned by @p other's methods exist.
215  
        @pre No awaitables returned by @p other's methods exist.
214  
        @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
215  
            accept awaitable.
217  
            accept awaitable.
216  
        @pre The execution context associated with @p other must
218  
        @pre The execution context associated with @p other must
217  
            outlive this socket.
219  
            outlive this socket.
218  
    */
220  
    */
219  
    native_tcp_socket(native_tcp_socket&&) noexcept = default;
221  
    native_tcp_socket(native_tcp_socket&&) noexcept = default;
220  

222  

221  
    /** Move assign.
223  
    /** Move assign.
222  

224  

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

226  

225  
        @pre No awaitables returned by either `*this` or @p other's
227  
        @pre No awaitables returned by either `*this` or @p other's
226  
            methods exist.
228  
            methods exist.
227  
        @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
228  
            any outstanding accept awaitable.
230  
            any outstanding accept awaitable.
229  
        @pre The execution context associated with @p other must
231  
        @pre The execution context associated with @p other must
230  
            outlive this socket.
232  
            outlive this socket.
231  
    */
233  
    */
232  
    native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
234  
    native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
233  

235  

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

238  

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

240  

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

243  

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

245  

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

247  

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

257  

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

259  

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

262  

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

264  

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

266  

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

276  

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

278  

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

281  

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

283  

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

285  

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

287  

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

297  

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

299  

298  
#endif
300  
#endif