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