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 <memory>
 
31  
#include <cstddef>
30  
#include <cstddef>
32  
#include <stop_token>
31  
#include <stop_token>
33  
#include <type_traits>
32  
#include <type_traits>
34  

33  

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

35  

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

37  

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

41  

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

44  

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

49  

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

53  

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

59  

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

67  

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

90  

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

96  

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

101  

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

106  

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

111  

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

120  

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

123  

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

127  

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

129  

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

133  

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

135  

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

138  

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

142  

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

146  

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

148  

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

150  

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

159  

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

161  

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

165  

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

174  

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

176  

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

178  

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

180  

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

186  

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

188  

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

190  

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

192  

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

197  

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

209  

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

212  

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

214  

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

218  

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

220  

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

223  

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

225  

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

233  

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

237  

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

239  

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

243  

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

245  

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

248  

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

255  

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

259  

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

261  

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

264  

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

267  

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

270  

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

274  

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

276  

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

281  

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

283  

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

290  

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

292  

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

296  

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

301  

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

304  

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

310  

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

314  

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

317  

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

333  

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

335  

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

340  

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

342  

345  
        Returns the local address and port to which the acceptor is bound.
343  
        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
344  
        This is useful when binding to port 0 (ephemeral port) to discover
347  
        the OS-assigned port number. The endpoint is cached when listen()
345  
        the OS-assigned port number. The endpoint is cached when listen()
348  
        is called.
346  
        is called.
349  

347  

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

350  

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

358  

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

360  

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

365  

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

373  

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

375  

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

389  

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

391  

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

393  

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

398  

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

400  

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

418  

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

427  

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

430  

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

433  

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

435  

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

439  

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

441  

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

453  

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

455  

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

467  

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

470  

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

478  

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

485  

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

487  

492  
#endif
488  
#endif