include/boost/corosio/native/detail/posix/posix_random_access_file_service.hpp

90.7% Lines (127/140) 100.0% List of functions (15/15)
posix_random_access_file_service.hpp
f(x) Functions (15)
Function Calls Lines Blocks
boost::corosio::detail::posix_random_access_file_service::posix_random_access_file_service(boost::capy::execution_context&, boost::corosio::detail::scheduler&) :33 1334x 100.0% 88.0% boost::corosio::detail::posix_random_access_file_service::~posix_random_access_file_service() :40 2668x 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::construct() :47 76x 100.0% 71.0% boost::corosio::detail::posix_random_access_file_service::destroy(boost::corosio::io_object::implementation*) :61 76x 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::close(boost::corosio::io_object::handle&) :69 136x 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::open_file(boost::corosio::random_access_file::implementation&, std::filesystem::__cxx11::path const&, boost::corosio::file_base::flags) :79 64x 80.0% 83.0% boost::corosio::detail::posix_random_access_file_service::shutdown() :92 1334x 62.5% 70.0% boost::corosio::detail::posix_random_access_file_service::destroy_impl(boost::corosio::detail::posix_random_access_file&) :104 76x 100.0% 67.0% boost::corosio::detail::posix_random_access_file_service::post(boost::corosio::detail::scheduler_op*) :111 294x 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::pool() :126 294x 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::get_or_create_pool(boost::capy::execution_context&) :132 1334x 80.0% 67.0% boost::corosio::detail::get_random_access_file_service(boost::capy::execution_context&, boost::corosio::detail::scheduler&) :152 1334x 100.0% 100.0% boost::corosio::detail::posix_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*) :162 272x 93.3% 84.0% boost::corosio::detail::posix_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*) :217 26x 93.3% 84.0% boost::corosio::detail::posix_random_access_file::raf_op::do_work(boost::corosio::detail::pool_work_item*) :274 294x 83.3% 76.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
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_POSIX_POSIX_RANDOM_ACCESS_FILE_SERVICE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RANDOM_ACCESS_FILE_SERVICE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_POSIX
16
17 #include <boost/corosio/native/detail/posix/posix_random_access_file.hpp>
18 #include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp>
19 #include <boost/corosio/detail/random_access_file_service.hpp>
20 #include <boost/corosio/detail/thread_pool.hpp>
21
22 #include <limits>
23 #include <mutex>
24 #include <unordered_map>
25
26 namespace boost::corosio::detail {
27
28 /** Random-access file service for POSIX backends. */
29 class BOOST_COROSIO_DECL posix_random_access_file_service final
30 : public random_access_file_service
31 {
32 public:
33 1334x posix_random_access_file_service(
34 capy::execution_context& ctx, scheduler& sched)
35 2668x : sched_(&sched)
36 1334x , pool_(get_or_create_pool(ctx))
37 {
38 1334x }
39
40 2668x ~posix_random_access_file_service() override = default;
41
42 posix_random_access_file_service(
43 posix_random_access_file_service const&) = delete;
44 posix_random_access_file_service& operator=(
45 posix_random_access_file_service const&) = delete;
46
47 76x io_object::implementation* construct() override
48 {
49 76x auto ptr = std::make_shared<posix_random_access_file>(*this);
50 76x auto* impl = ptr.get();
51
52 {
53 76x std::lock_guard<std::mutex> lock(mutex_);
54 76x file_list_.push_back(impl);
55 76x file_ptrs_[impl] = std::move(ptr);
56 76x }
57
58 76x return impl;
59 76x }
60
61 76x void destroy(io_object::implementation* p) override
62 {
63 76x auto& impl = static_cast<posix_random_access_file&>(*p);
64 76x impl.cancel();
65 76x impl.close_file();
66 76x destroy_impl(impl);
67 76x }
68
69 136x void close(io_object::handle& h) override
70 {
71 136x if (h.get())
72 {
73 136x auto& impl = static_cast<posix_random_access_file&>(*h.get());
74 136x impl.cancel();
75 136x impl.close_file();
76 }
77 136x }
78
79 64x std::error_code open_file(
80 random_access_file::implementation& impl,
81 std::filesystem::path const& path,
82 file_base::flags mode) override
83 {
84 // Unavailable in the unsafe tier: the file thread pool completes
85 // cross-thread, which the lockless scheduler cannot accept.
86 64x if (sched_->scheduler_locking_disabled())
87 return std::make_error_code(std::errc::operation_not_supported);
88 64x return static_cast<posix_random_access_file&>(impl).open_file(
89 64x path, mode);
90 }
91
92 1334x void shutdown() override
93 {
94 1334x std::lock_guard<std::mutex> lock(mutex_);
95 1334x for (auto* impl = file_list_.pop_front(); impl != nullptr;
96 impl = file_list_.pop_front())
97 {
98 impl->cancel();
99 impl->close_file();
100 }
101 1334x file_ptrs_.clear();
102 1334x }
103
104 76x void destroy_impl(posix_random_access_file& impl)
105 {
106 76x std::lock_guard<std::mutex> lock(mutex_);
107 76x file_list_.remove(&impl);
108 76x file_ptrs_.erase(&impl);
109 76x }
110
111 294x void post(scheduler_op* op)
112 {
113 294x sched_->post(op);
114 294x }
115
116 void work_started() noexcept
117 {
118 sched_->work_started();
119 }
120
121 void work_finished() noexcept
122 {
123 sched_->work_finished();
124 }
125
126 294x thread_pool& pool() noexcept
127 {
128 294x return pool_;
129 }
130
131 private:
132 1334x static thread_pool& get_or_create_pool(capy::execution_context& ctx)
133 {
134 1334x auto* p = ctx.find_service<thread_pool>();
135 1334x if (p)
136 1334x return *p;
137 return ctx.make_service<thread_pool>();
138 }
139
140 scheduler* sched_;
141 thread_pool& pool_;
142 std::mutex mutex_;
143 intrusive_list<posix_random_access_file> file_list_;
144 std::unordered_map<
145 posix_random_access_file*,
146 std::shared_ptr<posix_random_access_file>>
147 file_ptrs_;
148 };
149
150 /** Get or create the random-access file service for the given context. */
151 inline posix_random_access_file_service&
152 1334x get_random_access_file_service(capy::execution_context& ctx, scheduler& sched)
153 {
154 1334x return ctx.make_service<posix_random_access_file_service>(sched);
155 }
156
157 // ---------------------------------------------------------------------------
158 // posix_random_access_file inline implementations (require complete service)
159 // ---------------------------------------------------------------------------
160
161 inline std::coroutine_handle<>
162 272x posix_random_access_file::read_some_at(
163 std::uint64_t offset,
164 std::coroutine_handle<> h,
165 capy::executor_ref ex,
166 buffer_param param,
167 std::stop_token token,
168 std::error_code* ec,
169 std::size_t* bytes_out)
170 {
171 272x capy::mutable_buffer bufs[max_buffers];
172 272x auto count = param.copy_to(bufs, max_buffers);
173
174 272x if (count == 0)
175 {
176 2x *ec = {};
177 2x *bytes_out = 0;
178 2x return h;
179 }
180
181 270x auto* op = new raf_op();
182 270x op->is_read = true;
183 270x op->offset = offset;
184
185 270x op->iovec_count = static_cast<int>(count);
186 540x for (int i = 0; i < op->iovec_count; ++i)
187 {
188 270x op->iovecs[i].iov_base = bufs[i].data();
189 270x op->iovecs[i].iov_len = bufs[i].size();
190 }
191
192 270x op->h = h;
193 270x op->ex = ex;
194 270x op->ec_out = ec;
195 270x op->bytes_out = bytes_out;
196 270x op->file_ = this;
197 270x op->file_ref = this->shared_from_this();
198 270x op->start(token);
199
200 270x op->ex.on_work_started();
201
202 {
203 270x std::lock_guard<std::mutex> lock(ops_mutex_);
204 270x outstanding_ops_.push_back(op);
205 270x }
206
207 270x static_cast<pool_work_item*>(op)->func_ = &raf_op::do_work;
208 270x if (!svc_.pool().post(static_cast<pool_work_item*>(op)))
209 {
210 op->cancelled.store(true, std::memory_order_release);
211 svc_.post(static_cast<scheduler_op*>(op));
212 }
213 270x return std::noop_coroutine();
214 }
215
216 inline std::coroutine_handle<>
217 26x posix_random_access_file::write_some_at(
218 std::uint64_t offset,
219 std::coroutine_handle<> h,
220 capy::executor_ref ex,
221 buffer_param param,
222 std::stop_token token,
223 std::error_code* ec,
224 std::size_t* bytes_out)
225 {
226 26x capy::mutable_buffer bufs[max_buffers];
227 26x auto count = param.copy_to(bufs, max_buffers);
228
229 26x if (count == 0)
230 {
231 2x *ec = {};
232 2x *bytes_out = 0;
233 2x return h;
234 }
235
236 24x auto* op = new raf_op();
237 24x op->is_read = false;
238 24x op->offset = offset;
239
240 24x op->iovec_count = static_cast<int>(count);
241 48x for (int i = 0; i < op->iovec_count; ++i)
242 {
243 24x op->iovecs[i].iov_base = bufs[i].data();
244 24x op->iovecs[i].iov_len = bufs[i].size();
245 }
246
247 24x op->h = h;
248 24x op->ex = ex;
249 24x op->ec_out = ec;
250 24x op->bytes_out = bytes_out;
251 24x op->file_ = this;
252 24x op->file_ref = this->shared_from_this();
253 24x op->start(token);
254
255 24x op->ex.on_work_started();
256
257 {
258 24x std::lock_guard<std::mutex> lock(ops_mutex_);
259 24x outstanding_ops_.push_back(op);
260 24x }
261
262 24x static_cast<pool_work_item*>(op)->func_ = &raf_op::do_work;
263 24x if (!svc_.pool().post(static_cast<pool_work_item*>(op)))
264 {
265 op->cancelled.store(true, std::memory_order_release);
266 svc_.post(static_cast<scheduler_op*>(op));
267 }
268 24x return std::noop_coroutine();
269 }
270
271 // -- raf_op thread-pool work function --
272
273 inline void
274 294x posix_random_access_file::raf_op::do_work(pool_work_item* w) noexcept
275 {
276 294x auto* op = static_cast<raf_op*>(w);
277 294x auto* self = op->file_;
278
279 294x if (op->cancelled.load(std::memory_order_acquire))
280 {
281 2x op->errn = ECANCELED;
282 2x op->bytes_transferred = 0;
283 }
284 584x else if (op->offset >
285 292x static_cast<std::uint64_t>(std::numeric_limits<off_t>::max()))
286 {
287 op->errn = EOVERFLOW;
288 op->bytes_transferred = 0;
289 }
290 else
291 {
292 ssize_t n;
293 292x if (op->is_read)
294 {
295 do
296 {
297 536x n = ::preadv(self->fd_, op->iovecs, op->iovec_count,
298 268x static_cast<off_t>(op->offset));
299 }
300 268x while (n < 0 && errno == EINTR);
301 }
302 else
303 {
304 do
305 {
306 48x n = ::pwritev(self->fd_, op->iovecs, op->iovec_count,
307 24x static_cast<off_t>(op->offset));
308 }
309 24x while (n < 0 && errno == EINTR);
310 }
311
312 292x if (n >= 0)
313 {
314 292x op->errn = 0;
315 292x op->bytes_transferred = static_cast<std::size_t>(n);
316 }
317 else
318 {
319 op->errn = errno;
320 op->bytes_transferred = 0;
321 }
322 }
323
324 294x self->svc_.post(static_cast<scheduler_op*>(op));
325 294x }
326
327 } // namespace boost::corosio::detail
328
329 #endif // BOOST_COROSIO_POSIX
330
331 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RANDOM_ACCESS_FILE_SERVICE_HPP
332