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