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_IO_URING 23 + #if BOOST_COROSIO_HAS_URING
24 - #include <boost/corosio/native/detail/io_uring/io_uring_random_access_file.hpp> 24 + #include <boost/corosio/native/detail/uring/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 = 72 + using service_type = typename backend_type::random_access_file_service_type;
73 - typename backend_type::random_access_file_service_type;  
74   73  
HITCBC 75   14 impl_type& get_impl() noexcept 74   14 impl_type& get_impl() noexcept
76   { 75   {
HITCBC 77   14 return *static_cast<impl_type*>(h_.get()); 76   14 return *static_cast<impl_type*>(h_.get());
78   } 77   }
79   78  
80   template<class MutableBufferSequence> 79   template<class MutableBufferSequence>
81   struct native_read_at_awaitable 80   struct native_read_at_awaitable
82   { 81   {
83   native_random_access_file& self_; 82   native_random_access_file& self_;
84   std::uint64_t offset_; 83   std::uint64_t offset_;
85   MutableBufferSequence buffers_; 84   MutableBufferSequence buffers_;
86   std::stop_token token_; 85   std::stop_token token_;
87   mutable std::error_code ec_; 86   mutable std::error_code ec_;
88   mutable std::size_t bytes_transferred_ = 0; 87   mutable std::size_t bytes_transferred_ = 0;
89   88  
HITCBC 90   8 native_read_at_awaitable( 89   8 native_read_at_awaitable(
91   native_random_access_file& self, 90   native_random_access_file& self,
92   std::uint64_t offset, 91   std::uint64_t offset,
93   MutableBufferSequence buffers) noexcept 92   MutableBufferSequence buffers) noexcept
HITCBC 94   8 : self_(self) 93   8 : self_(self)
HITCBC 95   8 , offset_(offset) 94   8 , offset_(offset)
HITCBC 96   8 , buffers_(std::move(buffers)) 95   8 , buffers_(std::move(buffers))
97   { 96   {
HITCBC 98   8 } 97   8 }
99   98  
HITCBC 100   8 bool await_ready() const noexcept 99   8 bool await_ready() const noexcept
101   { 100   {
102   // A pre-set ec_ means the initiator failed before 101   // A pre-set ec_ means the initiator failed before
103   // dispatch (e.g. a closed object). 102   // dispatch (e.g. a closed object).
HITCBC 104   8 return static_cast<bool>(ec_) || token_.stop_requested(); 103   8 return static_cast<bool>(ec_) || token_.stop_requested();
105   } 104   }
106   105  
HITCBC 107   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 106   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
108   { 107   {
HITCBC 109   8 if (token_.stop_requested()) 108   8 if (token_.stop_requested())
HITCBC 110   2 return {make_error_code(std::errc::operation_canceled), 0}; 109   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 111   6 return {ec_, bytes_transferred_}; 110   6 return {ec_, bytes_transferred_};
112   } 111   }
113   112  
HITCBC 114   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 113   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
115   -> std::coroutine_handle<> 114   -> std::coroutine_handle<>
116   { 115   {
HITCBC 117   8 token_ = env->stop_token; 116   8 token_ = env->stop_token;
HITCBC 118   24 return self_.get_impl().read_some_at( 117   24 return self_.get_impl().read_some_at(
HITCBC 119 - 8 offset_, h, env->executor, buffers_, 118 + 8 offset_, h, env->executor, buffers_, token_, &ec_,
HITCBC 120 - 24 token_, &ec_, &bytes_transferred_); 119 + 16 &bytes_transferred_);
121   } 120   }
122   }; 121   };
123   122  
124   template<class ConstBufferSequence> 123   template<class ConstBufferSequence>
125   struct native_write_at_awaitable 124   struct native_write_at_awaitable
126   { 125   {
127   native_random_access_file& self_; 126   native_random_access_file& self_;
128   std::uint64_t offset_; 127   std::uint64_t offset_;
129   ConstBufferSequence buffers_; 128   ConstBufferSequence buffers_;
130   std::stop_token token_; 129   std::stop_token token_;
131   mutable std::error_code ec_; 130   mutable std::error_code ec_;
132   mutable std::size_t bytes_transferred_ = 0; 131   mutable std::size_t bytes_transferred_ = 0;
133   132  
HITCBC 134   6 native_write_at_awaitable( 133   6 native_write_at_awaitable(
135   native_random_access_file& self, 134   native_random_access_file& self,
136   std::uint64_t offset, 135   std::uint64_t offset,
137   ConstBufferSequence buffers) noexcept 136   ConstBufferSequence buffers) noexcept
HITCBC 138   6 : self_(self) 137   6 : self_(self)
HITCBC 139   6 , offset_(offset) 138   6 , offset_(offset)
HITCBC 140   6 , buffers_(std::move(buffers)) 139   6 , buffers_(std::move(buffers))
141   { 140   {
HITCBC 142   6 } 141   6 }
143   142  
HITCBC 144   6 bool await_ready() const noexcept 143   6 bool await_ready() const noexcept
145   { 144   {
146   // A pre-set ec_ means the initiator failed before 145   // A pre-set ec_ means the initiator failed before
147   // dispatch (e.g. a closed object). 146   // dispatch (e.g. a closed object).
HITCBC 148   6 return static_cast<bool>(ec_) || token_.stop_requested(); 147   6 return static_cast<bool>(ec_) || token_.stop_requested();
149   } 148   }
150   149  
HITCBC 151   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 150   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
152   { 151   {
HITCBC 153   6 if (token_.stop_requested()) 152   6 if (token_.stop_requested())
HITCBC 154   2 return {make_error_code(std::errc::operation_canceled), 0}; 153   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 155   4 return {ec_, bytes_transferred_}; 154   4 return {ec_, bytes_transferred_};
156   } 155   }
157   156  
HITCBC 158   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 157   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
159   -> std::coroutine_handle<> 158   -> std::coroutine_handle<>
160   { 159   {
HITCBC 161   6 token_ = env->stop_token; 160   6 token_ = env->stop_token;
HITCBC 162   18 return self_.get_impl().write_some_at( 161   18 return self_.get_impl().write_some_at(
HITCBC 163 - 6 offset_, h, env->executor, buffers_, 162 + 6 offset_, h, env->executor, buffers_, token_, &ec_,
HITCBC 164 - 18 token_, &ec_, &bytes_transferred_); 163 + 12 &bytes_transferred_);
165   } 164   }
166   }; 165   };
167   166  
168   public: 167   public:
169   /** Construct a native random-access file from an execution context. 168   /** Construct a native random-access file from an execution context.
170   169  
171   @param ctx The execution context that will own this file. 170   @param ctx The execution context that will own this file.
172   */ 171   */
HITCBC 173   16 explicit native_random_access_file(capy::execution_context& ctx) 172   16 explicit native_random_access_file(capy::execution_context& ctx)
HITCBC 174   16 : random_access_file(create_handle<service_type>(ctx)) 173   16 : random_access_file(create_handle<service_type>(ctx))
175   { 174   {
HITCBC 176   16 } 175   16 }
177   176  
178   /** Construct a native random-access file from an executor. 177   /** Construct a native random-access file from an executor.
179   178  
180   @param ex The executor whose context will own this file. 179   @param ex The executor whose context will own this file.
181   */ 180   */
182   template<class Ex> 181   template<class Ex>
183   requires(!std::same_as< 182   requires(!std::same_as<
184 - std::remove_cvref_t<Ex>, 183 + std::remove_cvref_t<Ex>,
185 - native_random_access_file>) && 184 + native_random_access_file>) &&
186   capy::Executor<Ex> 185   capy::Executor<Ex>
187   explicit native_random_access_file(Ex const& ex) 186   explicit native_random_access_file(Ex const& ex)
188   : native_random_access_file(ex.context()) 187   : native_random_access_file(ex.context())
189   { 188   {
190   } 189   }
191   190  
192   /// Move construct. 191   /// Move construct.
193   native_random_access_file(native_random_access_file&&) noexcept = default; 192   native_random_access_file(native_random_access_file&&) noexcept = default;
194   193  
195   /// Move assign. 194   /// Move assign.
196   native_random_access_file& 195   native_random_access_file&
197   operator=(native_random_access_file&&) noexcept = default; 196   operator=(native_random_access_file&&) noexcept = default;
198   197  
199   native_random_access_file(native_random_access_file const&) = delete; 198   native_random_access_file(native_random_access_file const&) = delete;
200   native_random_access_file& 199   native_random_access_file&
201   operator=(native_random_access_file const&) = delete; 200   operator=(native_random_access_file const&) = delete;
202   201  
203   /** Asynchronously read at the given offset. 202   /** Asynchronously read at the given offset.
204   203  
205   Calls the backend implementation directly, bypassing virtual 204   Calls the backend implementation directly, bypassing virtual
206   dispatch. Otherwise identical to @ref random_access_file::read_some_at. 205   dispatch. Otherwise identical to @ref random_access_file::read_some_at.
207   */ 206   */
208   template<capy::MutableBufferSequence MB> 207   template<capy::MutableBufferSequence MB>
HITCBC 209   8 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 208   8 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
210   { 209   {
HITCBC 211   8 return native_read_at_awaitable<MB>(*this, offset, buffers); 210   8 return native_read_at_awaitable<MB>(*this, offset, buffers);
212   } 211   }
213   212  
214   /** Asynchronously write at the given offset. 213   /** Asynchronously write at the given offset.
215   214  
216   Calls the backend implementation directly, bypassing virtual 215   Calls the backend implementation directly, bypassing virtual
217   dispatch. Otherwise identical to @ref random_access_file::write_some_at. 216   dispatch. Otherwise identical to @ref random_access_file::write_some_at.
218   */ 217   */
219   template<capy::ConstBufferSequence CB> 218   template<capy::ConstBufferSequence CB>
HITCBC 220   6 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 219   6 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
221   { 220   {
HITCBC 222   6 return native_write_at_awaitable<CB>(*this, offset, buffers); 221   6 return native_write_at_awaitable<CB>(*this, offset, buffers);
223   } 222   }
224   }; 223   };
225   224  
226   } // namespace boost::corosio 225   } // namespace boost::corosio
227   226  
228   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 227   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP