LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_random_access_file.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 90 90
Test Date: 2026-09-25 22:49:29 Functions: 100.0 % 14 14

           TLA  Line data    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_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RANDOM_ACCESS_FILE_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/platform.hpp>
      14                 : 
      15                 : #if BOOST_COROSIO_POSIX
      16                 : 
      17                 : #include <boost/corosio/detail/config.hpp>
      18                 : #include <boost/corosio/random_access_file.hpp>
      19                 : #include <boost/corosio/file_base.hpp>
      20                 : #include <boost/corosio/detail/intrusive.hpp>
      21                 : #include <boost/corosio/detail/scheduler_op.hpp>
      22                 : #include <boost/corosio/detail/thread_pool.hpp>
      23                 : #include <boost/corosio/detail/scheduler.hpp>
      24                 : #include <boost/corosio/detail/buffer_param.hpp>
      25                 : #include <boost/corosio/native/detail/coro_op.hpp>
      26                 : #include <boost/corosio/native/detail/coro_op_complete.hpp>
      27                 : #include <boost/corosio/native/detail/make_err.hpp>
      28                 : #include <boost/capy/ex/executor_ref.hpp>
      29                 : #include <boost/capy/error.hpp>
      30                 : #include <boost/capy/buffers.hpp>
      31                 : 
      32                 : #include <atomic>
      33                 : #include <coroutine>
      34                 : #include <cstddef>
      35                 : #include <cstdint>
      36                 : #include <filesystem>
      37                 : #include <limits>
      38                 : #include <memory>
      39                 : #include <mutex>
      40                 : #include <optional>
      41                 : #include <stop_token>
      42                 : #include <system_error>
      43                 : 
      44                 : #include <errno.h>
      45                 : #include <fcntl.h>
      46                 : #include <sys/stat.h>
      47                 : #include <sys/uio.h>
      48                 : #include <unistd.h>
      49                 : 
      50                 : /*
      51                 :     POSIX Random-Access File Implementation
      52                 :     ========================================
      53                 : 
      54                 :     Each async read/write heap-allocates an raf_op that serves
      55                 :     as both the thread-pool work item and the scheduler completion
      56                 :     op. This allows unlimited concurrent operations on the same
      57                 :     file object, matching Asio's per-op allocation model.
      58                 : 
      59                 :     The raf_op self-deletes on completion or shutdown.
      60                 : */
      61                 : 
      62                 : namespace boost::corosio::detail {
      63                 : 
      64                 : struct scheduler;
      65                 : class posix_random_access_file_service;
      66                 : 
      67                 : /** Random-access file implementation for POSIX backends. */
      68                 : class posix_random_access_file final
      69                 :     : public random_access_file::implementation
      70                 :     , public std::enable_shared_from_this<posix_random_access_file>
      71                 :     , public intrusive_list<posix_random_access_file>::node
      72                 : {
      73                 :     friend class posix_random_access_file_service;
      74                 : 
      75                 : public:
      76                 :     static constexpr std::size_t max_buffers = 16;
      77                 : 
      78                 :     /** Per-operation state, heap-allocated for each async call.
      79                 : 
      80                 :         Inherits from `coro_op` (for scheduler completion plus the shared
      81                 :         coroutine, cancellation and keepalive machinery) and
      82                 :         `pool_work_item` (for thread-pool dispatch). Linked into the
      83                 :         file's outstanding_ops_ list for cancellation tracking. `coro_op`
      84                 :         leads the base list so a `scheduler_op*` round-trips.
      85                 :     */
      86                 :     struct raf_op final
      87                 :         : coro_op
      88                 :         , pool_work_item
      89                 :         , intrusive_list<raf_op>::node
      90                 :     {
      91                 :         iovec iovecs[max_buffers];
      92                 :         int iovec_count      = 0;
      93                 :         std::uint64_t offset = 0;
      94                 : 
      95                 :         int errn                      = 0;
      96                 :         std::size_t bytes_transferred = 0;
      97                 : 
      98                 :         // Raw back-pointer for the typed work; `impl_ptr` is the keepalive.
      99                 :         posix_random_access_file* file_ = nullptr;
     100                 : 
     101                 :         void operator()() override;
     102                 :         void destroy() override;
     103                 : 
     104                 :         /// Thread-pool work function: executes preadv/pwritev.
     105                 :         static void do_work(pool_work_item*) noexcept;
     106                 :     };
     107                 : 
     108                 :     explicit posix_random_access_file(
     109                 :         posix_random_access_file_service& svc) noexcept;
     110                 : 
     111                 :     // -- random_access_file::implementation --
     112                 : 
     113                 :     std::coroutine_handle<> read_some_at(
     114                 :         std::uint64_t offset,
     115                 :         std::coroutine_handle<>,
     116                 :         capy::executor_ref,
     117                 :         buffer_param,
     118                 :         std::stop_token,
     119                 :         std::error_code*,
     120                 :         std::size_t*) override;
     121                 : 
     122                 :     std::coroutine_handle<> write_some_at(
     123                 :         std::uint64_t offset,
     124                 :         std::coroutine_handle<>,
     125                 :         capy::executor_ref,
     126                 :         buffer_param,
     127                 :         std::stop_token,
     128                 :         std::error_code*,
     129                 :         std::size_t*) override;
     130                 : 
     131 HIT        1082 :     native_handle_type native_handle() const noexcept override
     132                 :     {
     133            1082 :         return fd_;
     134                 :     }
     135                 : 
     136             620 :     void cancel() noexcept override
     137                 :     {
     138             620 :         std::lock_guard<std::mutex> lock(ops_mutex_);
     139             620 :         outstanding_ops_.for_each([](raf_op* op) {
     140               6 :             op->cancelled.store(true, std::memory_order_release);
     141               6 :         });
     142             620 :     }
     143                 : 
     144                 :     std::uint64_t size() const override;
     145                 :     std::error_code resize(std::uint64_t new_size) noexcept override;
     146                 :     std::error_code sync_data() noexcept override;
     147                 :     std::error_code sync_all() noexcept override;
     148                 :     native_handle_type release() override;
     149                 :     std::error_code assign(native_handle_type handle) noexcept override;
     150                 : 
     151                 :     std::error_code
     152                 :     open_file(std::filesystem::path const& path, file_base::flags mode);
     153                 :     void close_file() noexcept;
     154                 : 
     155                 : private:
     156                 :     posix_random_access_file_service& svc_;
     157                 :     int fd_ = -1;
     158                 :     std::mutex ops_mutex_;
     159                 :     intrusive_list<raf_op> outstanding_ops_;
     160                 : };
     161                 : 
     162                 : // ---------------------------------------------------------------------------
     163                 : // Inline implementation
     164                 : // ---------------------------------------------------------------------------
     165                 : 
     166             213 : inline posix_random_access_file::posix_random_access_file(
     167             213 :     posix_random_access_file_service& svc) noexcept
     168             213 :     : svc_(svc)
     169                 : {
     170             213 : }
     171                 : 
     172                 : inline std::error_code
     173             199 : posix_random_access_file::open_file(
     174                 :     std::filesystem::path const& path, file_base::flags mode)
     175                 : {
     176             199 :     close_file();
     177                 : 
     178             199 :     int oflags = 0;
     179                 : 
     180             199 :     unsigned access = static_cast<unsigned>(mode) & 3u;
     181             199 :     if (access == static_cast<unsigned>(file_base::read_write))
     182              31 :         oflags |= O_RDWR;
     183             168 :     else if (access == static_cast<unsigned>(file_base::write_only))
     184              64 :         oflags |= O_WRONLY;
     185                 :     else
     186             104 :         oflags |= O_RDONLY;
     187                 : 
     188             199 :     if ((mode & file_base::create) != file_base::flags(0))
     189              28 :         oflags |= O_CREAT;
     190             199 :     if ((mode & file_base::exclusive) != file_base::flags(0))
     191               4 :         oflags |= O_EXCL;
     192             199 :     if ((mode & file_base::truncate) != file_base::flags(0))
     193              14 :         oflags |= O_TRUNC;
     194             199 :     if ((mode & file_base::sync_all_on_write) != file_base::flags(0))
     195               2 :         oflags |= O_SYNC;
     196                 :     // Note: no O_APPEND for random access files
     197                 : 
     198             199 :     int fd = ::open(path.c_str(), oflags, 0666);
     199             199 :     if (fd < 0)
     200               9 :         return make_err(errno);
     201                 : 
     202             190 :     fd_ = fd;
     203                 : 
     204                 : #ifdef POSIX_FADV_RANDOM
     205             190 :     ::posix_fadvise(fd_, 0, 0, POSIX_FADV_RANDOM);
     206                 : #endif
     207                 : 
     208             190 :     return {};
     209                 : }
     210                 : 
     211                 : inline void
     212             822 : posix_random_access_file::close_file() noexcept
     213                 : {
     214             822 :     if (fd_ >= 0)
     215                 :     {
     216             194 :         ::close(fd_);
     217             194 :         fd_ = -1;
     218                 :     }
     219             822 : }
     220                 : 
     221                 : inline std::uint64_t
     222              13 : posix_random_access_file::size() const
     223                 : {
     224                 :     struct stat st;
     225              13 :     if (::fstat(fd_, &st) < 0)
     226               5 :         throw_system_error(make_err(errno), "random_access_file::size");
     227               8 :     return static_cast<std::uint64_t>(st.st_size);
     228                 : }
     229                 : 
     230                 : inline std::error_code
     231              13 : posix_random_access_file::resize(std::uint64_t new_size) noexcept
     232                 : {
     233              13 :     if (new_size >
     234              13 :         static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)()))
     235               2 :         return make_err(EOVERFLOW);
     236              11 :     if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0)
     237               7 :         return make_err(errno);
     238               4 :     return {};
     239                 : }
     240                 : 
     241                 : inline std::error_code
     242               9 : posix_random_access_file::sync_data() noexcept
     243                 : {
     244                 : #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
     245               9 :     if (::fdatasync(fd_) < 0)
     246                 : #else  // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
     247                 :     if (::fsync(fd_) < 0)
     248                 : #endif // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
     249               7 :         return make_err(errno);
     250               2 :     return {};
     251                 : }
     252                 : 
     253                 : inline std::error_code
     254               9 : posix_random_access_file::sync_all() noexcept
     255                 : {
     256               9 :     if (::fsync(fd_) < 0)
     257               7 :         return make_err(errno);
     258               2 :     return {};
     259                 : }
     260                 : 
     261                 : inline native_handle_type
     262               3 : posix_random_access_file::release()
     263                 : {
     264               3 :     int fd = fd_;
     265               3 :     fd_    = -1;
     266               3 :     return fd;
     267                 : }
     268                 : 
     269                 : inline std::error_code
     270               7 : posix_random_access_file::assign(native_handle_type handle) noexcept
     271                 : {
     272               7 :     close_file();
     273               7 :     fd_ = handle;
     274               7 :     return {};
     275                 : }
     276                 : 
     277                 : // read_some_at, write_some_at are defined in
     278                 : // posix_random_access_file_service.hpp after the service.
     279                 : 
     280                 : // -- raf_op completion handler (scheduler thread) --
     281                 : 
     282                 : inline void
     283             420 : posix_random_access_file::raf_op::operator()()
     284                 : {
     285             420 :     stop_cb.reset();
     286                 : 
     287                 :     // Empty buffers never reach the pool (diverted at initiation), so
     288                 :     // empty_buffer stays false and a 0-byte read is a genuine EOF.
     289             770 :     decode_io_result(
     290             420 :         ec_out, bytes_out, cancelled.load(std::memory_order_acquire),
     291             420 :         errn != 0 ? make_err(errn) : std::error_code{}, is_read,
     292                 :         bytes_transferred, /*empty_buffer=*/false);
     293                 : 
     294                 :     {
     295             420 :         std::lock_guard<std::mutex> lock(file_->ops_mutex_);
     296             420 :         file_->outstanding_ops_.remove(this);
     297             420 :     }
     298                 : 
     299             420 :     impl_ptr.reset();
     300                 : 
     301             420 :     auto coro = h;
     302             420 :     ex.on_work_finished();
     303             420 :     delete this;
     304             420 :     coro.resume();
     305             420 : }
     306                 : 
     307                 : // -- raf_op shutdown cleanup --
     308                 : 
     309                 : inline void
     310               6 : posix_random_access_file::raf_op::destroy()
     311                 : {
     312               6 :     stop_cb.reset();
     313                 :     {
     314               6 :         std::lock_guard<std::mutex> lock(file_->ops_mutex_);
     315               6 :         file_->outstanding_ops_.remove(this);
     316               6 :     }
     317               6 :     impl_ptr.reset();
     318               6 :     ex.on_work_finished();
     319               6 :     delete this;
     320               6 : }
     321                 : 
     322                 : } // namespace boost::corosio::detail
     323                 : 
     324                 : #endif // BOOST_COROSIO_POSIX
     325                 : 
     326                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RANDOM_ACCESS_FILE_HPP
        

Generated by: LCOV version 2.3