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/tcp.hpp>
 
19  
#include <boost/corosio/endpoint.hpp>
18  
#include <boost/corosio/endpoint.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
19  
#include <boost/corosio/tcp_socket.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
20  
#include <boost/capy/ex/executor_ref.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
24  
#include <boost/capy/concept/executor.hpp>
23  
#include <boost/capy/concept/executor.hpp>
25  

24  

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

26  

28  
#include <concepts>
27  
#include <concepts>
29  
#include <coroutine>
28  
#include <coroutine>
30  
#include <cstddef>
29  
#include <cstddef>
31  
#include <memory>
30  
#include <memory>
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 -
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
 
57  
    @code
55  
    @code
58  
    io_context ioc;
56  
    io_context ioc;
59 -
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
57 +
    tcp_acceptor acc(ioc);
 
58 +
    if (auto ec = acc.listen(endpoint(8080)))  // Bind to port 8080
 
59 +
        return ec;
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 -

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

78  

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

84  

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

89  

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

94  

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

99  

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

108  

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

111  

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

115  

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

117  

131  
        @param ctx The execution context that will own this acceptor.
118  
        @param ctx The execution context that will own this acceptor.
132  
    */
119  
    */
133  
    explicit tcp_acceptor(capy::execution_context& ctx);
120  
    explicit tcp_acceptor(capy::execution_context& ctx);
134 -
    /** Convenience constructor: open + SO_REUSEADDR + bind + listen.
 
135 -

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

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

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

 
148  

121  

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

123  

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

125  

153  
        @param ex The executor whose context will own the acceptor.
126  
        @param ex The executor whose context will own the acceptor.
154  
    */
127  
    */
155  
    template<class Ex>
128  
    template<class Ex>
156  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
129  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
157  
        capy::Executor<Ex>
130  
        capy::Executor<Ex>
158  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
131  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
159  
    {
132  
    {
160  
    }
133  
    }
161 -
    /** Convenience constructor from an executor.
 
162 -

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

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

 
176  

134  

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

136  

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

138  

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

140  

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

146  

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

148  

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

150  

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

152  

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

157  

200  
        @return Reference to this acceptor.
158  
        @return Reference to this acceptor.
201  
    */
159  
    */
202  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
160  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
203  
    {
161  
    {
204  
        if (this != &other)
162  
        if (this != &other)
205  
        {
163  
        {
206  
            close();
164  
            close();
207  
            h_ = std::move(other.h_);
165  
            h_ = std::move(other.h_);
208  
        }
166  
        }
209  
        return *this;
167  
        return *this;
210  
    }
168  
    }
211  

169  

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

172  

215 -
    /** Create the acceptor socket without binding or listening.
173 +
    /** Open, bind, and listen on an endpoint.
216 -

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

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

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

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

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

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

174  

240 -
    /** Bind to a local endpoint.
175 +
        Creates an IPv4 TCP socket, binds it to the specified endpoint,
 
176 +
        and begins listening for incoming connections. This must be
 
177 +
        called before initiating accept operations.
241  

178  

242 -
        The acceptor must be open. Binds the socket to @p ep and
179 +
        @param ep The local endpoint to bind to. Use `endpoint(port)` to
243 -
        caches the resolved local endpoint (useful when port 0 is
180 +
            bind to all interfaces on a specific port.
244 -
        used to request an ephemeral port).
 
245  

181  

246 -
        @param ep The local endpoint to bind to.
182 +
        @param backlog The maximum length of the queue of pending
 
183 +
            connections. Defaults to 128.
247  

184  

248 -
        @return An error code indicating success or the reason for
185 +
        @return An error code indicating success or the reason for failure.
249 -
            failure.
186 +
            A default-constructed error code indicates success.
250  

187  

251  
        @par Error Conditions
188  
        @par Error Conditions
252  
        @li `errc::address_in_use`: The endpoint is already in use.
189  
        @li `errc::address_in_use`: The endpoint is already in use.
253  
        @li `errc::address_not_available`: The address is not available
190  
        @li `errc::address_not_available`: The address is not available
254  
            on any local interface.
191  
            on any local interface.
255  
        @li `errc::permission_denied`: Insufficient privileges to bind
192  
        @li `errc::permission_denied`: Insufficient privileges to bind
256  
            to the endpoint (e.g., privileged port).
193  
            to the endpoint (e.g., privileged port).
 
194 +
        @li `errc::operation_not_supported`: The acceptor service is
 
195 +
            unavailable in the context (POSIX only).
257  

196  

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

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

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

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

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

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

200  

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

202  

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

207  

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

209  

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

216  

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

218  

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

222  

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

227  

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

230  

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

236  

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

240  

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

243  

320  
        @par Example
244  
        @par Example
321  
        @code
245  
        @code
322  
        tcp_socket peer(ioc);
246  
        tcp_socket peer(ioc);
323  
        auto [ec] = co_await acc.accept(peer);
247  
        auto [ec] = co_await acc.accept(peer);
324  
        if (!ec) {
248  
        if (!ec) {
325  
            // Use peer socket
249  
            // Use peer socket
326  
        }
250  
        }
327  
        @endcode
251  
        @endcode
328  
    */
252  
    */
329  
    auto accept(tcp_socket& peer)
253  
    auto accept(tcp_socket& peer)
330  
    {
254  
    {
331  
        if (!is_open())
255  
        if (!is_open())
332  
            detail::throw_logic_error("accept: acceptor not listening");
256  
            detail::throw_logic_error("accept: acceptor not listening");
333  
        return accept_awaitable(*this, peer);
257  
        return accept_awaitable(*this, peer);
334  
    }
258  
    }
335  

259  

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

261  

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

266  

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

268  

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

273  

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

276  

353  
        @par Thread Safety
277  
        @par Thread Safety
354  
        The cached endpoint value is set during listen() and cleared
278  
        The cached endpoint value is set during listen() and cleared
355  
        during close(). This function may be called concurrently with
279  
        during close(). This function may be called concurrently with
356  
        accept operations, but must not be called concurrently with
280  
        accept operations, but must not be called concurrently with
357  
        listen() or close().
281  
        listen() or close().
358  
    */
282  
    */
359  
    endpoint local_endpoint() const noexcept;
283  
    endpoint local_endpoint() const noexcept;
360 -
    /** Set a socket option on the acceptor.
 
361 -

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

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

 
375 -
        @param opt The option to set.
 
376 -

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

 
393 -
    /** Get a socket option from the acceptor.
 
394 -

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

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

 
402 -
        @return The current option value.
 
403 -

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

 
424  

284  

425  
    struct implementation : io_object::implementation
285  
    struct implementation : io_object::implementation
426  
    {
286  
    {
427  
        virtual std::coroutine_handle<> accept(
287  
        virtual std::coroutine_handle<> accept(
428  
            std::coroutine_handle<>,
288  
            std::coroutine_handle<>,
429  
            capy::executor_ref,
289  
            capy::executor_ref,
430  
            std::stop_token,
290  
            std::stop_token,
431  
            std::error_code*,
291  
            std::error_code*,
432  
            io_object::implementation**) = 0;
292  
            io_object::implementation**) = 0;
433  

293  

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

296  

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

299  

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

301  

442  
            All outstanding operations complete with operation_canceled error.
302  
            All outstanding operations complete with operation_canceled error.
443  
        */
303  
        */
444 -

 
445 -
        /** Set a socket option.
 
446 -

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

 
457 -
        /** Get a socket option.
 
458 -

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

306  

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

309  

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

317  

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

324  

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

326  

492  
#endif
327  
#endif