include/boost/corosio/native/detail/uring/uring_random_access_file.hpp

100.0% Lines (120/0/120) 100.0% List of functions (16/0/16)
uring_random_access_file.hpp
f(x) Functions (16)
Function Calls Lines Blocks
boost::corosio::detail::uring_random_access_file::uring_random_access_file(boost::corosio::detail::uring_scheduler&) :71 57x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::~uring_random_access_file() :76 56x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::native_handle() const :101 331x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::cancel() :106 2x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::size() const :112 6x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::resize(unsigned long) :120 6x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::sync_data() :130 4x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::sync_all() :141 4x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::release() :148 1x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::assign(int) :155 3x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::open_file(std::filesystem::__cxx11::path const&, boost::corosio::file_base::flags) :166 50x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::close_file() :206 261x 100.0% 100.0% boost::corosio::detail::uring_random_access_file::read_some_at(unsigned long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long*) :222 145x 100.0% 70.0% boost::corosio::detail::uring_random_access_file::write_some_at(unsigned long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long*) :260 21x 100.0% 70.0% boost::corosio::detail::uring_random_access_file_service::uring_random_access_file_service(boost::capy::execution_context&, boost::corosio::detail::uring_scheduler&) :316 799x 100.0% 100.0% boost::corosio::detail::uring_random_access_file_service::open_file(boost::corosio::random_access_file::implementation&, std::filesystem::__cxx11::path const&, boost::corosio::file_base::flags) :325 50x 100.0% 100.0%
Line TLA Hits 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_DETAIL_URING_URING_RANDOM_ACCESS_FILE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_URING_URING_RANDOM_ACCESS_FILE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_HAS_URING
16
17 #include <boost/corosio/detail/random_access_file_service.hpp>
18 #include <boost/corosio/detail/intrusive.hpp>
19 #include <boost/corosio/native/detail/uring/uring_file_ops.hpp>
20 #include <boost/corosio/native/detail/uring/uring_file_service_base.hpp>
21 #include <boost/corosio/native/detail/uring/uring_scheduler.hpp>
22 #include <boost/corosio/native/detail/make_err.hpp>
23 #include <boost/corosio/random_access_file.hpp>
24
25 #include <cstdint>
26 #include <filesystem>
27 #include <limits>
28 #include <memory>
29 #include <mutex>
30 #include <system_error>
31 #include <unordered_map>
32
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 namespace boost::corosio::detail {
39
40 class uring_random_access_file_service;
41
42 /** Native io_uring random-access-file implementation.
43
44 Async `read_some_at` / `write_some_at` submit `IORING_OP_READV`
45 / `IORING_OP_WRITEV` with the caller-supplied offset. Metadata
46 operations (open, size, resize, sync, close) are synchronous
47 syscalls.
48
49 @par Thread Safety
50 Concurrent `read_some_at` / `write_some_at` calls on the same
51 file at distinct offsets are safe; ordering between two
52 submissions at the same offset is unspecified at the kernel
53 level (matches POSIX `pread(2)` / `pwrite(2)` semantics).
54 */
55 class BOOST_COROSIO_DECL uring_random_access_file final
56 : public random_access_file::implementation
57 , public std::enable_shared_from_this<uring_random_access_file>
58 , public intrusive_list<uring_random_access_file>::node
59 {
60 friend class uring_random_access_file_service;
61
62 int fd_ = -1;
63 uring_scheduler* sched_ = nullptr;
64
65 // Random-access files legitimately support concurrent ops at
66 // different offsets on the same fd (e.g. parallel reads in
67 // testConcurrentReads). Embedding a single slot would smash
68 // state across calls; ops are heap-allocated per submission.
69
70 public:
71 57x explicit uring_random_access_file(uring_scheduler& sched) noexcept
72 57x : sched_(&sched)
73 {
74 57x }
75
76 56x ~uring_random_access_file() override
77 56x {
78 56x close_file();
79 56x }
80
81 // -- random_access_file::implementation --
82
83 std::coroutine_handle<> read_some_at(
84 std::uint64_t,
85 std::coroutine_handle<>,
86 capy::executor_ref,
87 buffer_param,
88 std::stop_token,
89 std::error_code*,
90 std::size_t*) override;
91
92 std::coroutine_handle<> write_some_at(
93 std::uint64_t,
94 std::coroutine_handle<>,
95 capy::executor_ref,
96 buffer_param,
97 std::stop_token,
98 std::error_code*,
99 std::size_t*) override;
100
101 331x native_handle_type native_handle() const noexcept override
102 {
103 331x return fd_;
104 }
105
106 2x void cancel() noexcept override
107 {
108 2x if (fd_ >= 0)
109 2x sched_->submit_cancel_by_fd(fd_);
110 2x }
111
112 6x std::uint64_t size() const override
113 {
114 struct stat st;
115 6x if (::fstat(fd_, &st) < 0)
116 2x throw_system_error(make_err(errno), "random_access_file::size");
117 4x return static_cast<std::uint64_t>(st.st_size);
118 }
119
120 6x std::error_code resize(std::uint64_t new_size) noexcept override
121 {
122 6x if (new_size >
123 6x static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)()))
124 1x return make_err(EOVERFLOW);
125 5x if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0)
126 3x return make_err(errno);
127 2x return {};
128 }
129
130 4x std::error_code sync_data() noexcept override
131 {
132 #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
133 4x if (::fdatasync(fd_) < 0)
134 #else
135 if (::fsync(fd_) < 0)
136 #endif
137 3x return make_err(errno);
138 1x return {};
139 }
140
141 4x std::error_code sync_all() noexcept override
142 {
143 4x if (::fsync(fd_) < 0)
144 3x return make_err(errno);
145 1x return {};
146 }
147
148 1x native_handle_type release() override
149 {
150 1x int fd = fd_;
151 1x fd_ = -1;
152 1x return fd;
153 }
154
155 3x std::error_code assign(native_handle_type handle) noexcept override
156 {
157 3x close_file();
158 3x fd_ = handle;
159 3x return {};
160 }
161
162 // -- Internal --
163
164 /// Open the file. Synchronous; sets `fd_`. Caller is the service.
165 std::error_code
166 50x open_file(std::filesystem::path const& path, file_base::flags mode)
167 {
168 50x close_file();
169
170 50x int oflags = 0;
171 50x unsigned access = static_cast<unsigned>(mode) & 3u;
172 50x if (access == static_cast<unsigned>(file_base::read_write))
173 18x oflags |= O_RDWR;
174 32x else if (access == static_cast<unsigned>(file_base::write_only))
175 7x oflags |= O_WRONLY;
176 else
177 25x oflags |= O_RDONLY;
178
179 50x if ((mode & file_base::create) != file_base::flags(0))
180 14x oflags |= O_CREAT;
181 50x if ((mode & file_base::exclusive) != file_base::flags(0))
182 2x oflags |= O_EXCL;
183 50x if ((mode & file_base::truncate) != file_base::flags(0))
184 7x oflags |= O_TRUNC;
185 50x if ((mode & file_base::sync_all_on_write) != file_base::flags(0))
186 1x oflags |= O_SYNC;
187
188 50x oflags |= O_CLOEXEC;
189
190 50x int fd = ::open(path.c_str(), oflags, 0666);
191 50x if (fd < 0)
192 4x return make_err(errno);
193
194 46x fd_ = fd;
195
196 #ifdef POSIX_FADV_RANDOM
197 // Hint the page cache that access will be random; matches
198 // the POSIX backend.
199 46x ::posix_fadvise(fd_, 0, 0, POSIX_FADV_RANDOM);
200 #endif
201
202 46x return {};
203 }
204
205 /// Cancel any in-flight ops and close the fd. Idempotent.
206 261x void close_file() noexcept
207 {
208 261x if (fd_ >= 0)
209 {
210 // The kernel may run a queued pipe write as task work at
211 // either kernel entry below; with the reader already gone
212 // that raises SIGPIPE.
213 48x scoped_sigpipe_block no_sigpipe;
214 48x sched_->cancel_and_flush(fd_);
215 48x ::close(fd_);
216 48x fd_ = -1;
217 48x }
218 261x }
219 };
220
221 inline std::coroutine_handle<>
222 145x uring_random_access_file::read_some_at(
223 std::uint64_t user_offset,
224 std::coroutine_handle<> h,
225 capy::executor_ref ex,
226 buffer_param buffers,
227 std::stop_token token,
228 std::error_code* ec,
229 std::size_t* bytes)
230 {
231 145x auto op_guard = std::make_unique<uring_random_access_read_op>();
232 290x op_guard->prepare(
233 h, ex, ec, bytes, fd_, static_cast<std::int64_t>(user_offset), sched_,
234 290x shared_from_this(), buffers, token);
235 145x sched_->work_started();
236
237 // Closed-object contract outranks the zero-length no-op.
238 145x if (fd_ < 0)
239 {
240 2x op_guard->empty_buffer = false;
241 2x op_guard->res = -EBADF;
242 2x uring_scheduler::lock_type lock(sched_->dispatch_mutex());
243 2x sched_->push_completed_locked(op_guard.release());
244 2x return std::noop_coroutine();
245 2x }
246
247 285x if (op_guard->empty_buffer ||
248 142x op_guard->cancelled.load(std::memory_order_acquire))
249 {
250 4x uring_scheduler::lock_type lock(sched_->dispatch_mutex());
251 4x sched_->push_completed_locked(op_guard.release());
252 4x return std::noop_coroutine();
253 4x }
254
255 139x uring_submit_op(*sched_, op_guard.release());
256 139x return std::noop_coroutine();
257 145x }
258
259 inline std::coroutine_handle<>
260 21x uring_random_access_file::write_some_at(
261 std::uint64_t user_offset,
262 std::coroutine_handle<> h,
263 capy::executor_ref ex,
264 buffer_param buffers,
265 std::stop_token token,
266 std::error_code* ec,
267 std::size_t* bytes)
268 {
269 21x auto op_guard = std::make_unique<uring_random_access_write_op>();
270 42x op_guard->prepare(
271 h, ex, ec, bytes, fd_, static_cast<std::int64_t>(user_offset), sched_,
272 42x shared_from_this(), buffers, token);
273 21x sched_->work_started();
274
275 // Closed-object contract outranks the zero-length no-op.
276 21x if (fd_ < 0)
277 {
278 1x op_guard->empty_buffer = false;
279 1x op_guard->res = -EBADF;
280 1x uring_scheduler::lock_type lock(sched_->dispatch_mutex());
281 1x sched_->push_completed_locked(op_guard.release());
282 1x return std::noop_coroutine();
283 1x }
284
285 39x if (op_guard->empty_buffer ||
286 19x op_guard->cancelled.load(std::memory_order_acquire))
287 {
288 3x uring_scheduler::lock_type lock(sched_->dispatch_mutex());
289 3x sched_->push_completed_locked(op_guard.release());
290 3x return std::noop_coroutine();
291 3x }
292
293 17x uring_submit_op(*sched_, op_guard.release());
294 17x return std::noop_coroutine();
295 21x }
296
297 /** Native io_uring random-access-file service.
298
299 Owns all `uring_random_access_file` impls. Replaces
300 `posix_random_access_file_service` for the io_uring backend;
301 registered under the abstract `random_access_file_service` key
302 by `uring_t::construct`.
303 */
304 class BOOST_COROSIO_DECL uring_random_access_file_service final
305 : public uring_file_service_base<
306 uring_random_access_file_service,
307 random_access_file_service,
308 uring_random_access_file>
309 {
310 using base_service = uring_file_service_base<
311 uring_random_access_file_service,
312 random_access_file_service,
313 uring_random_access_file>;
314
315 public:
316 799x explicit uring_random_access_file_service(
317 capy::execution_context& /*ctx*/, uring_scheduler& sched)
318 799x : base_service(sched)
319 {
320 799x }
321
322 // construct / destroy / close / shutdown / scheduler() are inherited
323 // from uring_file_service_base.
324
325 50x std::error_code open_file(
326 random_access_file::implementation& impl,
327 std::filesystem::path const& path,
328 file_base::flags mode) override
329 {
330 50x return static_cast<uring_random_access_file&>(impl).open_file(
331 50x path, mode);
332 }
333 };
334
335 } // namespace boost::corosio::detail
336
337 #endif // BOOST_COROSIO_HAS_URING
338
339 #endif // BOOST_COROSIO_NATIVE_DETAIL_URING_URING_RANDOM_ACCESS_FILE_HPP
340