1  
//
1  
//
2  
// Copyright (c) 2026 Michael Vandeberg
2  
// Copyright (c) 2026 Michael Vandeberg
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
10  
#ifndef BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
11  
#define BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
11  
#define BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
12  

12  

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

24  

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

26  

27  
#include <cassert>
27  
#include <cassert>
28  
#include <concepts>
28  
#include <concepts>
29  
#include <coroutine>
29  
#include <coroutine>
30  
#include <cstddef>
30  
#include <cstddef>
31  
#include <stop_token>
31  
#include <stop_token>
32  
#include <type_traits>
32  
#include <type_traits>
33  

33  

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

35  

36  
/** Options for @ref local_stream_acceptor::bind().
36  
/** Options for @ref local_stream_acceptor::bind().
37  

37  

38  
    Controls filesystem cleanup behavior before binding
38  
    Controls filesystem cleanup behavior before binding
39  
    to a Unix domain socket path.
39  
    to a Unix domain socket path.
40  
*/
40  
*/
41  
enum class bind_option
41  
enum class bind_option
42  
{
42  
{
43  
    none,
43  
    none,
44  
    /// Unlink the socket path before binding (ignored for abstract paths).
44  
    /// Unlink the socket path before binding (ignored for abstract paths).
45  
    unlink_existing
45  
    unlink_existing
46  
};
46  
};
47  

47  

48  
/** An asynchronous Unix domain stream acceptor for coroutine I/O.
48  
/** An asynchronous Unix domain stream acceptor for coroutine I/O.
49  

49  

50  
    This class provides asynchronous Unix domain stream accept
50  
    This class provides asynchronous Unix domain stream accept
51  
    operations that return awaitable types. The acceptor binds
51  
    operations that return awaitable types. The acceptor binds
52  
    to a local endpoint (filesystem path or abstract name) and
52  
    to a local endpoint (filesystem path or abstract name) and
53  
    listens for incoming connections.
53  
    listens for incoming connections.
54  

54  

55  
    The library does NOT automatically unlink the socket path
55  
    The library does NOT automatically unlink the socket path
56  
    on close. Callers are responsible for removing the socket
56  
    on close. Callers are responsible for removing the socket
57  
    file before bind (via @ref bind_option::unlink_existing) or
57  
    file before bind (via @ref bind_option::unlink_existing) or
58  
    after close.
58  
    after close.
59  

59  

60  
    @par Thread Safety
60  
    @par Thread Safety
61  
    Distinct objects: Safe.@n
61  
    Distinct objects: Safe.@n
62  
    Shared objects: Unsafe. An acceptor must not have concurrent
62  
    Shared objects: Unsafe. An acceptor must not have concurrent
63  
    accept operations.
63  
    accept operations.
64  

64  

65  
    @par Example
65  
    @par Example
66  
    @code
66  
    @code
67  
    io_context ioc;
67  
    io_context ioc;
68  
    local_stream_acceptor acc(ioc);
68  
    local_stream_acceptor acc(ioc);
69  
    acc.open();
69  
    acc.open();
70  
    acc.bind(local_endpoint("/tmp/my.sock"),
70  
    acc.bind(local_endpoint("/tmp/my.sock"),
71  
             bind_option::unlink_existing);
71  
             bind_option::unlink_existing);
72  
    acc.listen();
72  
    acc.listen();
73  
    auto [ec, peer] = co_await acc.accept();
73  
    auto [ec, peer] = co_await acc.accept();
74  
    @endcode
74  
    @endcode
75  
*/
75  
*/
76  
class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
76  
class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
77  
{
77  
{
78  
    struct move_accept_awaitable
78  
    struct move_accept_awaitable
79  
    {
79  
    {
80  
        local_stream_acceptor& acc_;
80  
        local_stream_acceptor& acc_;
81  
        std::stop_token token_;
81  
        std::stop_token token_;
82  
        mutable std::error_code ec_;
82  
        mutable std::error_code ec_;
83  
        mutable io_object::implementation* peer_impl_ = nullptr;
83  
        mutable io_object::implementation* peer_impl_ = nullptr;
84  

84  

85  
        explicit move_accept_awaitable(
85  
        explicit move_accept_awaitable(
86  
            local_stream_acceptor& acc) noexcept
86  
            local_stream_acceptor& acc) noexcept
87  
            : acc_(acc)
87  
            : acc_(acc)
88  
        {
88  
        {
89  
        }
89  
        }
90  

90  

91  
        bool await_ready() const noexcept
91  
        bool await_ready() const noexcept
92  
        {
92  
        {
93  
            return token_.stop_requested();
93  
            return token_.stop_requested();
94  
        }
94  
        }
95  

95  

96  
        capy::io_result<local_stream_socket> await_resume() const noexcept
96  
        capy::io_result<local_stream_socket> await_resume() const noexcept
97  
        {
97  
        {
98  
            if (token_.stop_requested())
98  
            if (token_.stop_requested())
99  
                return {make_error_code(std::errc::operation_canceled),
99  
                return {make_error_code(std::errc::operation_canceled),
100  
                        local_stream_socket()};
100  
                        local_stream_socket()};
101  

101  

102  
            if (ec_ || !peer_impl_)
102  
            if (ec_ || !peer_impl_)
103  
                return {ec_, local_stream_socket()};
103  
                return {ec_, local_stream_socket()};
104  

104  

105  
            local_stream_socket peer(acc_.ctx_);
105  
            local_stream_socket peer(acc_.ctx_);
106  
            reset_peer_impl(peer, peer_impl_);
106  
            reset_peer_impl(peer, peer_impl_);
107  
            return {ec_, std::move(peer)};
107  
            return {ec_, std::move(peer)};
108  
        }
108  
        }
109  

109  

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

118  

119  
    struct accept_awaitable
119  
    struct accept_awaitable
120  
    {
120  
    {
121  
        local_stream_acceptor& acc_;
121  
        local_stream_acceptor& acc_;
122  
        local_stream_socket& peer_;
122  
        local_stream_socket& peer_;
123  
        std::stop_token token_;
123  
        std::stop_token token_;
124  
        mutable std::error_code ec_;
124  
        mutable std::error_code ec_;
125  
        mutable io_object::implementation* peer_impl_ = nullptr;
125  
        mutable io_object::implementation* peer_impl_ = nullptr;
126  

126  

127  
        accept_awaitable(
127  
        accept_awaitable(
128  
            local_stream_acceptor& acc, local_stream_socket& peer) noexcept
128  
            local_stream_acceptor& acc, local_stream_socket& peer) noexcept
129  
            : acc_(acc)
129  
            : acc_(acc)
130  
            , peer_(peer)
130  
            , peer_(peer)
131  
        {
131  
        {
132  
        }
132  
        }
133  

133  

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

138  

139  
        capy::io_result<> await_resume() const noexcept
139  
        capy::io_result<> await_resume() const noexcept
140  
        {
140  
        {
141  
            if (token_.stop_requested())
141  
            if (token_.stop_requested())
142  
                return {make_error_code(std::errc::operation_canceled)};
142  
                return {make_error_code(std::errc::operation_canceled)};
143  

143  

144  
            if (!ec_ && peer_impl_)
144  
            if (!ec_ && peer_impl_)
145  
                peer_.h_.reset(peer_impl_);
145  
                peer_.h_.reset(peer_impl_);
146  
            return {ec_};
146  
            return {ec_};
147  
        }
147  
        }
148  

148  

149  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
149  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
150  
            -> std::coroutine_handle<>
150  
            -> std::coroutine_handle<>
151  
        {
151  
        {
152  
            token_ = env->stop_token;
152  
            token_ = env->stop_token;
153  
            return acc_.get().accept(
153  
            return acc_.get().accept(
154  
                h, env->executor, token_, &ec_, &peer_impl_);
154  
                h, env->executor, token_, &ec_, &peer_impl_);
155  
        }
155  
        }
156  
    };
156  
    };
157  

157  

158  
public:
158  
public:
159  
    /** Destructor.
159  
    /** Destructor.
160  

160  

161  
        Closes the acceptor if open, cancelling any pending operations.
161  
        Closes the acceptor if open, cancelling any pending operations.
162  
    */
162  
    */
163  
    ~local_stream_acceptor() override;
163  
    ~local_stream_acceptor() override;
164  

164  

165  
    /** Construct an acceptor from an execution context.
165  
    /** Construct an acceptor from an execution context.
166  

166  

167  
        @param ctx The execution context that will own this acceptor.
167  
        @param ctx The execution context that will own this acceptor.
168  
    */
168  
    */
169  
    explicit local_stream_acceptor(capy::execution_context& ctx);
169  
    explicit local_stream_acceptor(capy::execution_context& ctx);
170  

170  

171  
    /** Construct an acceptor from an executor.
171  
    /** Construct an acceptor from an executor.
172  

172  

173  
        The acceptor is associated with the executor's context.
173  
        The acceptor is associated with the executor's context.
174  

174  

175  
        @param ex The executor whose context will own the acceptor.
175  
        @param ex The executor whose context will own the acceptor.
176  

176  

177  
        @tparam Ex A type satisfying @ref capy::Executor. Must not
177  
        @tparam Ex A type satisfying @ref capy::Executor. Must not
178  
            be `local_stream_acceptor` itself (disables implicit
178  
            be `local_stream_acceptor` itself (disables implicit
179  
            conversion from move).
179  
            conversion from move).
180  
    */
180  
    */
181  
    template<class Ex>
181  
    template<class Ex>
182  
        requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
182  
        requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
183  
        capy::Executor<Ex>
183  
        capy::Executor<Ex>
184  
    explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context())
184  
    explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context())
185  
    {
185  
    {
186  
    }
186  
    }
187  

187  

188  
    /** Move constructor.
188  
    /** Move constructor.
189  

189  

190  
        Transfers ownership of the acceptor resources.
190  
        Transfers ownership of the acceptor resources.
191  

191  

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

193  

194  
        @pre No awaitables returned by @p other's methods exist.
194  
        @pre No awaitables returned by @p other's methods exist.
195  
        @pre The execution context associated with @p other must
195  
        @pre The execution context associated with @p other must
196  
            outlive this acceptor.
196  
            outlive this acceptor.
197  
    */
197  
    */
198  
    local_stream_acceptor(local_stream_acceptor&& other) noexcept
198  
    local_stream_acceptor(local_stream_acceptor&& other) noexcept
199  
        : local_stream_acceptor(other.ctx_, std::move(other))
199  
        : local_stream_acceptor(other.ctx_, std::move(other))
200  
    {
200  
    {
201  
    }
201  
    }
202  

202  

203  
    /** Move assignment operator.
203  
    /** Move assignment operator.
204  

204  

205  
        Closes any existing acceptor and transfers ownership.
205  
        Closes any existing acceptor and transfers ownership.
206  
        Both acceptors must share the same execution context.
206  
        Both acceptors must share the same execution context.
207  

207  

208  
        @param other The acceptor to move from.
208  
        @param other The acceptor to move from.
209  

209  

210  
        @return Reference to this acceptor.
210  
        @return Reference to this acceptor.
211  

211  

212  
        @pre `&ctx_ == &other.ctx_` (same execution context).
212  
        @pre `&ctx_ == &other.ctx_` (same execution context).
213  
        @pre No awaitables returned by either `*this` or @p other's
213  
        @pre No awaitables returned by either `*this` or @p other's
214  
            methods exist.
214  
            methods exist.
215  
    */
215  
    */
216  
    local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
216  
    local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
217  
    {
217  
    {
218  
        assert(&ctx_ == &other.ctx_ &&
218  
        assert(&ctx_ == &other.ctx_ &&
219  
            "move-assign requires the same execution_context");
219  
            "move-assign requires the same execution_context");
220  
        if (this != &other)
220  
        if (this != &other)
221  
        {
221  
        {
222  
            close();
222  
            close();
223  
            io_object::operator=(std::move(other));
223  
            io_object::operator=(std::move(other));
224  
        }
224  
        }
225  
        return *this;
225  
        return *this;
226  
    }
226  
    }
227  

227  

228  
    local_stream_acceptor(local_stream_acceptor const&)            = delete;
228  
    local_stream_acceptor(local_stream_acceptor const&)            = delete;
229  
    local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
229  
    local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
230  

230  

231  
    /** Create the acceptor socket.
231  
    /** Create the acceptor socket.
232  

232  

233  
        @param proto The protocol. Defaults to local_stream{}.
233  
        @param proto The protocol. Defaults to local_stream{}.
234  

234  

235  
        @throws std::system_error on failure.
235  
        @throws std::system_error on failure.
236  
    */
236  
    */
237  
    void open(local_stream proto = {});
237  
    void open(local_stream proto = {});
238  

238  

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

240  

241  
        @param ep The local endpoint (path) to bind to.
241  
        @param ep The local endpoint (path) to bind to.
242  
        @param opt Bind options. Pass bind_option::unlink_existing
242  
        @param opt Bind options. Pass bind_option::unlink_existing
243  
            to unlink the socket path before binding (ignored for
243  
            to unlink the socket path before binding (ignored for
244  
            abstract sockets and empty endpoints).
244  
            abstract sockets and empty endpoints).
245  

245  

246  
        @return An error code on failure, empty on success.
246  
        @return An error code on failure, empty on success.
247  

247  

248  
        @throws std::logic_error if the acceptor is not open.
248  
        @throws std::logic_error if the acceptor is not open.
249  
    */
249  
    */
250  
    [[nodiscard]] std::error_code
250  
    [[nodiscard]] std::error_code
251  
    bind(corosio::local_endpoint ep,
251  
    bind(corosio::local_endpoint ep,
252  
         bind_option opt = bind_option::none);
252  
         bind_option opt = bind_option::none);
253  

253  

254  
    /** Start listening for incoming connections.
254  
    /** Start listening for incoming connections.
255  

255  

256  
        @param backlog The maximum pending connection queue length.
256  
        @param backlog The maximum pending connection queue length.
257  

257  

258  
        @return An error code on failure, empty on success.
258  
        @return An error code on failure, empty on success.
259  

259  

260  
        @throws std::logic_error if the acceptor is not open.
260  
        @throws std::logic_error if the acceptor is not open.
261  
    */
261  
    */
262  
    [[nodiscard]] std::error_code listen(int backlog = 128);
262  
    [[nodiscard]] std::error_code listen(int backlog = 128);
263  

263  

264  
    /** Close the acceptor.
264  
    /** Close the acceptor.
265  

265  

266  
        Cancels any pending accept operations and releases the
266  
        Cancels any pending accept operations and releases the
267  
        underlying socket. Has no effect if the acceptor is not
267  
        underlying socket. Has no effect if the acceptor is not
268  
        open.
268  
        open.
269  

269  

270  
        @post is_open() == false
270  
        @post is_open() == false
271  
    */
271  
    */
272  
    void close();
272  
    void close();
273  

273  

274  
    /// Check if the acceptor has an open socket handle.
274  
    /// Check if the acceptor has an open socket handle.
275  
    bool is_open() const noexcept
275  
    bool is_open() const noexcept
276  
    {
276  
    {
277  
        return h_ && get().is_open();
277  
        return h_ && get().is_open();
278  
    }
278  
    }
279  

279  

280  
    /** Initiate an asynchronous accept into an existing socket.
280  
    /** Initiate an asynchronous accept into an existing socket.
281  

281  

282  
        Completes when a new connection is available. On success
282  
        Completes when a new connection is available. On success
283  
        @p peer is reset to the accepted connection. Only one
283  
        @p peer is reset to the accepted connection. Only one
284  
        accept may be in flight at a time.
284  
        accept may be in flight at a time.
285  

285  

286  
        @param peer The socket to receive the accepted connection.
286  
        @param peer The socket to receive the accepted connection.
287  

287  

288  
        @par Cancellation
288  
        @par Cancellation
289  
        Supports cancellation via stop_token or cancel().
289  
        Supports cancellation via stop_token or cancel().
290  
        On cancellation, yields `capy::cond::canceled` and
290  
        On cancellation, yields `capy::cond::canceled` and
291  
        @p peer is not modified.
291  
        @p peer is not modified.
292  

292  

293  
        @return An awaitable that completes with io_result<>.
293  
        @return An awaitable that completes with io_result<>.
294  

294  

295  
        @throws std::logic_error if the acceptor is not open.
295  
        @throws std::logic_error if the acceptor is not open.
296  
    */
296  
    */
297  
    auto accept(local_stream_socket& peer)
297  
    auto accept(local_stream_socket& peer)
298  
    {
298  
    {
299  
        if (!is_open())
299  
        if (!is_open())
300  
            detail::throw_logic_error("accept: acceptor not listening");
300  
            detail::throw_logic_error("accept: acceptor not listening");
301  
        return accept_awaitable(*this, peer);
301  
        return accept_awaitable(*this, peer);
302  
    }
302  
    }
303  

303  

304  
    /** Initiate an asynchronous accept, returning the socket.
304  
    /** Initiate an asynchronous accept, returning the socket.
305  

305  

306  
        Completes when a new connection is available. Only one
306  
        Completes when a new connection is available. Only one
307  
        accept may be in flight at a time.
307  
        accept may be in flight at a time.
308  

308  

309  
        @par Cancellation
309  
        @par Cancellation
310  
        Supports cancellation via stop_token or cancel().
310  
        Supports cancellation via stop_token or cancel().
311  
        On cancellation, yields `capy::cond::canceled` with
311  
        On cancellation, yields `capy::cond::canceled` with
312  
        a default-constructed socket.
312  
        a default-constructed socket.
313  

313  

314  
        @return An awaitable that completes with
314  
        @return An awaitable that completes with
315  
            io_result<local_stream_socket>.
315  
            io_result<local_stream_socket>.
316  

316  

317  
        @throws std::logic_error if the acceptor is not open.
317  
        @throws std::logic_error if the acceptor is not open.
318  
    */
318  
    */
319  
    auto accept()
319  
    auto accept()
320  
    {
320  
    {
321  
        if (!is_open())
321  
        if (!is_open())
322  
            detail::throw_logic_error("accept: acceptor not listening");
322  
            detail::throw_logic_error("accept: acceptor not listening");
323  
        return move_accept_awaitable(*this);
323  
        return move_accept_awaitable(*this);
324  
    }
324  
    }
325  

325  

326  
    /** Cancel pending asynchronous accept operations.
326  
    /** Cancel pending asynchronous accept operations.
327  

327  

328  
        Outstanding accept operations complete with
328  
        Outstanding accept operations complete with
329  
        @c capy::cond::canceled. Safe to call when no
329  
        @c capy::cond::canceled. Safe to call when no
330  
        operations are pending (no-op).
330  
        operations are pending (no-op).
331  
    */
331  
    */
332  
    void cancel();
332  
    void cancel();
333  

333  

334  
    /** Release ownership of the native socket handle.
334  
    /** Release ownership of the native socket handle.
335  

335  

336  
        Deregisters the acceptor from the reactor and cancels
336  
        Deregisters the acceptor from the reactor and cancels
337  
        pending operations without closing the descriptor. The
337  
        pending operations without closing the descriptor. The
338  
        caller takes ownership of the returned handle.
338  
        caller takes ownership of the returned handle.
339  

339  

340  
        @return The native handle.
340  
        @return The native handle.
341  

341  

342  
        @throws std::logic_error if the acceptor is not open.
342  
        @throws std::logic_error if the acceptor is not open.
343  

343  

344  
        @post is_open() == false
344  
        @post is_open() == false
345  
    */
345  
    */
346  
    native_handle_type release();
346  
    native_handle_type release();
347  

347  

348  
    /** Return the local endpoint the acceptor is bound to.
348  
    /** Return the local endpoint the acceptor is bound to.
349  

349  

350  
        Returns a default-constructed (empty) endpoint if the
350  
        Returns a default-constructed (empty) endpoint if the
351  
        acceptor is not open or not yet bound. Safe to call in
351  
        acceptor is not open or not yet bound. Safe to call in
352  
        any state.
352  
        any state.
353  
    */
353  
    */
354  
    corosio::local_endpoint local_endpoint() const noexcept;
354  
    corosio::local_endpoint local_endpoint() const noexcept;
355  

355  

356  
    /** Set a socket option on the acceptor.
356  
    /** Set a socket option on the acceptor.
357  

357  

358  
        Applies a type-safe socket option to the underlying socket.
358  
        Applies a type-safe socket option to the underlying socket.
359  
        The option type encodes the protocol level and option name.
359  
        The option type encodes the protocol level and option name.
360  

360  

361  
        @param opt The option to set.
361  
        @param opt The option to set.
362  

362  

363  
        @tparam Option A socket option type providing static
363  
        @tparam Option A socket option type providing static
364  
            `level()` and `name()` members, and `data()` / `size()`
364  
            `level()` and `name()` members, and `data()` / `size()`
365  
            accessors.
365  
            accessors.
366  

366  

367  
        @throws std::logic_error if the acceptor is not open.
367  
        @throws std::logic_error if the acceptor is not open.
368  
        @throws std::system_error on failure.
368  
        @throws std::system_error on failure.
369  
    */
369  
    */
370  
    template<class Option>
370  
    template<class Option>
371  
    void set_option(Option const& opt)
371  
    void set_option(Option const& opt)
372  
    {
372  
    {
373  
        if (!is_open())
373  
        if (!is_open())
374  
            detail::throw_logic_error("set_option: acceptor not open");
374  
            detail::throw_logic_error("set_option: acceptor not open");
375  
        std::error_code ec = get().set_option(
375  
        std::error_code ec = get().set_option(
376  
            Option::level(), Option::name(), opt.data(), opt.size());
376  
            Option::level(), Option::name(), opt.data(), opt.size());
377  
        if (ec)
377  
        if (ec)
378  
            detail::throw_system_error(ec, "local_stream_acceptor::set_option");
378  
            detail::throw_system_error(ec, "local_stream_acceptor::set_option");
379  
    }
379  
    }
380  

380  

381  
    /** Get a socket option from the acceptor.
381  
    /** Get a socket option from the acceptor.
382  

382  

383  
        Retrieves the current value of a type-safe socket option.
383  
        Retrieves the current value of a type-safe socket option.
384  

384  

385  
        @return The current option value.
385  
        @return The current option value.
386  

386  

387  
        @tparam Option A socket option type providing static
387  
        @tparam Option A socket option type providing static
388  
            `level()` and `name()` members, and `data()` / `size()`
388  
            `level()` and `name()` members, and `data()` / `size()`
389  
            / `resize()` members.
389  
            / `resize()` members.
390  

390  

391  
        @throws std::logic_error if the acceptor is not open.
391  
        @throws std::logic_error if the acceptor is not open.
392  
        @throws std::system_error on failure.
392  
        @throws std::system_error on failure.
393  
    */
393  
    */
394  
    template<class Option>
394  
    template<class Option>
395  
    Option get_option() const
395  
    Option get_option() const
396  
    {
396  
    {
397  
        if (!is_open())
397  
        if (!is_open())
398  
            detail::throw_logic_error("get_option: acceptor not open");
398  
            detail::throw_logic_error("get_option: acceptor not open");
399  
        Option opt{};
399  
        Option opt{};
400  
        std::size_t sz = opt.size();
400  
        std::size_t sz = opt.size();
401  
        std::error_code ec =
401  
        std::error_code ec =
402  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
402  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
403  
        if (ec)
403  
        if (ec)
404  
            detail::throw_system_error(ec, "local_stream_acceptor::get_option");
404  
            detail::throw_system_error(ec, "local_stream_acceptor::get_option");
405  
        opt.resize(sz);
405  
        opt.resize(sz);
406  
        return opt;
406  
        return opt;
407  
    }
407  
    }
408  

408  

409  
    /** Backend hooks for local stream acceptor operations.
409  
    /** Backend hooks for local stream acceptor operations.
410  

410  

411  
        Platform backends derive from this to implement
411  
        Platform backends derive from this to implement
412  
        accept, option, and lifecycle management.
412  
        accept, option, and lifecycle management.
413  
    */
413  
    */
414  
    struct implementation : io_object::implementation
414  
    struct implementation : io_object::implementation
415  
    {
415  
    {
416  
        /** Initiate an asynchronous accept.
416  
        /** Initiate an asynchronous accept.
417  

417  

418  
            On completion the backend sets @p *ec and, on
418  
            On completion the backend sets @p *ec and, on
419  
            success, stores a pointer to the new socket
419  
            success, stores a pointer to the new socket
420  
            implementation in @p *impl_out.
420  
            implementation in @p *impl_out.
421  

421  

422  
            @param h Coroutine handle to resume.
422  
            @param h Coroutine handle to resume.
423  
            @param ex Executor for dispatching the completion.
423  
            @param ex Executor for dispatching the completion.
424  
            @param token Stop token for cancellation.
424  
            @param token Stop token for cancellation.
425  
            @param ec Output error code.
425  
            @param ec Output error code.
426  
            @param impl_out Output pointer for the accepted socket.
426  
            @param impl_out Output pointer for the accepted socket.
427  
            @return Coroutine handle to resume immediately.
427  
            @return Coroutine handle to resume immediately.
428  
        */
428  
        */
429  
        virtual std::coroutine_handle<> accept(
429  
        virtual std::coroutine_handle<> accept(
430  
            std::coroutine_handle<>,
430  
            std::coroutine_handle<>,
431  
            capy::executor_ref,
431  
            capy::executor_ref,
432  
            std::stop_token,
432  
            std::stop_token,
433  
            std::error_code*,
433  
            std::error_code*,
434  
            io_object::implementation**) = 0;
434  
            io_object::implementation**) = 0;
435  

435  

436  
        /// Return the cached local endpoint.
436  
        /// Return the cached local endpoint.
437  
        virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
437  
        virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
438  

438  

439  
        /// Return whether the underlying socket is open.
439  
        /// Return whether the underlying socket is open.
440  
        virtual bool is_open() const noexcept = 0;
440  
        virtual bool is_open() const noexcept = 0;
441  

441  

442  
        /// Release and return the native handle without closing.
442  
        /// Release and return the native handle without closing.
443  
        virtual native_handle_type release_socket() noexcept = 0;
443  
        virtual native_handle_type release_socket() noexcept = 0;
444  

444  

445  
        /// Cancel pending accept operations.
445  
        /// Cancel pending accept operations.
446  
        virtual void cancel() noexcept = 0;
446  
        virtual void cancel() noexcept = 0;
447  

447  

448  
        /// Set a raw socket option.
448  
        /// Set a raw socket option.
449  
        virtual std::error_code set_option(
449  
        virtual std::error_code set_option(
450  
            int level,
450  
            int level,
451  
            int optname,
451  
            int optname,
452  
            void const* data,
452  
            void const* data,
453  
            std::size_t size) noexcept = 0;
453  
            std::size_t size) noexcept = 0;
454  

454  

455  
        /// Get a raw socket option.
455  
        /// Get a raw socket option.
456  
        virtual std::error_code
456  
        virtual std::error_code
457  
        get_option(int level, int optname, void* data, std::size_t* size)
457  
        get_option(int level, int optname, void* data, std::size_t* size)
458  
            const noexcept = 0;
458  
            const noexcept = 0;
459  
    };
459  
    };
460  

460  

461  
protected:
461  
protected:
462  
    local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
462  
    local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
463  
        : io_object(std::move(h))
463  
        : io_object(std::move(h))
464  
        , ctx_(ctx)
464  
        , ctx_(ctx)
465  
    {
465  
    {
466  
    }
466  
    }
467  

467  

468  
    local_stream_acceptor(
468  
    local_stream_acceptor(
469  
        capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
469  
        capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
470  
        : io_object(std::move(other))
470  
        : io_object(std::move(other))
471  
        , ctx_(ctx)
471  
        , ctx_(ctx)
472  
    {
472  
    {
473  
    }
473  
    }
474  

474  

475  
    static void reset_peer_impl(
475  
    static void reset_peer_impl(
476  
        local_stream_socket& peer, io_object::implementation* impl) noexcept
476  
        local_stream_socket& peer, io_object::implementation* impl) noexcept
477  
    {
477  
    {
478  
        if (impl)
478  
        if (impl)
479  
            peer.h_.reset(impl);
479  
            peer.h_.reset(impl);
480  
    }
480  
    }
481  

481  

482  
private:
482  
private:
483  
    capy::execution_context& ctx_;
483  
    capy::execution_context& ctx_;
484  

484  

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

490  

491  
} // namespace boost::corosio
491  
} // namespace boost::corosio
492  

492  

493  
#endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
493  
#endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP