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

99.4% Lines (166/167) 100.0% List of functions (13/13) 75.0% Branches (48/64)
posix_random_access_file_service.hpp
f(x) Functions (13)
Function Calls Lines Branches Blocks
boost::corosio::detail::posix_random_access_file_service::posix_random_access_file_service(boost::capy::execution_context&) :33 334x 100.0% 50.0% 100.0% boost::corosio::detail::posix_random_access_file_service::~posix_random_access_file_service() :39 501x 100.0% – 100.0% boost::corosio::detail::posix_random_access_file_service::construct() :46 222x 100.0% 50.0% 42.0% boost::corosio::detail::posix_random_access_file_service::destroy(boost::corosio::io_object::implementation*) :60 220x 100.0% – 100.0% boost::corosio::detail::posix_random_access_file_service::close(boost::corosio::io_object::handle&) :68 411x 100.0% 50.0% 100.0% boost::corosio::detail::posix_random_access_file_service::open_file(boost::corosio::random_access_file::implementation&, std::__1::__fs::filesystem::path const&, boost::corosio::file_base::flags) :78 201x 83.3% 50.0% 75.0% boost::corosio::detail::posix_random_access_file_service::shutdown() :91 167x 100.0% 100.0% 100.0% boost::corosio::detail::posix_random_access_file_service::destroy_impl(boost::corosio::detail::posix_random_access_file&) :103 220x 100.0% 50.0% 50.0% boost::corosio::detail::posix_random_access_file_service::post(boost::corosio::detail::scheduler_op*) :110 420x 100.0% – 100.0% boost::corosio::detail::posix_random_access_file_service::pool() :138 424x 100.0% – 100.0% boost::corosio::detail::posix_random_access_file::read_some_at(unsigned long long, boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::buffer_param, std::__1::stop_token, std::__1::error_code*, unsigned long*) :159 343x 100.0% 78.6% 86.0% boost::corosio::detail::posix_random_access_file::write_some_at(unsigned long long, boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::buffer_param, std::__1::stop_token, std::__1::error_code*, unsigned long*) :232 91x 100.0% 83.3% 94.0% boost::corosio::detail::posix_random_access_file::raf_op::do_work(boost::corosio::detail::pool_work_item*) :307 420x 100.0% 79.2% 93.0%
Line Branch 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 334x explicit posix_random_access_file_service(capy::execution_context& ctx)
34
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167x : sched_(&get_scheduler(ctx))
35 167x , pool_(ctx)
36 334x {
37 334x }
38
39 501x ~posix_random_access_file_service() override = default;
40
41 posix_random_access_file_service(posix_random_access_file_service const&) =
42 delete;
43 posix_random_access_file_service&
44 operator=(posix_random_access_file_service const&) = delete;
45
46 222x io_object::implementation* construct() override
47 {
48 222x auto ptr = std::make_shared<posix_random_access_file>(*this);
49 222x auto* impl = ptr.get();
50
51 {
52
1/2
✓ Branch 0 taken 222 times.
✗ Branch 1 not taken.
222x std::lock_guard<std::mutex> lock(mutex_);
53 222x file_list_.push_back(impl);
54
1/2
✓ Branch 0 taken 222 times.
✗ Branch 1 not taken.
222x file_ptrs_[impl] = std::move(ptr);
55 222x }
56
57 222x return impl;
58 222x }
59
60 220x void destroy(io_object::implementation* p) override
61 {
62 220x auto& impl = static_cast<posix_random_access_file&>(*p);
63 220x impl.cancel();
64 220x impl.close_file();
65 220x destroy_impl(impl);
66 220x }
67
68 411x void close(io_object::handle& h) override
69 {
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 411 times.
411x if (h.get())
71 {
72 411x auto& impl = static_cast<posix_random_access_file&>(*h.get());
73 411x impl.cancel();
74 411x impl.close_file();
75 411x }
76 411x }
77
78 201x std::error_code open_file(
79 random_access_file::implementation& impl,
80 std::filesystem::path const& path,
81 file_base::flags mode) override
82 {
83 // Unavailable in the unsafe tier: the file thread pool completes
84 // cross-thread, which the lockless scheduler cannot accept.
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 201 times.
201x if (sched_->scheduler_locking_disabled())
86 ✗ return std::make_error_code(std::errc::operation_not_supported);
87 402x return static_cast<posix_random_access_file&>(impl).open_file(
88 201x path, mode);
89 201x }
90
91 167x void shutdown() override
92 {
93 167x std::lock_guard<std::mutex> lock(mutex_);
94
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 167 times.
169x for (auto* impl = file_list_.pop_front(); impl != nullptr;
95 2x impl = file_list_.pop_front())
96 {
97 2x impl->cancel();
98 2x impl->close_file();
99 2x }
100 167x file_ptrs_.clear();
101 167x }
102
103 220x void destroy_impl(posix_random_access_file& impl)
104 {
105 220x std::lock_guard<std::mutex> lock(mutex_);
106 220x file_list_.remove(&impl);
107
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220x file_ptrs_.erase(&impl);
108 220x }
109
110 420x void post(scheduler_op* op)
111 {
112 420x sched_->post(op);
113 420x }
114
115 void work_started() noexcept
116 {
117 sched_->work_started();
118 }
119
120 void work_finished() noexcept
121 {
122 sched_->work_finished();
123 }
124
125 /** Return the thread pool that runs this service's file work.
126
127 The pool's service is created on first use, so this can fail
128 where a plain accessor could not. Its workers start later, on
129 the first post, and a thread the system refuses there is
130 reported by that post rather than thrown here.
131
132 @throws std::bad_alloc If the service cannot be allocated.
133
134 @return The context's shared blocking-I/O pool.
135
136 @see thread_pool_ref::get
137 */
138 424x thread_pool& pool()
139 {
140 424x return pool_.get();
141 }
142
143 private:
144 scheduler* sched_;
145 thread_pool_ref pool_;
146 std::mutex mutex_;
147 intrusive_list<posix_random_access_file> file_list_;
148 std::unordered_map<
149 posix_random_access_file*,
150 std::shared_ptr<posix_random_access_file>>
151 file_ptrs_;
152 };
153
154 // ---------------------------------------------------------------------------
155 // posix_random_access_file inline implementations (require complete service)
156 // ---------------------------------------------------------------------------
157
158 inline std::coroutine_handle<>
159 343x posix_random_access_file::read_some_at(
160 std::uint64_t offset,
161 capy::continuation& cont,
162 capy::executor_ref ex,
163 buffer_param param,
164 std::stop_token token,
165 std::error_code* ec,
166 std::size_t* bytes_out)
167 {
168 // Closed-object contract outranks the zero-length no-op.
169
2/2
✓ Branch 0 taken 339 times.
✓ Branch 1 taken 4 times.
343x if (fd_ < 0)
170 {
171 4x *ec = make_error_code(std::errc::bad_file_descriptor);
172 4x *bytes_out = 0;
173 4x return cont.h;
174 }
175
176 339x capy::mutable_buffer bufs[max_buffers];
177 339x auto count = param.copy_to(bufs, max_buffers);
178
179
2/2
✓ Branch 0 taken 337 times.
✓ Branch 1 taken 2 times.
339x if (count == 0)
180 {
181 2x *ec = {};
182 2x *bytes_out = 0;
183 2x return cont.h;
184 }
185
186 337x auto* op = new raf_op();
187 337x op->is_read = true;
188 337x op->offset = offset;
189 337x op->fd = fd_;
190
191 337x op->iovec_count = static_cast<int>(count);
192
2/2
✓ Branch 0 taken 337 times.
✓ Branch 1 taken 337 times.
674x for (int i = 0; i < op->iovec_count; ++i)
193 {
194 337x op->iovecs[i].iov_base = bufs[i].data();
195 337x op->iovecs[i].iov_len = bufs[i].size();
196 337x }
197
198 337x op->h = cont.h;
199 337x op->awaiting = &cont;
200 337x op->ex = ex;
201 337x op->ec_out = ec;
202 337x op->bytes_out = bytes_out;
203 337x op->file_ = this;
204
1/2
✓ Branch 0 taken 337 times.
✗ Branch 1 not taken.
337x op->impl_ptr = this->shared_from_this();
205 337x op->start(token);
206
207 337x op->ex.on_work_started();
208
209 {
210 337x std::lock_guard<std::mutex> lock(ops_mutex_);
211 337x outstanding_ops_.push_back(op);
212 337x }
213
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 337 times.
337x static_cast<pool_work_item*>(op)->func_ = &raf_op::do_work;
215
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 337 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 335 times.
337x if (auto pec = svc_.pool().post(static_cast<pool_work_item*>(op)))
216 {
217 // The pool is shutting down, or the system refused it a thread.
218 // Nothing of this read went cross-thread, so it answers here
219 // like the closed-descriptor and zero-length exits above rather
220 // than through a completion the scheduler has to carry back.
221 // destroy() is the discard the op never reaching the queue
222 // needs: it unlinks, unwinds the work count and frees.
223 2x op->destroy();
224 2x *ec = pec;
225 2x *bytes_out = 0;
226 2x return cont.h;
227 }
228 335x return std::noop_coroutine();
229 343x }
230
231 inline std::coroutine_handle<>
232 91x posix_random_access_file::write_some_at(
233 std::uint64_t offset,
234 capy::continuation& cont,
235 capy::executor_ref ex,
236 buffer_param param,
237 std::stop_token token,
238 std::error_code* ec,
239 std::size_t* bytes_out)
240 {
241 // Closed-object contract outranks the zero-length no-op.
242
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 2 times.
91x if (fd_ < 0)
243 {
244 2x *ec = make_error_code(std::errc::bad_file_descriptor);
245 2x *bytes_out = 0;
246 2x return cont.h;
247 }
248
249 89x capy::mutable_buffer bufs[max_buffers];
250 89x auto count = param.copy_to(bufs, max_buffers);
251
252
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 2 times.
89x if (count == 0)
253 {
254 2x *ec = {};
255 2x *bytes_out = 0;
256 2x return cont.h;
257 }
258
259 87x auto* op = new raf_op();
260 87x op->is_read = false;
261 87x op->offset = offset;
262 87x op->fd = fd_;
263
264 87x op->iovec_count = static_cast<int>(count);
265
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 87 times.
174x for (int i = 0; i < op->iovec_count; ++i)
266 {
267 87x op->iovecs[i].iov_base = bufs[i].data();
268 87x op->iovecs[i].iov_len = bufs[i].size();
269 87x }
270
271 87x op->h = cont.h;
272 87x op->awaiting = &cont;
273 87x op->ex = ex;
274 87x op->ec_out = ec;
275 87x op->bytes_out = bytes_out;
276 87x op->file_ = this;
277 87x op->impl_ptr = this->shared_from_this();
278 87x op->start(token);
279
280 87x op->ex.on_work_started();
281
282 {
283 87x std::lock_guard<std::mutex> lock(ops_mutex_);
284 87x outstanding_ops_.push_back(op);
285 87x }
286
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
87x static_cast<pool_work_item*>(op)->func_ = &raf_op::do_work;
288
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 85 times.
87x if (auto pec = svc_.pool().post(static_cast<pool_work_item*>(op)))
289 {
290 // The pool is shutting down, or the system refused it a thread.
291 // Nothing of this write went cross-thread, so it answers here
292 // like the closed-descriptor and zero-length exits above rather
293 // than through a completion the scheduler has to carry back.
294 // destroy() is the discard the op never reaching the queue
295 // needs: it unlinks, unwinds the work count and frees.
296 2x op->destroy();
297 2x *ec = pec;
298 2x *bytes_out = 0;
299 2x return cont.h;
300 }
301 85x return std::noop_coroutine();
302 91x }
303
304 // -- raf_op thread-pool work function --
305
306 inline void
307 420x posix_random_access_file::raf_op::do_work(pool_work_item* w) noexcept
308 {
309
1/2
✓ Branch 0 taken 420 times.
✗ Branch 1 not taken.
420x auto* op = static_cast<raf_op*>(w);
310 420x auto* self = op->file_;
311
312
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 316 times.
420x if (op->cancelled.load(std::memory_order_acquire))
313 {
314 104x op->errn = ECANCELED;
315 104x op->bytes_transferred = 0;
316 104x }
317 else if (
318
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 314 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 314 times.
632x op->offset >
319 316x static_cast<std::uint64_t>(std::numeric_limits<file_off_t>::max()))
320 {
321 2x op->errn = EOVERFLOW;
322 2x op->bytes_transferred = 0;
323 2x }
324 else
325 {
326 ssize_t n;
327
2/2
✓ Branch 0 taken 281 times.
✓ Branch 1 taken 33 times.
314x if (op->is_read)
328 {
329 281x do
330 {
331 281x n = file_preadv(
332 281x op->fd, op->iovecs, op->iovec_count,
333 281x static_cast<file_off_t>(op->offset));
334 562x }
335
3/4
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
281x while (n < 0 && errno == EINTR);
336 281x }
337 else
338 {
339 33x do
340 {
341 33x n = file_pwritev(
342 33x op->fd, op->iovecs, op->iovec_count,
343 33x static_cast<file_off_t>(op->offset));
344 66x }
345
3/4
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
33x while (n < 0 && errno == EINTR);
346 }
347
348
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 306 times.
314x if (n >= 0)
349 {
350 306x op->errn = 0;
351 306x op->bytes_transferred = static_cast<std::size_t>(n);
352 306x }
353 else
354 {
355
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8x op->errn = errno;
356 8x op->bytes_transferred = 0;
357 }
358 }
359
360
1/2
✓ Branch 0 taken 420 times.
✗ Branch 1 not taken.
420x self->svc_.post(static_cast<scheduler_op*>(op));
361 420x }
362
363 } // namespace boost::corosio::detail
364
365 #endif // BOOST_COROSIO_POSIX
366
367 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RANDOM_ACCESS_FILE_SERVICE_HPP
368