100.00% Lines (41/41) 100.00% Functions (12/12)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
13   13  
14   #include <boost/corosio/random_access_file.hpp> 14   #include <boost/corosio/random_access_file.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   16  
17   #ifndef BOOST_COROSIO_MRDOCS 17   #ifndef BOOST_COROSIO_MRDOCS
18   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \ 18   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
19   BOOST_COROSIO_HAS_KQUEUE 19   BOOST_COROSIO_HAS_KQUEUE
20   #include <boost/corosio/native/detail/posix/posix_random_access_file_service.hpp> 20   #include <boost/corosio/native/detail/posix/posix_random_access_file_service.hpp>
21   #endif 21   #endif
22   22  
23 - #if BOOST_COROSIO_HAS_URING 23 + #if BOOST_COROSIO_HAS_IO_URING
24 - #include <boost/corosio/native/detail/uring/uring_random_access_file.hpp> 24 + #include <boost/corosio/native/detail/io_uring/io_uring_random_access_file.hpp>
25   #endif 25   #endif
26   26  
27   #if BOOST_COROSIO_HAS_IOCP 27   #if BOOST_COROSIO_HAS_IOCP
28   #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp> 28   #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp>
29   #endif 29   #endif
30   #endif // !BOOST_COROSIO_MRDOCS 30   #endif // !BOOST_COROSIO_MRDOCS
31   31  
32   namespace boost::corosio { 32   namespace boost::corosio {
33   33  
34   /** A random-access file with devirtualized async I/O operations. 34   /** A random-access file with devirtualized async I/O operations.
35   35  
36   This class template inherits from @ref random_access_file and 36   This class template inherits from @ref random_access_file and
37   shadows `read_some_at` / `write_some_at` with versions that 37   shadows `read_some_at` / `write_some_at` with versions that
38   call the backend implementation directly, allowing the compiler 38   call the backend implementation directly, allowing the compiler
39   to inline through the entire call chain. 39   to inline through the entire call chain.
40   40  
41   Non-async operations (`open`, `close`, `size`, `resize`, 41   Non-async operations (`open`, `close`, `size`, `resize`,
42   `sync_data`, `sync_all`) remain unchanged and dispatch through 42   `sync_data`, `sync_all`) remain unchanged and dispatch through
43   the compiled library. 43   the compiled library.
44   44  
45   A `native_random_access_file` IS-A `random_access_file` and 45   A `native_random_access_file` IS-A `random_access_file` and
46   can be passed to any function expecting `random_access_file&`, 46   can be passed to any function expecting `random_access_file&`,
47   in which case virtual dispatch is used transparently. 47   in which case virtual dispatch is used transparently.
48   48  
49   @note On POSIX platforms, file I/O is dispatched to a thread 49   @note On POSIX platforms, file I/O is dispatched to a thread
50   pool regardless of the chosen reactor backend, so all three 50   pool regardless of the chosen reactor backend, so all three
51   reactor tags (`epoll`, `select`, `kqueue`) resolve to the same 51   reactor tags (`epoll`, `select`, `kqueue`) resolve to the same
52   underlying implementation. The `Backend` template parameter 52   underlying implementation. The `Backend` template parameter
53   exists for API symmetry with @ref native_tcp_socket and friends. 53   exists for API symmetry with @ref native_tcp_socket and friends.
54   The vtable savings are smaller relative to the thread-pool / 54   The vtable savings are smaller relative to the thread-pool /
55   overlapped-I/O cost than they are for socket operations. 55   overlapped-I/O cost than they are for socket operations.
56   56  
57   @tparam Backend A backend tag value (e.g., `epoll`, `iocp`). 57   @tparam Backend A backend tag value (e.g., `epoll`, `iocp`).
58   58  
59   @par Thread Safety 59   @par Thread Safety
60   Same as @ref random_access_file. 60   Same as @ref random_access_file.
61   61  
62   @par Example 62   @par Example
63   @par !example native_random_access_file 63   @par !example native_random_access_file
64   64  
65   @see random_access_file, epoll_t, iocp_t 65   @see random_access_file, epoll_t, iocp_t
66   */ 66   */
67   template<auto Backend> 67   template<auto Backend>
68   class native_random_access_file : public random_access_file 68   class native_random_access_file : public random_access_file
69   { 69   {
70   using backend_type = decltype(Backend); 70   using backend_type = decltype(Backend);
71   using impl_type = typename backend_type::random_access_file_type; 71   using impl_type = typename backend_type::random_access_file_type;
72 - using service_type = typename backend_type::random_access_file_service_type; 72 + using service_type =
  73 + typename backend_type::random_access_file_service_type;
73   74  
HITCBC 74   14 impl_type& get_impl() noexcept 75   14 impl_type& get_impl() noexcept
75   { 76   {
HITCBC 76   14 return *static_cast<impl_type*>(h_.get()); 77   14 return *static_cast<impl_type*>(h_.get());
77   } 78   }
78   79  
79   template<class MutableBufferSequence> 80   template<class MutableBufferSequence>
80   struct native_read_at_awaitable 81   struct native_read_at_awaitable
81   { 82   {
82   native_random_access_file& self_; 83   native_random_access_file& self_;
83   std::uint64_t offset_; 84   std::uint64_t offset_;
84   MutableBufferSequence buffers_; 85   MutableBufferSequence buffers_;
85   std::stop_token token_; 86   std::stop_token token_;
86   mutable std::error_code ec_; 87   mutable std::error_code ec_;
87   mutable std::size_t bytes_transferred_ = 0; 88   mutable std::size_t bytes_transferred_ = 0;
88   89  
HITCBC 89   8 native_read_at_awaitable( 90   8 native_read_at_awaitable(
90   native_random_access_file& self, 91   native_random_access_file& self,
91   std::uint64_t offset, 92   std::uint64_t offset,
92   MutableBufferSequence buffers) noexcept 93   MutableBufferSequence buffers) noexcept
HITCBC 93   8 : self_(self) 94   8 : self_(self)
HITCBC 94   8 , offset_(offset) 95   8 , offset_(offset)
HITCBC 95   8 , buffers_(std::move(buffers)) 96   8 , buffers_(std::move(buffers))
96   { 97   {
HITCBC 97   8 } 98   8 }
98   99  
HITCBC 99   8 bool await_ready() const noexcept 100   8 bool await_ready() const noexcept
100   { 101   {
101   // A pre-set ec_ means the initiator failed before 102   // A pre-set ec_ means the initiator failed before
102   // dispatch (e.g. a closed object). 103   // dispatch (e.g. a closed object).
HITCBC 103   8 return static_cast<bool>(ec_) || token_.stop_requested(); 104   8 return static_cast<bool>(ec_) || token_.stop_requested();
104   } 105   }
105   106  
HITCBC 106   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 107   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
107   { 108   {
HITCBC 108   8 if (token_.stop_requested()) 109   8 if (token_.stop_requested())
HITCBC 109   2 return {make_error_code(std::errc::operation_canceled), 0}; 110   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 110   6 return {ec_, bytes_transferred_}; 111   6 return {ec_, bytes_transferred_};
111   } 112   }
112   113  
HITCBC 113   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 114   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
114   -> std::coroutine_handle<> 115   -> std::coroutine_handle<>
115   { 116   {
HITCBC 116   8 token_ = env->stop_token; 117   8 token_ = env->stop_token;
HITCBC 117   24 return self_.get_impl().read_some_at( 118   24 return self_.get_impl().read_some_at(
HITCBC 118 - 8 offset_, h, env->executor, buffers_, token_, &ec_, 119 + 8 offset_, h, env->executor, buffers_,
HITCBC 119 - 16 &bytes_transferred_); 120 + 24 token_, &ec_, &bytes_transferred_);
120   } 121   }
121   }; 122   };
122   123  
123   template<class ConstBufferSequence> 124   template<class ConstBufferSequence>
124   struct native_write_at_awaitable 125   struct native_write_at_awaitable
125   { 126   {
126   native_random_access_file& self_; 127   native_random_access_file& self_;
127   std::uint64_t offset_; 128   std::uint64_t offset_;
128   ConstBufferSequence buffers_; 129   ConstBufferSequence buffers_;
129   std::stop_token token_; 130   std::stop_token token_;
130   mutable std::error_code ec_; 131   mutable std::error_code ec_;
131   mutable std::size_t bytes_transferred_ = 0; 132   mutable std::size_t bytes_transferred_ = 0;
132   133  
HITCBC 133   6 native_write_at_awaitable( 134   6 native_write_at_awaitable(
134   native_random_access_file& self, 135   native_random_access_file& self,
135   std::uint64_t offset, 136   std::uint64_t offset,
136   ConstBufferSequence buffers) noexcept 137   ConstBufferSequence buffers) noexcept
HITCBC 137   6 : self_(self) 138   6 : self_(self)
HITCBC 138   6 , offset_(offset) 139   6 , offset_(offset)
HITCBC 139   6 , buffers_(std::move(buffers)) 140   6 , buffers_(std::move(buffers))
140   { 141   {
HITCBC 141   6 } 142   6 }
142   143  
HITCBC 143   6 bool await_ready() const noexcept 144   6 bool await_ready() const noexcept
144   { 145   {
145   // A pre-set ec_ means the initiator failed before 146   // A pre-set ec_ means the initiator failed before
146   // dispatch (e.g. a closed object). 147   // dispatch (e.g. a closed object).
HITCBC 147   6 return static_cast<bool>(ec_) || token_.stop_requested(); 148   6 return static_cast<bool>(ec_) || token_.stop_requested();
148   } 149   }
149   150  
HITCBC 150   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 151   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
151   { 152   {
HITCBC 152   6 if (token_.stop_requested()) 153   6 if (token_.stop_requested())
HITCBC 153   2 return {make_error_code(std::errc::operation_canceled), 0}; 154   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 154   4 return {ec_, bytes_transferred_}; 155   4 return {ec_, bytes_transferred_};
155   } 156   }
156   157  
HITCBC 157   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 158   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
158   -> std::coroutine_handle<> 159   -> std::coroutine_handle<>
159   { 160   {
HITCBC 160   6 token_ = env->stop_token; 161   6 token_ = env->stop_token;
HITCBC 161   18 return self_.get_impl().write_some_at( 162   18 return self_.get_impl().write_some_at(
HITCBC 162 - 6 offset_, h, env->executor, buffers_, token_, &ec_, 163 + 6 offset_, h, env->executor, buffers_,
HITCBC 163 - 12 &bytes_transferred_); 164 + 18 token_, &ec_, &bytes_transferred_);
164   } 165   }
165   }; 166   };
166   167  
167   public: 168   public:
168   /** Construct a native random-access file from an execution context. 169   /** Construct a native random-access file from an execution context.
169   170  
170   @param ctx The execution context that will own this file. 171   @param ctx The execution context that will own this file.
171   */ 172   */
HITCBC 172   16 explicit native_random_access_file(capy::execution_context& ctx) 173   16 explicit native_random_access_file(capy::execution_context& ctx)
HITCBC 173   16 : random_access_file(create_handle<service_type>(ctx)) 174   16 : random_access_file(create_handle<service_type>(ctx))
174   { 175   {
HITCBC 175   16 } 176   16 }
176   177  
177   /** Construct a native random-access file from an executor. 178   /** Construct a native random-access file from an executor.
178   179  
179   @param ex The executor whose context will own this file. 180   @param ex The executor whose context will own this file.
180   */ 181   */
181   template<class Ex> 182   template<class Ex>
182   requires(!std::same_as< 183   requires(!std::same_as<
183 - std::remove_cvref_t<Ex>, 184 + std::remove_cvref_t<Ex>,
184 - native_random_access_file>) && 185 + native_random_access_file>) &&
185   capy::Executor<Ex> 186   capy::Executor<Ex>
186   explicit native_random_access_file(Ex const& ex) 187   explicit native_random_access_file(Ex const& ex)
187   : native_random_access_file(ex.context()) 188   : native_random_access_file(ex.context())
188   { 189   {
189   } 190   }
190   191  
191   /// Move construct. 192   /// Move construct.
192   native_random_access_file(native_random_access_file&&) noexcept = default; 193   native_random_access_file(native_random_access_file&&) noexcept = default;
193   194  
194   /// Move assign. 195   /// Move assign.
195   native_random_access_file& 196   native_random_access_file&
196   operator=(native_random_access_file&&) noexcept = default; 197   operator=(native_random_access_file&&) noexcept = default;
197   198  
198   native_random_access_file(native_random_access_file const&) = delete; 199   native_random_access_file(native_random_access_file const&) = delete;
199   native_random_access_file& 200   native_random_access_file&
200   operator=(native_random_access_file const&) = delete; 201   operator=(native_random_access_file const&) = delete;
201   202  
202   /** Asynchronously read at the given offset. 203   /** Asynchronously read at the given offset.
203   204  
204   Calls the backend implementation directly, bypassing virtual 205   Calls the backend implementation directly, bypassing virtual
205   dispatch. Otherwise identical to @ref random_access_file::read_some_at. 206   dispatch. Otherwise identical to @ref random_access_file::read_some_at.
206   */ 207   */
207   template<capy::MutableBufferSequence MB> 208   template<capy::MutableBufferSequence MB>
HITCBC 208   8 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 209   8 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
209   { 210   {
HITCBC 210   8 return native_read_at_awaitable<MB>(*this, offset, buffers); 211   8 return native_read_at_awaitable<MB>(*this, offset, buffers);
211   } 212   }
212   213  
213   /** Asynchronously write at the given offset. 214   /** Asynchronously write at the given offset.
214   215  
215   Calls the backend implementation directly, bypassing virtual 216   Calls the backend implementation directly, bypassing virtual
216   dispatch. Otherwise identical to @ref random_access_file::write_some_at. 217   dispatch. Otherwise identical to @ref random_access_file::write_some_at.
217   */ 218   */
218   template<capy::ConstBufferSequence CB> 219   template<capy::ConstBufferSequence CB>
HITCBC 219   6 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 220   6 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
220   { 221   {
HITCBC 221   6 return native_write_at_awaitable<CB>(*this, offset, buffers); 222   6 return native_write_at_awaitable<CB>(*this, offset, buffers);
222   } 223   }
223   }; 224   };
224   225  
225   } // namespace boost::corosio 226   } // namespace boost::corosio
226   227  
227   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 228   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP