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_ACCEPTOR_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
11  
#define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
11  
#define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
12  

12  

13  
#include <boost/corosio/tcp_acceptor.hpp>
13  
#include <boost/corosio/tcp_acceptor.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_acceptor_service.hpp>
17  
#include <boost/corosio/native/detail/epoll/epoll_acceptor_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_acceptor_service.hpp>
21  
#include <boost/corosio/native/detail/select/select_acceptor_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_acceptor_service.hpp>
25  
#include <boost/corosio/native/detail/kqueue/kqueue_acceptor_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 acceptor with devirtualized accept operations.
34  
/** An asynchronous TCP acceptor with devirtualized accept operations.
35  

35  

36  
    This class template inherits from @ref tcp_acceptor and shadows
36  
    This class template inherits from @ref tcp_acceptor and shadows
37  
    the `accept` operation with a version that calls the backend
37  
    the `accept` operation with a version that calls the backend
38  
    implementation directly, allowing the compiler to inline through
38  
    implementation directly, allowing the compiler to inline through
39  
    the entire call chain.
39  
    the entire call chain.
40  

40  

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

43  

44  
    A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed
44  
    A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed
45  
    to any function expecting `tcp_acceptor&`.
45  
    to any function expecting `tcp_acceptor&`.
46  

46  

47  
    @tparam Backend A backend tag value (e.g., `epoll`).
47  
    @tparam Backend A backend tag value (e.g., `epoll`).
48  

48  

49  
    @par Thread Safety
49  
    @par Thread Safety
50  
    Same as @ref tcp_acceptor.
50  
    Same as @ref tcp_acceptor.
51  

51  

52  
    @see tcp_acceptor, epoll_t, iocp_t
52  
    @see tcp_acceptor, epoll_t, iocp_t
53  
*/
53  
*/
54  
template<auto Backend>
54  
template<auto Backend>
55  
class native_tcp_acceptor : public tcp_acceptor
55  
class native_tcp_acceptor : public tcp_acceptor
56  
{
56  
{
57  
    using backend_type = decltype(Backend);
57  
    using backend_type = decltype(Backend);
58  
    using impl_type    = typename backend_type::acceptor_type;
58  
    using impl_type    = typename backend_type::acceptor_type;
59  
    using service_type = typename backend_type::acceptor_service_type;
59  
    using service_type = typename backend_type::acceptor_service_type;
60  

60  

61  
    impl_type& get_impl() noexcept
61  
    impl_type& get_impl() noexcept
62  
    {
62  
    {
63  
        return *static_cast<impl_type*>(h_.get());
63  
        return *static_cast<impl_type*>(h_.get());
64  
    }
64  
    }
65  

65  

66  
    struct native_accept_awaitable
66  
    struct native_accept_awaitable
67  
    {
67  
    {
68  
        native_tcp_acceptor& acc_;
68  
        native_tcp_acceptor& acc_;
69  
        tcp_socket& peer_;
69  
        tcp_socket& peer_;
70  
        std::stop_token token_;
70  
        std::stop_token token_;
71  
        mutable std::error_code ec_;
71  
        mutable std::error_code ec_;
72  
        mutable io_object::implementation* peer_impl_ = nullptr;
72  
        mutable io_object::implementation* peer_impl_ = nullptr;
73  

73  

74  
        native_accept_awaitable(
74  
        native_accept_awaitable(
75  
            native_tcp_acceptor& acc, tcp_socket& peer) noexcept
75  
            native_tcp_acceptor& acc, tcp_socket& peer) noexcept
76  
            : acc_(acc)
76  
            : acc_(acc)
77  
            , peer_(peer)
77  
            , peer_(peer)
78  
        {
78  
        {
79  
        }
79  
        }
80  

80  

81  
        bool await_ready() const noexcept
81  
        bool await_ready() const noexcept
82  
        {
82  
        {
83  
            return token_.stop_requested();
83  
            return token_.stop_requested();
84  
        }
84  
        }
85  

85  

86  
        capy::io_result<> await_resume() const noexcept
86  
        capy::io_result<> await_resume() const noexcept
87  
        {
87  
        {
88  
            if (token_.stop_requested())
88  
            if (token_.stop_requested())
89  
                return {make_error_code(std::errc::operation_canceled)};
89  
                return {make_error_code(std::errc::operation_canceled)};
90  
            if (!ec_)
90  
            if (!ec_)
91  
                acc_.reset_peer_impl(peer_, peer_impl_);
91  
                acc_.reset_peer_impl(peer_, peer_impl_);
92  
            return {ec_};
92  
            return {ec_};
93  
        }
93  
        }
94  

94  

95  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
95  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
96  
            -> std::coroutine_handle<>
96  
            -> std::coroutine_handle<>
97  
        {
97  
        {
98  
            token_ = env->stop_token;
98  
            token_ = env->stop_token;
99  
            return acc_.get_impl().accept(
99  
            return acc_.get_impl().accept(
100  
                h, env->executor, token_, &ec_, &peer_impl_);
100  
                h, env->executor, token_, &ec_, &peer_impl_);
101  
        }
101  
        }
102  
    };
102  
    };
103  

103  

104  
public:
104  
public:
105  
    /** Construct a native acceptor from an execution context.
105  
    /** Construct a native acceptor from an execution context.
106  

106  

107  
        @param ctx The execution context that will own this acceptor.
107  
        @param ctx The execution context that will own this acceptor.
108  
    */
108  
    */
109  
    explicit native_tcp_acceptor(capy::execution_context& ctx)
109  
    explicit native_tcp_acceptor(capy::execution_context& ctx)
110  
        : tcp_acceptor(create_handle<service_type>(ctx))
110  
        : tcp_acceptor(create_handle<service_type>(ctx))
111  
    {
111  
    {
112  
    }
112  
    }
113  

113  

114  
    /** Construct a native acceptor from an executor.
114  
    /** Construct a native acceptor from an executor.
115  

115  

116  
        @param ex The executor whose context will own the acceptor.
116  
        @param ex The executor whose context will own the acceptor.
117  
    */
117  
    */
118  
    template<class Ex>
118  
    template<class Ex>
119  
        requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
119  
        requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
120  
        capy::Executor<Ex>
120  
        capy::Executor<Ex>
121  
    explicit native_tcp_acceptor(Ex const& ex)
121  
    explicit native_tcp_acceptor(Ex const& ex)
122  
        : native_tcp_acceptor(ex.context())
122  
        : native_tcp_acceptor(ex.context())
123  
    {
123  
    {
124  
    }
124  
    }
125  

125  

126  
    /// Move construct.
126  
    /// Move construct.
127  
    native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
127  
    native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
128  

128  

129  
    /// Move assign.
129  
    /// Move assign.
130  
    native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
130  
    native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
131  

131  

132  
    native_tcp_acceptor(native_tcp_acceptor const&)            = delete;
132  
    native_tcp_acceptor(native_tcp_acceptor const&)            = delete;
133  
    native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
133  
    native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
134  

134  

135  
    /** Asynchronously accept an incoming connection.
135  
    /** Asynchronously accept an incoming connection.
136  

136  

137  
        Calls the backend implementation directly, bypassing virtual
137  
        Calls the backend implementation directly, bypassing virtual
138  
        dispatch. Otherwise identical to @ref tcp_acceptor::accept.
138  
        dispatch. Otherwise identical to @ref tcp_acceptor::accept.
139  

139  

140  
        @param peer The socket to receive the accepted connection.
140  
        @param peer The socket to receive the accepted connection.
141  

141  

142  
        @return An awaitable yielding `io_result<>`.
142  
        @return An awaitable yielding `io_result<>`.
143  

143  

144  
        @throws std::logic_error if the acceptor is not listening.
144  
        @throws std::logic_error if the acceptor is not listening.
145  
    */
145  
    */
146  
    auto accept(tcp_socket& peer)
146  
    auto accept(tcp_socket& peer)
147  
    {
147  
    {
148  
        if (!is_open())
148  
        if (!is_open())
149  
            detail::throw_logic_error("accept: acceptor not listening");
149  
            detail::throw_logic_error("accept: acceptor not listening");
150  
        return native_accept_awaitable(*this, peer);
150  
        return native_accept_awaitable(*this, peer);
151  
    }
151  
    }
152  
};
152  
};
153  

153  

154  
} // namespace boost::corosio
154  
} // namespace boost::corosio
155  

155  

156  
#endif
156  
#endif