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_URING
24 : #include <boost/corosio/native/detail/uring/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 = typename backend_type::random_access_file_service_type;
73 :
74 HIT 14 : impl_type& get_impl() noexcept
75 : {
76 14 : return *static_cast<impl_type*>(h_.get());
77 : }
78 :
79 : template<class MutableBufferSequence>
80 : struct native_read_at_awaitable
81 : {
82 : native_random_access_file& self_;
83 : std::uint64_t offset_;
84 : MutableBufferSequence buffers_;
85 : std::stop_token token_;
86 : mutable std::error_code ec_;
87 : mutable std::size_t bytes_transferred_ = 0;
88 :
89 8 : native_read_at_awaitable(
90 : native_random_access_file& self,
91 : std::uint64_t offset,
92 : MutableBufferSequence buffers) noexcept
93 8 : : self_(self)
94 8 : , offset_(offset)
95 8 : , buffers_(std::move(buffers))
96 : {
97 8 : }
98 :
99 8 : bool await_ready() const noexcept
100 : {
101 : // A pre-set ec_ means the initiator failed before
102 : // dispatch (e.g. a closed object).
103 8 : return static_cast<bool>(ec_) || token_.stop_requested();
104 : }
105 :
106 8 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
107 : {
108 8 : if (token_.stop_requested())
109 2 : return {make_error_code(std::errc::operation_canceled), 0};
110 6 : return {ec_, bytes_transferred_};
111 : }
112 :
113 8 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
114 : -> std::coroutine_handle<>
115 : {
116 8 : token_ = env->stop_token;
117 24 : return self_.get_impl().read_some_at(
118 8 : offset_, h, env->executor, buffers_, token_, &ec_,
119 16 : &bytes_transferred_);
120 : }
121 : };
122 :
123 : template<class ConstBufferSequence>
124 : struct native_write_at_awaitable
125 : {
126 : native_random_access_file& self_;
127 : std::uint64_t offset_;
128 : ConstBufferSequence buffers_;
129 : std::stop_token token_;
130 : mutable std::error_code ec_;
131 : mutable std::size_t bytes_transferred_ = 0;
132 :
133 6 : native_write_at_awaitable(
134 : native_random_access_file& self,
135 : std::uint64_t offset,
136 : ConstBufferSequence buffers) noexcept
137 6 : : self_(self)
138 6 : , offset_(offset)
139 6 : , buffers_(std::move(buffers))
140 : {
141 6 : }
142 :
143 6 : bool await_ready() const noexcept
144 : {
145 : // A pre-set ec_ means the initiator failed before
146 : // dispatch (e.g. a closed object).
147 6 : return static_cast<bool>(ec_) || token_.stop_requested();
148 : }
149 :
150 6 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
151 : {
152 6 : if (token_.stop_requested())
153 2 : return {make_error_code(std::errc::operation_canceled), 0};
154 4 : return {ec_, bytes_transferred_};
155 : }
156 :
157 6 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
158 : -> std::coroutine_handle<>
159 : {
160 6 : token_ = env->stop_token;
161 18 : return self_.get_impl().write_some_at(
162 6 : offset_, h, env->executor, buffers_, token_, &ec_,
163 12 : &bytes_transferred_);
164 : }
165 : };
166 :
167 : public:
168 : /** Construct a native random-access file from an execution context.
169 :
170 : @param ctx The execution context that will own this file.
171 : */
172 16 : explicit native_random_access_file(capy::execution_context& ctx)
173 16 : : random_access_file(create_handle<service_type>(ctx))
174 : {
175 16 : }
176 :
177 : /** Construct a native random-access file from an executor.
178 :
179 : @param ex The executor whose context will own this file.
180 : */
181 : template<class Ex>
182 : requires(!std::same_as<
183 : std::remove_cvref_t<Ex>,
184 : native_random_access_file>) &&
185 : capy::Executor<Ex>
186 : explicit native_random_access_file(Ex const& ex)
187 : : native_random_access_file(ex.context())
188 : {
189 : }
190 :
191 : /// Move construct.
192 : native_random_access_file(native_random_access_file&&) noexcept = default;
193 :
194 : /// Move assign.
195 : native_random_access_file&
196 : operator=(native_random_access_file&&) noexcept = default;
197 :
198 : native_random_access_file(native_random_access_file const&) = delete;
199 : native_random_access_file&
200 : operator=(native_random_access_file const&) = delete;
201 :
202 : /** Asynchronously read at the given offset.
203 :
204 : Calls the backend implementation directly, bypassing virtual
205 : dispatch. Otherwise identical to @ref random_access_file::read_some_at.
206 : */
207 : template<capy::MutableBufferSequence MB>
208 8 : [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
209 : {
210 8 : return native_read_at_awaitable<MB>(*this, offset, buffers);
211 : }
212 :
213 : /** Asynchronously write at the given offset.
214 :
215 : Calls the backend implementation directly, bypassing virtual
216 : dispatch. Otherwise identical to @ref random_access_file::write_some_at.
217 : */
218 : template<capy::ConstBufferSequence CB>
219 6 : [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
220 : {
221 6 : return native_write_at_awaitable<CB>(*this, offset, buffers);
222 : }
223 : };
224 :
225 : } // namespace boost::corosio
226 :
227 : #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
|