LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_random_access_file.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 88.8 % 107 95 12
Test Date: 2026-08-21 20:48:07 Functions: 93.8 % 16 15 1

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

Generated by: LCOV version 2.3