TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
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)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP
12 : #define BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP
13 :
14 : #include <boost/corosio/stream_file.hpp>
15 : #include <boost/corosio/backend.hpp>
16 :
17 : #ifndef BOOST_COROSIO_MRDOCS
18 : #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
19 : BOOST_COROSIO_HAS_KQUEUE
20 : #include <boost/corosio/native/detail/posix/posix_stream_file_service.hpp>
21 : #endif
22 :
23 : #if BOOST_COROSIO_HAS_URING
24 : #include <boost/corosio/native/detail/uring/uring_stream_file.hpp>
25 : #endif
26 :
27 : #if BOOST_COROSIO_HAS_IOCP
28 : #include <boost/corosio/native/detail/iocp/win_file_service.hpp>
29 : #endif
30 : #endif // !BOOST_COROSIO_MRDOCS
31 :
32 : namespace boost::corosio {
33 :
34 : /** A sequential file with devirtualized async I/O operations.
35 :
36 : This class template inherits from @ref stream_file and shadows
37 : `read_some` / `write_some` with versions that call the backend
38 : implementation directly, allowing the compiler to inline through
39 : the entire call chain.
40 :
41 : Non-async operations (`open`, `close`, `size`, `resize`, `seek`,
42 : `sync_data`, `sync_all`) remain unchanged and dispatch through
43 : the compiled library.
44 :
45 : A `native_stream_file` IS-A `stream_file` and can be passed to
46 : any function expecting `stream_file&` or `io_stream&`, in which
47 : case virtual dispatch is used transparently.
48 :
49 : @note On POSIX platforms, file I/O is dispatched to a thread
50 : pool regardless of the chosen reactor backend, so all three
51 : reactor tags (`epoll`, `select`, `kqueue`) resolve to the same
52 : underlying implementation. The `Backend` template parameter
53 : exists for API symmetry with @ref native_tcp_socket and friends.
54 : The vtable savings are smaller relative to the thread-pool /
55 : overlapped-I/O cost than they are for socket operations.
56 :
57 : @tparam Backend A backend tag value (e.g., `epoll`, `iocp`).
58 :
59 : @par Thread Safety
60 : Same as @ref stream_file.
61 :
62 : @par Example
63 : @par !example native_stream_file
64 :
65 : @see stream_file, epoll_t, iocp_t
66 : */
67 : template<auto Backend>
68 : class native_stream_file : public stream_file
69 : {
70 : using backend_type = decltype(Backend);
71 : using impl_type = typename backend_type::stream_file_type;
72 : using service_type = typename backend_type::stream_file_service_type;
73 :
74 HIT 12 : impl_type& get_impl() noexcept
75 : {
76 12 : return *static_cast<impl_type*>(h_.get());
77 : }
78 :
79 : template<class MutableBufferSequence>
80 : struct native_read_awaitable
81 : {
82 : native_stream_file& self_;
83 : MutableBufferSequence buffers_;
84 : std::stop_token token_;
85 : mutable std::error_code ec_;
86 : mutable std::size_t bytes_transferred_ = 0;
87 :
88 6 : native_read_awaitable(
89 : native_stream_file& self, MutableBufferSequence buffers) noexcept
90 6 : : self_(self)
91 6 : , buffers_(std::move(buffers))
92 : {
93 6 : }
94 :
95 6 : bool await_ready() const noexcept
96 : {
97 : // A pre-set ec_ means the initiator failed before
98 : // dispatch (e.g. a closed object).
99 6 : return static_cast<bool>(ec_) || token_.stop_requested();
100 : }
101 :
102 6 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
103 : {
104 6 : if (token_.stop_requested())
105 2 : return {make_error_code(std::errc::operation_canceled), 0};
106 4 : return {ec_, bytes_transferred_};
107 : }
108 :
109 6 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
110 : -> std::coroutine_handle<>
111 : {
112 6 : token_ = env->stop_token;
113 18 : return self_.get_impl().read_some(
114 18 : h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
115 : }
116 : };
117 :
118 : template<class ConstBufferSequence>
119 : struct native_write_awaitable
120 : {
121 : native_stream_file& self_;
122 : ConstBufferSequence buffers_;
123 : std::stop_token token_;
124 : mutable std::error_code ec_;
125 : mutable std::size_t bytes_transferred_ = 0;
126 :
127 6 : native_write_awaitable(
128 : native_stream_file& self, ConstBufferSequence buffers) noexcept
129 6 : : self_(self)
130 6 : , buffers_(std::move(buffers))
131 : {
132 6 : }
133 :
134 6 : bool await_ready() const noexcept
135 : {
136 : // A pre-set ec_ means the initiator failed before
137 : // dispatch (e.g. a closed object).
138 6 : return static_cast<bool>(ec_) || token_.stop_requested();
139 : }
140 :
141 6 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
142 : {
143 6 : if (token_.stop_requested())
144 2 : return {make_error_code(std::errc::operation_canceled), 0};
145 4 : return {ec_, bytes_transferred_};
146 : }
147 :
148 6 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
149 : -> std::coroutine_handle<>
150 : {
151 6 : token_ = env->stop_token;
152 18 : return self_.get_impl().write_some(
153 18 : h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
154 : }
155 : };
156 :
157 : public:
158 : /** Construct a native stream file from an execution context.
159 :
160 : @param ctx The execution context that will own this file.
161 : */
162 16 : explicit native_stream_file(capy::execution_context& ctx)
163 16 : : io_object(create_handle<service_type>(ctx))
164 : {
165 16 : }
166 :
167 : /** Construct a native stream file from an executor.
168 :
169 : @param ex The executor whose context will own this file.
170 : */
171 : template<class Ex>
172 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_stream_file>) &&
173 : capy::Executor<Ex>
174 : explicit native_stream_file(Ex const& ex) : native_stream_file(ex.context())
175 : {
176 : }
177 :
178 : /// Move construct.
179 : native_stream_file(native_stream_file&&) noexcept = default;
180 :
181 : /// Move assign.
182 : native_stream_file& operator=(native_stream_file&&) noexcept = default;
183 :
184 : native_stream_file(native_stream_file const&) = delete;
185 : native_stream_file& operator=(native_stream_file const&) = delete;
186 :
187 : /** Asynchronously read data from the file.
188 :
189 : Calls the backend implementation directly, bypassing virtual
190 : dispatch. Otherwise identical to @ref io_stream::read_some.
191 : */
192 : template<capy::MutableBufferSequence MB>
193 6 : [[nodiscard]] auto read_some(MB const& buffers)
194 : {
195 6 : return native_read_awaitable<MB>(*this, buffers);
196 : }
197 :
198 : /** Asynchronously write data to the file.
199 :
200 : Calls the backend implementation directly, bypassing virtual
201 : dispatch. Otherwise identical to @ref io_stream::write_some.
202 : */
203 : template<capy::ConstBufferSequence CB>
204 6 : [[nodiscard]] auto write_some(CB const& buffers)
205 : {
206 6 : return native_write_awaitable<CB>(*this, buffers);
207 : }
208 : };
209 :
210 : } // namespace boost::corosio
211 :
212 : #endif // BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP
|