1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
11  
#ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
12  
#define BOOST_COROSIO_TCP_ACCEPTOR_HPP
12  
#define BOOST_COROSIO_TCP_ACCEPTOR_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/endpoint.hpp>
18  
#include <boost/corosio/endpoint.hpp>
19  
#include <boost/corosio/tcp.hpp>
19  
#include <boost/corosio/tcp.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
24  
#include <boost/capy/concept/executor.hpp>
24  
#include <boost/capy/concept/executor.hpp>
25  

25  

26  
#include <system_error>
26  
#include <system_error>
27  

27  

28  
#include <concepts>
28  
#include <concepts>
29  
#include <coroutine>
29  
#include <coroutine>
30  
#include <cstddef>
30  
#include <cstddef>
31  
#include <memory>
31  
#include <memory>
32  
#include <stop_token>
32  
#include <stop_token>
33  
#include <type_traits>
33  
#include <type_traits>
34  

34  

35  
namespace boost::corosio {
35  
namespace boost::corosio {
36  

36  

37  
/** An asynchronous TCP acceptor for coroutine I/O.
37  
/** An asynchronous TCP acceptor for coroutine I/O.
38  

38  

39  
    This class provides asynchronous TCP accept operations that return
39  
    This class provides asynchronous TCP accept operations that return
40  
    awaitable types. The acceptor binds to a local endpoint and listens
40  
    awaitable types. The acceptor binds to a local endpoint and listens
41  
    for incoming connections.
41  
    for incoming connections.
42  

42  

43  
    Each accept operation participates in the affine awaitable protocol,
43  
    Each accept operation participates in the affine awaitable protocol,
44  
    ensuring coroutines resume on the correct executor.
44  
    ensuring coroutines resume on the correct executor.
45  

45  

46  
    @par Thread Safety
46  
    @par Thread Safety
47  
    Distinct objects: Safe.@n
47  
    Distinct objects: Safe.@n
48  
    Shared objects: Unsafe. An acceptor must not have concurrent accept
48  
    Shared objects: Unsafe. An acceptor must not have concurrent accept
49  
    operations.
49  
    operations.
50  

50  

51  
    @par Semantics
51  
    @par Semantics
52  
    Wraps the platform TCP listener. Operations dispatch to
52  
    Wraps the platform TCP listener. Operations dispatch to
53  
    OS accept APIs via the io_context reactor.
53  
    OS accept APIs via the io_context reactor.
54  

54  

55  
    @par Example
55  
    @par Example
56  
    @code
56  
    @code
57  
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
57  
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
58  
    io_context ioc;
58  
    io_context ioc;
59  
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
59  
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
60  

60  

61  
    tcp_socket peer( ioc );
61  
    tcp_socket peer( ioc );
62  
    auto [ec] = co_await acc.accept( peer );
62  
    auto [ec] = co_await acc.accept( peer );
63  
    if ( !ec ) {
63  
    if ( !ec ) {
64  
        // peer is now a connected socket
64  
        // peer is now a connected socket
65  
        auto [ec2, n] = co_await peer.read_some( buf );
65  
        auto [ec2, n] = co_await peer.read_some( buf );
66  
    }
66  
    }
67  
    @endcode
67  
    @endcode
68  

68  

69  
    @par Example
69  
    @par Example
70  
    @code
70  
    @code
71  
    // Fine-grained setup
71  
    // Fine-grained setup
72  
    tcp_acceptor acc( ioc );
72  
    tcp_acceptor acc( ioc );
73  
    acc.open( tcp::v6() );
73  
    acc.open( tcp::v6() );
74  
    acc.set_option( socket_option::reuse_address( true ) );
74  
    acc.set_option( socket_option::reuse_address( true ) );
75  
    acc.set_option( socket_option::v6_only( true ) );
75  
    acc.set_option( socket_option::v6_only( true ) );
76  
    if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
76  
    if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
77  
        return ec;
77  
        return ec;
78  
    if ( auto ec = acc.listen() )
78  
    if ( auto ec = acc.listen() )
79  
        return ec;
79  
        return ec;
80  
    @endcode
80  
    @endcode
81  
*/
81  
*/
82  
class BOOST_COROSIO_DECL tcp_acceptor : public io_object
82  
class BOOST_COROSIO_DECL tcp_acceptor : public io_object
83  
{
83  
{
84  
    struct accept_awaitable
84  
    struct accept_awaitable
85  
    {
85  
    {
86  
        tcp_acceptor& acc_;
86  
        tcp_acceptor& acc_;
87  
        tcp_socket& peer_;
87  
        tcp_socket& peer_;
88  
        std::stop_token token_;
88  
        std::stop_token token_;
89  
        mutable std::error_code ec_;
89  
        mutable std::error_code ec_;
90  
        mutable io_object::implementation* peer_impl_ = nullptr;
90  
        mutable io_object::implementation* peer_impl_ = nullptr;
91  

91  

92  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
92  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
93  
            : acc_(acc)
93  
            : acc_(acc)
94  
            , peer_(peer)
94  
            , peer_(peer)
95  
        {
95  
        {
96  
        }
96  
        }
97  

97  

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

102  

103  
        capy::io_result<> await_resume() const noexcept
103  
        capy::io_result<> await_resume() const noexcept
104  
        {
104  
        {
105  
            if (token_.stop_requested())
105  
            if (token_.stop_requested())
106  
                return {make_error_code(std::errc::operation_canceled)};
106  
                return {make_error_code(std::errc::operation_canceled)};
107  

107  

108  
            if (!ec_ && peer_impl_)
108  
            if (!ec_ && peer_impl_)
109  
                peer_.h_.reset(peer_impl_);
109  
                peer_.h_.reset(peer_impl_);
110  
            return {ec_};
110  
            return {ec_};
111  
        }
111  
        }
112  

112  

113  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
113  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
114  
            -> std::coroutine_handle<>
114  
            -> std::coroutine_handle<>
115  
        {
115  
        {
116  
            token_ = env->stop_token;
116  
            token_ = env->stop_token;
117  
            return acc_.get().accept(
117  
            return acc_.get().accept(
118  
                h, env->executor, token_, &ec_, &peer_impl_);
118  
                h, env->executor, token_, &ec_, &peer_impl_);
119  
        }
119  
        }
120  
    };
120  
    };
121  

121  

122  
public:
122  
public:
123  
    /** Destructor.
123  
    /** Destructor.
124  

124  

125  
        Closes the acceptor if open, cancelling any pending operations.
125  
        Closes the acceptor if open, cancelling any pending operations.
126  
    */
126  
    */
127  
    ~tcp_acceptor() override;
127  
    ~tcp_acceptor() override;
128  

128  

129  
    /** Construct an acceptor from an execution context.
129  
    /** Construct an acceptor from an execution context.
130  

130  

131  
        @param ctx The execution context that will own this acceptor.
131  
        @param ctx The execution context that will own this acceptor.
132  
    */
132  
    */
133  
    explicit tcp_acceptor(capy::execution_context& ctx);
133  
    explicit tcp_acceptor(capy::execution_context& ctx);
134  

134  

135  
    /** Convenience constructor: open + SO_REUSEADDR + bind + listen.
135  
    /** Convenience constructor: open + SO_REUSEADDR + bind + listen.
136  

136  

137  
        Creates a fully-bound listening acceptor in a single
137  
        Creates a fully-bound listening acceptor in a single
138  
        expression. The address family is deduced from @p ep.
138  
        expression. The address family is deduced from @p ep.
139  

139  

140  
        @param ctx The execution context that will own this acceptor.
140  
        @param ctx The execution context that will own this acceptor.
141  
        @param ep The local endpoint to bind to.
141  
        @param ep The local endpoint to bind to.
142  
        @param backlog The maximum pending connection queue length.
142  
        @param backlog The maximum pending connection queue length.
143  

143  

144  
        @throws std::system_error on bind or listen failure.
144  
        @throws std::system_error on bind or listen failure.
145  
    */
145  
    */
146  
    tcp_acceptor(
146  
    tcp_acceptor(
147  
        capy::execution_context& ctx, endpoint ep, int backlog = 128 );
147  
        capy::execution_context& ctx, endpoint ep, int backlog = 128 );
148  

148  

149  
    /** Construct an acceptor from an executor.
149  
    /** Construct an acceptor from an executor.
150  

150  

151  
        The acceptor is associated with the executor's context.
151  
        The acceptor is associated with the executor's context.
152  

152  

153  
        @param ex The executor whose context will own the acceptor.
153  
        @param ex The executor whose context will own the acceptor.
154  
    */
154  
    */
155  
    template<class Ex>
155  
    template<class Ex>
156  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
156  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
157  
        capy::Executor<Ex>
157  
        capy::Executor<Ex>
158  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
158  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
159  
    {
159  
    {
160  
    }
160  
    }
161  

161  

162  
    /** Convenience constructor from an executor.
162  
    /** Convenience constructor from an executor.
163  

163  

164  
        @param ex The executor whose context will own the acceptor.
164  
        @param ex The executor whose context will own the acceptor.
165  
        @param ep The local endpoint to bind to.
165  
        @param ep The local endpoint to bind to.
166  
        @param backlog The maximum pending connection queue length.
166  
        @param backlog The maximum pending connection queue length.
167  

167  

168  
        @throws std::system_error on bind or listen failure.
168  
        @throws std::system_error on bind or listen failure.
169  
    */
169  
    */
170  
    template<class Ex>
170  
    template<class Ex>
171  
        requires capy::Executor<Ex>
171  
        requires capy::Executor<Ex>
172  
    tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128 )
172  
    tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128 )
173  
        : tcp_acceptor(ex.context(), ep, backlog)
173  
        : tcp_acceptor(ex.context(), ep, backlog)
174  
    {
174  
    {
175  
    }
175  
    }
176  

176  

177  
    /** Move constructor.
177  
    /** Move constructor.
178  

178  

179  
        Transfers ownership of the acceptor resources.
179  
        Transfers ownership of the acceptor resources.
180  

180  

181  
        @param other The acceptor to move from.
181  
        @param other The acceptor to move from.
182  

182  

183  
        @pre No awaitables returned by @p other's methods exist.
183  
        @pre No awaitables returned by @p other's methods exist.
184  
        @pre The execution context associated with @p other must
184  
        @pre The execution context associated with @p other must
185  
            outlive this acceptor.
185  
            outlive this acceptor.
186  
    */
186  
    */
187  
    tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
187  
    tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
188  

188  

189  
    /** Move assignment operator.
189  
    /** Move assignment operator.
190  

190  

191  
        Closes any existing acceptor and transfers ownership.
191  
        Closes any existing acceptor and transfers ownership.
192  

192  

193  
        @param other The acceptor to move from.
193  
        @param other The acceptor to move from.
194  

194  

195  
        @pre No awaitables returned by either `*this` or @p other's
195  
        @pre No awaitables returned by either `*this` or @p other's
196  
            methods exist.
196  
            methods exist.
197  
        @pre The execution context associated with @p other must
197  
        @pre The execution context associated with @p other must
198  
            outlive this acceptor.
198  
            outlive this acceptor.
199  

199  

200  
        @return Reference to this acceptor.
200  
        @return Reference to this acceptor.
201  
    */
201  
    */
202  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
202  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
203  
    {
203  
    {
204  
        if (this != &other)
204  
        if (this != &other)
205  
        {
205  
        {
206  
            close();
206  
            close();
207  
            h_ = std::move(other.h_);
207  
            h_ = std::move(other.h_);
208  
        }
208  
        }
209  
        return *this;
209  
        return *this;
210  
    }
210  
    }
211  

211  

212  
    tcp_acceptor(tcp_acceptor const&)            = delete;
212  
    tcp_acceptor(tcp_acceptor const&)            = delete;
213  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
213  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
214  

214  

215  
    /** Create the acceptor socket without binding or listening.
215  
    /** Create the acceptor socket without binding or listening.
216  

216  

217  
        Creates a TCP socket with dual-stack enabled for IPv6.
217  
        Creates a TCP socket with dual-stack enabled for IPv6.
218  
        Does not set SO_REUSEADDR — call `set_option` explicitly
218  
        Does not set SO_REUSEADDR — call `set_option` explicitly
219  
        if needed.
219  
        if needed.
220  

220  

221  
        If the acceptor is already open, this function is a no-op.
221  
        If the acceptor is already open, this function is a no-op.
222  

222  

223  
        @param proto The protocol (IPv4 or IPv6). Defaults to
223  
        @param proto The protocol (IPv4 or IPv6). Defaults to
224  
            `tcp::v4()`.
224  
            `tcp::v4()`.
225  

225  

226  
        @throws std::system_error on failure.
226  
        @throws std::system_error on failure.
227  

227  

228  
        @par Example
228  
        @par Example
229  
        @code
229  
        @code
230  
        acc.open( tcp::v6() );
230  
        acc.open( tcp::v6() );
231  
        acc.set_option( socket_option::reuse_address( true ) );
231  
        acc.set_option( socket_option::reuse_address( true ) );
232  
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
232  
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
233  
        acc.listen();
233  
        acc.listen();
234  
        @endcode
234  
        @endcode
235  

235  

236  
        @see bind, listen
236  
        @see bind, listen
237  
    */
237  
    */
238  
    void open( tcp proto = tcp::v4() );
238  
    void open( tcp proto = tcp::v4() );
239  

239  

240  
    /** Bind to a local endpoint.
240  
    /** Bind to a local endpoint.
241  

241  

242  
        The acceptor must be open. Binds the socket to @p ep and
242  
        The acceptor must be open. Binds the socket to @p ep and
243  
        caches the resolved local endpoint (useful when port 0 is
243  
        caches the resolved local endpoint (useful when port 0 is
244  
        used to request an ephemeral port).
244  
        used to request an ephemeral port).
245  

245  

246  
        @param ep The local endpoint to bind to.
246  
        @param ep The local endpoint to bind to.
247  

247  

248  
        @return An error code indicating success or the reason for
248  
        @return An error code indicating success or the reason for
249  
            failure.
249  
            failure.
250  

250  

251  
        @par Error Conditions
251  
        @par Error Conditions
252  
        @li `errc::address_in_use`: The endpoint is already in use.
252  
        @li `errc::address_in_use`: The endpoint is already in use.
253  
        @li `errc::address_not_available`: The address is not available
253  
        @li `errc::address_not_available`: The address is not available
254  
            on any local interface.
254  
            on any local interface.
255  
        @li `errc::permission_denied`: Insufficient privileges to bind
255  
        @li `errc::permission_denied`: Insufficient privileges to bind
256  
            to the endpoint (e.g., privileged port).
256  
            to the endpoint (e.g., privileged port).
257  

257  

258  
        @throws std::logic_error if the acceptor is not open.
258  
        @throws std::logic_error if the acceptor is not open.
259  
    */
259  
    */
260  
    [[nodiscard]] std::error_code bind( endpoint ep );
260  
    [[nodiscard]] std::error_code bind( endpoint ep );
261  

261  

262  
    /** Start listening for incoming connections.
262  
    /** Start listening for incoming connections.
263  

263  

264  
        The acceptor must be open and bound. Registers the acceptor
264  
        The acceptor must be open and bound. Registers the acceptor
265  
        with the platform reactor.
265  
        with the platform reactor.
266  

266  

267  
        @param backlog The maximum length of the queue of pending
267  
        @param backlog The maximum length of the queue of pending
268  
            connections. Defaults to 128.
268  
            connections. Defaults to 128.
269  

269  

270  
        @return An error code indicating success or the reason for
270  
        @return An error code indicating success or the reason for
271  
            failure.
271  
            failure.
272  

272  

273  
        @throws std::logic_error if the acceptor is not open.
273  
        @throws std::logic_error if the acceptor is not open.
274  
    */
274  
    */
275  
    [[nodiscard]] std::error_code listen( int backlog = 128 );
275  
    [[nodiscard]] std::error_code listen( int backlog = 128 );
276  

276  

277  
    /** Close the acceptor.
277  
    /** Close the acceptor.
278  

278  

279  
        Releases acceptor resources. Any pending operations complete
279  
        Releases acceptor resources. Any pending operations complete
280  
        with `errc::operation_canceled`.
280  
        with `errc::operation_canceled`.
281  
    */
281  
    */
282  
    void close();
282  
    void close();
283  

283  

284  
    /** Check if the acceptor is listening.
284  
    /** Check if the acceptor is listening.
285  

285  

286  
        @return `true` if the acceptor is open and listening.
286  
        @return `true` if the acceptor is open and listening.
287  
    */
287  
    */
288  
    bool is_open() const noexcept
288  
    bool is_open() const noexcept
289  
    {
289  
    {
290  
        return h_ && get().is_open();
290  
        return h_ && get().is_open();
291  
    }
291  
    }
292  

292  

293  
    /** Initiate an asynchronous accept operation.
293  
    /** Initiate an asynchronous accept operation.
294  

294  

295  
        Accepts an incoming connection and initializes the provided
295  
        Accepts an incoming connection and initializes the provided
296  
        socket with the new connection. The acceptor must be listening
296  
        socket with the new connection. The acceptor must be listening
297  
        before calling this function.
297  
        before calling this function.
298  

298  

299  
        The operation supports cancellation via `std::stop_token` through
299  
        The operation supports cancellation via `std::stop_token` through
300  
        the affine awaitable protocol. If the associated stop token is
300  
        the affine awaitable protocol. If the associated stop token is
301  
        triggered, the operation completes immediately with
301  
        triggered, the operation completes immediately with
302  
        `errc::operation_canceled`.
302  
        `errc::operation_canceled`.
303  

303  

304  
        @param peer The socket to receive the accepted connection. Any
304  
        @param peer The socket to receive the accepted connection. Any
305  
            existing connection on this socket will be closed.
305  
            existing connection on this socket will be closed.
306  

306  

307  
        @return An awaitable that completes with `io_result<>`.
307  
        @return An awaitable that completes with `io_result<>`.
308  
            Returns success on successful accept, or an error code on
308  
            Returns success on successful accept, or an error code on
309  
            failure including:
309  
            failure including:
310  
            - operation_canceled: Cancelled via stop_token or cancel().
310  
            - operation_canceled: Cancelled via stop_token or cancel().
311  
                Check `ec == cond::canceled` for portable comparison.
311  
                Check `ec == cond::canceled` for portable comparison.
312  

312  

313  
        @par Preconditions
313  
        @par Preconditions
314  
        The acceptor must be listening (`is_open() == true`).
314  
        The acceptor must be listening (`is_open() == true`).
315  
        The peer socket must be associated with the same execution context.
315  
        The peer socket must be associated with the same execution context.
316  

316  

317  
        Both this acceptor and @p peer must outlive the returned
317  
        Both this acceptor and @p peer must outlive the returned
318  
        awaitable.
318  
        awaitable.
319  

319  

320  
        @par Example
320  
        @par Example
321  
        @code
321  
        @code
322  
        tcp_socket peer(ioc);
322  
        tcp_socket peer(ioc);
323  
        auto [ec] = co_await acc.accept(peer);
323  
        auto [ec] = co_await acc.accept(peer);
324  
        if (!ec) {
324  
        if (!ec) {
325  
            // Use peer socket
325  
            // Use peer socket
326  
        }
326  
        }
327  
        @endcode
327  
        @endcode
328  
    */
328  
    */
329  
    auto accept(tcp_socket& peer)
329  
    auto accept(tcp_socket& peer)
330  
    {
330  
    {
331  
        if (!is_open())
331  
        if (!is_open())
332  
            detail::throw_logic_error("accept: acceptor not listening");
332  
            detail::throw_logic_error("accept: acceptor not listening");
333  
        return accept_awaitable(*this, peer);
333  
        return accept_awaitable(*this, peer);
334  
    }
334  
    }
335  

335  

336  
    /** Cancel any pending asynchronous operations.
336  
    /** Cancel any pending asynchronous operations.
337  

337  

338  
        All outstanding operations complete with `errc::operation_canceled`.
338  
        All outstanding operations complete with `errc::operation_canceled`.
339  
        Check `ec == cond::canceled` for portable comparison.
339  
        Check `ec == cond::canceled` for portable comparison.
340  
    */
340  
    */
341  
    void cancel();
341  
    void cancel();
342  

342  

343  
    /** Get the local endpoint of the acceptor.
343  
    /** Get the local endpoint of the acceptor.
344  

344  

345  
        Returns the local address and port to which the acceptor is bound.
345  
        Returns the local address and port to which the acceptor is bound.
346  
        This is useful when binding to port 0 (ephemeral port) to discover
346  
        This is useful when binding to port 0 (ephemeral port) to discover
347  
        the OS-assigned port number. The endpoint is cached when listen()
347  
        the OS-assigned port number. The endpoint is cached when listen()
348  
        is called.
348  
        is called.
349  

349  

350  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
350  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
351  
            the acceptor is not listening.
351  
            the acceptor is not listening.
352  

352  

353  
        @par Thread Safety
353  
        @par Thread Safety
354  
        The cached endpoint value is set during listen() and cleared
354  
        The cached endpoint value is set during listen() and cleared
355  
        during close(). This function may be called concurrently with
355  
        during close(). This function may be called concurrently with
356  
        accept operations, but must not be called concurrently with
356  
        accept operations, but must not be called concurrently with
357  
        listen() or close().
357  
        listen() or close().
358  
    */
358  
    */
359  
    endpoint local_endpoint() const noexcept;
359  
    endpoint local_endpoint() const noexcept;
360  

360  

361  
    /** Set a socket option on the acceptor.
361  
    /** Set a socket option on the acceptor.
362  

362  

363  
        Applies a type-safe socket option to the underlying listening
363  
        Applies a type-safe socket option to the underlying listening
364  
        socket. The socket must be open (via `open()` or `listen()`).
364  
        socket. The socket must be open (via `open()` or `listen()`).
365  
        This is useful for setting options between `open()` and
365  
        This is useful for setting options between `open()` and
366  
        `listen()`, such as `socket_option::reuse_port`.
366  
        `listen()`, such as `socket_option::reuse_port`.
367  

367  

368  
        @par Example
368  
        @par Example
369  
        @code
369  
        @code
370  
        acc.open( tcp::v6() );
370  
        acc.open( tcp::v6() );
371  
        acc.set_option( socket_option::reuse_port( true ) );
371  
        acc.set_option( socket_option::reuse_port( true ) );
372  
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
372  
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
373  
        acc.listen();
373  
        acc.listen();
374  
        @endcode
374  
        @endcode
375  

375  

376  
        @param opt The option to set.
376  
        @param opt The option to set.
377  

377  

378  
        @throws std::logic_error if the acceptor is not open.
378  
        @throws std::logic_error if the acceptor is not open.
379  
        @throws std::system_error on failure.
379  
        @throws std::system_error on failure.
380  
    */
380  
    */
381  
    template<class Option>
381  
    template<class Option>
382  
    void set_option( Option const& opt )
382  
    void set_option( Option const& opt )
383  
    {
383  
    {
384  
        if (!is_open())
384  
        if (!is_open())
385  
            detail::throw_logic_error(
385  
            detail::throw_logic_error(
386  
                "set_option: acceptor not open" );
386  
                "set_option: acceptor not open" );
387  
        std::error_code ec = get().set_option(
387  
        std::error_code ec = get().set_option(
388  
            Option::level(), Option::name(), opt.data(), opt.size() );
388  
            Option::level(), Option::name(), opt.data(), opt.size() );
389  
        if (ec)
389  
        if (ec)
390  
            detail::throw_system_error(
390  
            detail::throw_system_error(
391  
                ec, "tcp_acceptor::set_option" );
391  
                ec, "tcp_acceptor::set_option" );
392  
    }
392  
    }
393  

393  

394  
    /** Get a socket option from the acceptor.
394  
    /** Get a socket option from the acceptor.
395  

395  

396  
        Retrieves the current value of a type-safe socket option.
396  
        Retrieves the current value of a type-safe socket option.
397  

397  

398  
        @par Example
398  
        @par Example
399  
        @code
399  
        @code
400  
        auto opt = acc.get_option<socket_option::reuse_address>();
400  
        auto opt = acc.get_option<socket_option::reuse_address>();
401  
        @endcode
401  
        @endcode
402  

402  

403  
        @return The current option value.
403  
        @return The current option value.
404  

404  

405  
        @throws std::logic_error if the acceptor is not open.
405  
        @throws std::logic_error if the acceptor is not open.
406  
        @throws std::system_error on failure.
406  
        @throws std::system_error on failure.
407  
    */
407  
    */
408  
    template<class Option>
408  
    template<class Option>
409  
    Option get_option() const
409  
    Option get_option() const
410  
    {
410  
    {
411  
        if (!is_open())
411  
        if (!is_open())
412  
            detail::throw_logic_error(
412  
            detail::throw_logic_error(
413  
                "get_option: acceptor not open" );
413  
                "get_option: acceptor not open" );
414  
        Option opt{};
414  
        Option opt{};
415  
        std::size_t sz = opt.size();
415  
        std::size_t sz = opt.size();
416  
        std::error_code ec = get().get_option(
416  
        std::error_code ec = get().get_option(
417  
            Option::level(), Option::name(), opt.data(), &sz );
417  
            Option::level(), Option::name(), opt.data(), &sz );
418  
        if (ec)
418  
        if (ec)
419  
            detail::throw_system_error(
419  
            detail::throw_system_error(
420  
                ec, "tcp_acceptor::get_option" );
420  
                ec, "tcp_acceptor::get_option" );
421  
        opt.resize( sz );
421  
        opt.resize( sz );
422  
        return opt;
422  
        return opt;
423  
    }
423  
    }
424  

424  

425  
    struct implementation : io_object::implementation
425  
    struct implementation : io_object::implementation
426  
    {
426  
    {
427  
        virtual std::coroutine_handle<> accept(
427  
        virtual std::coroutine_handle<> accept(
428  
            std::coroutine_handle<>,
428  
            std::coroutine_handle<>,
429  
            capy::executor_ref,
429  
            capy::executor_ref,
430  
            std::stop_token,
430  
            std::stop_token,
431  
            std::error_code*,
431  
            std::error_code*,
432  
            io_object::implementation**) = 0;
432  
            io_object::implementation**) = 0;
433  

433  

434  
        /// Returns the cached local endpoint.
434  
        /// Returns the cached local endpoint.
435  
        virtual endpoint local_endpoint() const noexcept = 0;
435  
        virtual endpoint local_endpoint() const noexcept = 0;
436  

436  

437  
        /// Return true if the acceptor has a kernel resource open.
437  
        /// Return true if the acceptor has a kernel resource open.
438  
        virtual bool is_open() const noexcept = 0;
438  
        virtual bool is_open() const noexcept = 0;
439  

439  

440  
        /** Cancel any pending asynchronous operations.
440  
        /** Cancel any pending asynchronous operations.
441  

441  

442  
            All outstanding operations complete with operation_canceled error.
442  
            All outstanding operations complete with operation_canceled error.
443  
        */
443  
        */
444  
        virtual void cancel() noexcept = 0;
444  
        virtual void cancel() noexcept = 0;
445  

445  

446  
        /** Set a socket option.
446  
        /** Set a socket option.
447  

447  

448  
            @param level The protocol level.
448  
            @param level The protocol level.
449  
            @param optname The option name.
449  
            @param optname The option name.
450  
            @param data Pointer to the option value.
450  
            @param data Pointer to the option value.
451  
            @param size Size of the option value in bytes.
451  
            @param size Size of the option value in bytes.
452  
            @return Error code on failure, empty on success.
452  
            @return Error code on failure, empty on success.
453  
        */
453  
        */
454  
        virtual std::error_code set_option(
454  
        virtual std::error_code set_option(
455  
            int level, int optname,
455  
            int level, int optname,
456  
            void const* data, std::size_t size) noexcept = 0;
456  
            void const* data, std::size_t size) noexcept = 0;
457  

457  

458  
        /** Get a socket option.
458  
        /** Get a socket option.
459  

459  

460  
            @param level The protocol level.
460  
            @param level The protocol level.
461  
            @param optname The option name.
461  
            @param optname The option name.
462  
            @param data Pointer to receive the option value.
462  
            @param data Pointer to receive the option value.
463  
            @param size On entry, the size of the buffer. On exit,
463  
            @param size On entry, the size of the buffer. On exit,
464  
                the size of the option value.
464  
                the size of the option value.
465  
            @return Error code on failure, empty on success.
465  
            @return Error code on failure, empty on success.
466  
        */
466  
        */
467  
        virtual std::error_code get_option(
467  
        virtual std::error_code get_option(
468  
            int level, int optname,
468  
            int level, int optname,
469  
            void* data, std::size_t* size) const noexcept = 0;
469  
            void* data, std::size_t* size) const noexcept = 0;
470  
    };
470  
    };
471  

471  

472  
protected:
472  
protected:
473  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
473  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
474  

474  

475  
    /// Transfer accepted peer impl to the peer socket.
475  
    /// Transfer accepted peer impl to the peer socket.
476  
    static void
476  
    static void
477  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
477  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
478  
    {
478  
    {
479  
        if (impl)
479  
        if (impl)
480  
            peer.h_.reset(impl);
480  
            peer.h_.reset(impl);
481  
    }
481  
    }
482  

482  

483  
private:
483  
private:
484  
    inline implementation& get() const noexcept
484  
    inline implementation& get() const noexcept
485  
    {
485  
    {
486  
        return *static_cast<implementation*>(h_.get());
486  
        return *static_cast<implementation*>(h_.get());
487  
    }
487  
    }
488  
};
488  
};
489  

489  

490  
} // namespace boost::corosio
490  
} // namespace boost::corosio
491  

491  

492  
#endif
492  
#endif