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_RANDOM_ACCESS_FILE_HPP
12 : #define BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
13 :
14 : #include <boost/corosio/random_access_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_random_access_file_service.hpp>
21 : #endif
22 :
23 : #if BOOST_COROSIO_HAS_IO_URING
24 : #include <boost/corosio/native/detail/io_uring/io_uring_random_access_file.hpp>
25 : #endif
26 :
27 : #if BOOST_COROSIO_HAS_IOCP
28 : #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp>
29 : #endif
30 : #endif // !BOOST_COROSIO_MRDOCS
31 :
32 : namespace boost::corosio {
33 :
34 : /** A random-access file with devirtualized async I/O operations.
35 :
36 : This class template inherits from @ref random_access_file and
37 : shadows `read_some_at` / `write_some_at` with versions that
38 : call the backend implementation directly, allowing the compiler
39 : to inline through the entire call chain.
40 :
41 : Non-async operations (`open`, `close`, `size`, `resize`,
42 : `sync_data`, `sync_all`) remain unchanged and dispatch through
43 : the compiled library.
44 :
45 : A `native_random_access_file` IS-A `random_access_file` and
46 : can be passed to any function expecting `random_access_file&`,
47 : in which 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 random_access_file.
61 :
62 : @par Example
63 : @par !example native_random_access_file
64 :
65 : @see random_access_file, epoll_t, iocp_t
66 : */
67 : template<auto Backend>
68 : class native_random_access_file : public random_access_file
69 : {
70 : using backend_type = decltype(Backend);
71 : using impl_type = typename backend_type::random_access_file_type;
72 : using service_type =
73 : typename backend_type::random_access_file_service_type;
74 :
75 HIT 14 : impl_type& get_impl() noexcept
76 : {
77 14 : return *static_cast<impl_type*>(h_.get());
78 : }
79 :
80 : template<class MutableBufferSequence>
81 : struct native_read_at_awaitable
82 : {
83 : native_random_access_file& self_;
84 : std::uint64_t offset_;
85 : MutableBufferSequence buffers_;
86 : std::stop_token token_;
87 : mutable std::error_code ec_;
88 : mutable std::size_t bytes_transferred_ = 0;
89 :
90 8 : native_read_at_awaitable(
91 : native_random_access_file& self,
92 : std::uint64_t offset,
93 : MutableBufferSequence buffers) noexcept
94 8 : : self_(self)
95 8 : , offset_(offset)
96 8 : , buffers_(std::move(buffers))
97 : {
98 8 : }
99 :
100 8 : bool await_ready() const noexcept
101 : {
102 : // A pre-set ec_ means the initiator failed before
103 : // dispatch (e.g. a closed object).
104 8 : return static_cast<bool>(ec_) || token_.stop_requested();
105 : }
106 :
107 8 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
108 : {
109 8 : if (token_.stop_requested())
110 2 : return {make_error_code(std::errc::operation_canceled), 0};
111 6 : return {ec_, bytes_transferred_};
112 : }
113 :
114 8 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
115 : -> std::coroutine_handle<>
116 : {
117 8 : token_ = env->stop_token;
118 24 : return self_.get_impl().read_some_at(
119 8 : offset_, h, env->executor, buffers_,
120 24 : token_, &ec_, &bytes_transferred_);
121 : }
122 : };
123 :
124 : template<class ConstBufferSequence>
125 : struct native_write_at_awaitable
126 : {
127 : native_random_access_file& self_;
128 : std::uint64_t offset_;
129 : ConstBufferSequence buffers_;
130 : std::stop_token token_;
131 : mutable std::error_code ec_;
132 : mutable std::size_t bytes_transferred_ = 0;
133 :
134 6 : native_write_at_awaitable(
135 : native_random_access_file& self,
136 : std::uint64_t offset,
137 : ConstBufferSequence buffers) noexcept
138 6 : : self_(self)
139 6 : , offset_(offset)
140 6 : , buffers_(std::move(buffers))
141 : {
142 6 : }
143 :
144 6 : bool await_ready() const noexcept
145 : {
146 : // A pre-set ec_ means the initiator failed before
147 : // dispatch (e.g. a closed object).
148 6 : return static_cast<bool>(ec_) || token_.stop_requested();
149 : }
150 :
151 6 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
152 : {
153 6 : if (token_.stop_requested())
154 2 : return {make_error_code(std::errc::operation_canceled), 0};
155 4 : return {ec_, bytes_transferred_};
156 : }
157 :
158 6 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
159 : -> std::coroutine_handle<>
160 : {
161 6 : token_ = env->stop_token;
162 18 : return self_.get_impl().write_some_at(
163 6 : offset_, h, env->executor, buffers_,
164 18 : 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 16 : explicit native_random_access_file(capy::execution_context& ctx)
174 16 : : random_access_file(create_handle<service_type>(ctx))
175 : {
176 16 : }
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 8 : [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
210 : {
211 8 : 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 6 : [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
221 : {
222 6 : 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
|