TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
11 : #define BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
12 :
13 : #include <boost/corosio/local_stream_acceptor.hpp>
14 : #include <boost/corosio/native/native_local_stream_socket.hpp>
15 : #include <boost/corosio/backend.hpp>
16 :
17 : #ifndef BOOST_COROSIO_MRDOCS
18 : #if BOOST_COROSIO_HAS_EPOLL
19 : #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
20 : #endif
21 :
22 : #if BOOST_COROSIO_HAS_SELECT
23 : #include <boost/corosio/native/detail/select/select_types.hpp>
24 : #endif
25 :
26 : #if BOOST_COROSIO_HAS_KQUEUE
27 : #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
28 : #endif
29 :
30 : #if BOOST_COROSIO_HAS_IOCP
31 : #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor_service.hpp>
32 : #endif
33 : #endif // !BOOST_COROSIO_MRDOCS
34 :
35 : namespace boost::corosio {
36 :
37 : /** An asynchronous Unix stream acceptor with devirtualized accept.
38 :
39 : This class template inherits from @ref local_stream_acceptor
40 : and shadows both `accept` overloads (the peer-reference form
41 : and the move-return form) with versions that call the backend
42 : implementation directly, allowing the compiler to inline
43 : through the entire call chain. The move-return form yields a
44 : @ref native_local_stream_socket so subsequent I/O on the peer
45 : is also devirtualized.
46 :
47 : Non-async operations (`listen`, `close`, `cancel`) remain
48 : unchanged and dispatch through the compiled library.
49 :
50 : A `native_local_stream_acceptor` IS-A `local_stream_acceptor`
51 : and can be passed to any function expecting
52 : `local_stream_acceptor&`.
53 :
54 : @tparam Backend A backend tag value (e.g., `epoll`).
55 :
56 : @par Thread Safety
57 : Same as @ref local_stream_acceptor.
58 :
59 : @see local_stream_acceptor, epoll_t, iocp_t
60 : */
61 : template<auto Backend>
62 : class native_local_stream_acceptor : public local_stream_acceptor
63 : {
64 : using backend_type = decltype(Backend);
65 : using impl_type = typename backend_type::local_stream_acceptor_type;
66 : using service_type =
67 : typename backend_type::local_stream_acceptor_service_type;
68 :
69 HIT 8 : impl_type& get_impl() noexcept
70 : {
71 8 : return *static_cast<impl_type*>(h_.get());
72 : }
73 :
74 : struct native_wait_awaitable
75 : {
76 : native_local_stream_acceptor& acc_;
77 : wait_type w_;
78 : std::stop_token token_;
79 : mutable std::error_code ec_;
80 :
81 2 : native_wait_awaitable(
82 : native_local_stream_acceptor& acc, wait_type w) noexcept
83 2 : : acc_(acc)
84 2 : , w_(w)
85 : {
86 2 : }
87 :
88 2 : bool await_ready() const noexcept
89 : {
90 2 : return token_.stop_requested();
91 : }
92 :
93 2 : capy::io_result<> await_resume() const noexcept
94 : {
95 2 : if (token_.stop_requested())
96 MIS 0 : return {make_error_code(std::errc::operation_canceled)};
97 HIT 2 : return {ec_};
98 : }
99 :
100 2 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
101 : -> std::coroutine_handle<>
102 : {
103 2 : token_ = env->stop_token;
104 6 : return acc_.get_impl().wait(
105 6 : h, env->executor, w_, token_, &ec_);
106 : }
107 : };
108 :
109 : struct native_accept_awaitable
110 : {
111 : native_local_stream_acceptor& acc_;
112 : local_stream_socket& peer_;
113 : std::stop_token token_;
114 : mutable std::error_code ec_;
115 : mutable io_object::implementation* peer_impl_ = nullptr;
116 :
117 4 : native_accept_awaitable(
118 : native_local_stream_acceptor& acc,
119 : local_stream_socket& peer) noexcept
120 4 : : acc_(acc)
121 4 : , peer_(peer)
122 : {
123 4 : }
124 :
125 4 : bool await_ready() const noexcept
126 : {
127 4 : return token_.stop_requested();
128 : }
129 :
130 4 : capy::io_result<> await_resume() const noexcept
131 : {
132 4 : if (token_.stop_requested())
133 MIS 0 : return {make_error_code(std::errc::operation_canceled)};
134 HIT 4 : if (!ec_)
135 4 : acc_.reset_peer_impl(peer_, peer_impl_);
136 4 : return {ec_};
137 : }
138 :
139 4 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
140 : -> std::coroutine_handle<>
141 : {
142 4 : token_ = env->stop_token;
143 12 : return acc_.get_impl().accept(
144 12 : h, env->executor, token_, &ec_, &peer_impl_);
145 : }
146 : };
147 :
148 : struct native_move_accept_awaitable
149 : {
150 : native_local_stream_acceptor& acc_;
151 : std::stop_token token_;
152 : mutable std::error_code ec_;
153 : mutable io_object::implementation* peer_impl_ = nullptr;
154 :
155 2 : explicit native_move_accept_awaitable(
156 : native_local_stream_acceptor& acc) noexcept
157 2 : : acc_(acc)
158 : {
159 2 : }
160 :
161 2 : bool await_ready() const noexcept
162 : {
163 2 : return token_.stop_requested();
164 : }
165 :
166 : capy::io_result<native_local_stream_socket<Backend>>
167 2 : await_resume() const noexcept
168 : {
169 2 : if (token_.stop_requested())
170 : return {
171 MIS 0 : make_error_code(std::errc::operation_canceled),
172 0 : native_local_stream_socket<Backend>(acc_.context())};
173 HIT 2 : if (ec_ || !peer_impl_)
174 : return {
175 : ec_,
176 MIS 0 : native_local_stream_socket<Backend>(acc_.context())};
177 :
178 HIT 2 : native_local_stream_socket<Backend> peer(acc_.context());
179 2 : acc_.reset_peer_impl(peer, peer_impl_);
180 2 : return {ec_, std::move(peer)};
181 2 : }
182 :
183 2 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
184 : -> std::coroutine_handle<>
185 : {
186 2 : token_ = env->stop_token;
187 6 : return acc_.get_impl().accept(
188 6 : h, env->executor, token_, &ec_, &peer_impl_);
189 : }
190 : };
191 :
192 : public:
193 : /** Construct a native acceptor from an execution context.
194 :
195 : @param ctx The execution context that will own this acceptor.
196 : */
197 12 : explicit native_local_stream_acceptor(capy::execution_context& ctx)
198 12 : : local_stream_acceptor(create_handle<service_type>(ctx), ctx)
199 : {
200 12 : }
201 :
202 : /** Construct a native acceptor from an executor.
203 :
204 : @param ex The executor whose context will own the acceptor.
205 : */
206 : template<class Ex>
207 : requires(!std::same_as<
208 : std::remove_cvref_t<Ex>,
209 : native_local_stream_acceptor>) &&
210 : capy::Executor<Ex>
211 : explicit native_local_stream_acceptor(Ex const& ex)
212 : : native_local_stream_acceptor(ex.context())
213 : {
214 : }
215 :
216 : /// Move construct.
217 : native_local_stream_acceptor(native_local_stream_acceptor&&) noexcept =
218 : default;
219 :
220 : /// Move assign.
221 : native_local_stream_acceptor&
222 : operator=(native_local_stream_acceptor&&) noexcept = default;
223 :
224 : native_local_stream_acceptor(native_local_stream_acceptor const&) = delete;
225 : native_local_stream_acceptor&
226 : operator=(native_local_stream_acceptor const&) = delete;
227 :
228 : /** Asynchronously accept an incoming connection.
229 :
230 : Calls the backend implementation directly, bypassing virtual
231 : dispatch. Otherwise identical to @ref local_stream_acceptor::accept.
232 :
233 : @param peer The socket to receive the accepted connection.
234 :
235 : @return An awaitable yielding `io_result<>`.
236 :
237 : @throws std::logic_error if the acceptor is not listening.
238 :
239 : Both this acceptor and @p peer must outlive the returned
240 : awaitable.
241 : */
242 6 : auto accept(local_stream_socket& peer)
243 : {
244 6 : if (!is_open())
245 2 : detail::throw_logic_error("accept: acceptor not listening");
246 4 : return native_accept_awaitable(*this, peer);
247 : }
248 :
249 : /** Asynchronously accept an incoming connection, returning the peer.
250 :
251 : Calls the backend implementation directly, bypassing virtual
252 : dispatch. The accepted peer is returned as a
253 : @ref native_local_stream_socket so that subsequent I/O on it
254 : is also devirtualized.
255 :
256 : @return An awaitable yielding
257 : `io_result<native_local_stream_socket<Backend>>`.
258 :
259 : @throws std::logic_error if the acceptor is not listening.
260 :
261 : This acceptor must outlive the returned awaitable.
262 : */
263 4 : auto accept()
264 : {
265 4 : if (!is_open())
266 2 : detail::throw_logic_error("accept: acceptor not listening");
267 2 : return native_move_accept_awaitable(*this);
268 : }
269 :
270 : /** Asynchronously wait for the acceptor to be ready.
271 :
272 : Calls the backend implementation directly, bypassing virtual
273 : dispatch. Otherwise identical to @ref local_stream_acceptor::wait.
274 :
275 : @param w The wait direction (typically `wait_type::read`).
276 :
277 : @return An awaitable yielding `io_result<>`.
278 : */
279 2 : [[nodiscard]] auto wait(wait_type w)
280 : {
281 2 : return native_wait_awaitable(*this, w);
282 : }
283 : };
284 :
285 : } // namespace boost::corosio
286 :
287 : #endif // BOOST_COROSIO_NATIVE_NATIVE_LOCAL_STREAM_ACCEPTOR_HPP
|