100.00% Lines (40/40) 100.00% Functions (11/11)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 10   #ifndef BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP
11   #define BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 11   #define BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   #include <boost/corosio/detail/except.hpp> 15   #include <boost/corosio/detail/except.hpp>
16   #include <boost/corosio/detail/native_handle.hpp> 16   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/buffer_param.hpp> 17   #include <boost/corosio/detail/buffer_param.hpp>
18   #include <boost/corosio/detail/op_base.hpp> 18   #include <boost/corosio/detail/op_base.hpp>
19   #include <boost/corosio/error.hpp> 19   #include <boost/corosio/error.hpp>
20   #include <boost/corosio/file_base.hpp> 20   #include <boost/corosio/file_base.hpp>
21   #include <boost/corosio/io/io_object.hpp> 21   #include <boost/corosio/io/io_object.hpp>
22   #include <boost/capy/continuation.hpp> 22   #include <boost/capy/continuation.hpp>
23   #include <boost/capy/io_result.hpp> 23   #include <boost/capy/io_result.hpp>
24   #include <boost/capy/ex/executor_ref.hpp> 24   #include <boost/capy/ex/executor_ref.hpp>
25   #include <boost/capy/ex/execution_context.hpp> 25   #include <boost/capy/ex/execution_context.hpp>
26   #include <boost/capy/ex/io_env.hpp> 26   #include <boost/capy/ex/io_env.hpp>
27   #include <boost/capy/concept/executor.hpp> 27   #include <boost/capy/concept/executor.hpp>
28   #include <boost/capy/buffers.hpp> 28   #include <boost/capy/buffers.hpp>
29   29  
30   #include <concepts> 30   #include <concepts>
31   #include <coroutine> 31   #include <coroutine>
32   #include <cstddef> 32   #include <cstddef>
33   #include <cstdint> 33   #include <cstdint>
34   #include <type_traits> 34   #include <type_traits>
35   #include <filesystem> 35   #include <filesystem>
36   #include <stop_token> 36   #include <stop_token>
37   #include <system_error> 37   #include <system_error>
38   38  
39   namespace boost::corosio { 39   namespace boost::corosio {
40   40  
41   /** Reads and writes a file at arbitrary offsets, from a coroutine. 41   /** Reads and writes a file at arbitrary offsets, from a coroutine.
42   42  
43   Provides asynchronous read and write operations at explicit 43   Provides asynchronous read and write operations at explicit
44   byte offsets, without maintaining an implicit file position. 44   byte offsets, without maintaining an implicit file position.
45   45  
46   On POSIX platforms, file I/O is dispatched to a thread pool 46   On POSIX platforms, file I/O is dispatched to a thread pool
47   (blocking `preadv`/`pwritev`) with completion posted back to 47   (blocking `preadv`/`pwritev`) with completion posted back to
48   the scheduler. On Windows, true overlapped I/O is used via IOCP. 48   the scheduler. On Windows, true overlapped I/O is used via IOCP.
49   49  
50   On Windows, while the file is open, its handle is bound to the 50   On Windows, while the file is open, its handle is bound to the
51   execution context's completion port. Every overlapped call on the 51   execution context's completion port. Every overlapped call on the
52   handle queues a packet to that port. Do not issue your own 52   handle queues a packet to that port. Do not issue your own
53   overlapped I/O on `native_handle()` (`DeviceIoControl`, 53   overlapped I/O on `native_handle()` (`DeviceIoControl`,
54   `ReadFile`) unless the `OVERLAPPED`'s `hEvent` has its low-order 54   `ReadFile`) unless the `OVERLAPPED`'s `hEvent` has its low-order
55   bit set, which suppresses the packet. 55   bit set, which suppresses the packet.
56   56  
57   @par Thread Safety 57   @par Thread Safety
58   Distinct objects: Safe.@n 58   Distinct objects: Safe.@n
59   Shared objects: Unsafe. Coroutines sharing the same file object may 59   Shared objects: Unsafe. Coroutines sharing the same file object may
60   run multiple concurrent reads and writes. Non-async operations such as open, close, size, and resize require external synchronization. 60   run multiple concurrent reads and writes. Non-async operations such as open, close, size, and resize require external synchronization.
61   61  
62   @par Example 62   @par Example
63   @par !example random_access_file 63   @par !example random_access_file
64   */ 64   */
65   class BOOST_COROSIO_DECL random_access_file : public io_object 65   class BOOST_COROSIO_DECL random_access_file : public io_object
66   { 66   {
67   public: 67   public:
68   /** Declares the offset-based file operations a platform backend 68   /** Declares the offset-based file operations a platform backend
69   must implement. 69   must implement.
70   70  
71   Backends derive from this to provide offset-based file I/O. 71   Backends derive from this to provide offset-based file I/O.
72   */ 72   */
73   struct implementation : io_object::implementation 73   struct implementation : io_object::implementation
74   { 74   {
75   /** Initiate a read at the given offset. 75   /** Initiate a read at the given offset.
76   76  
77   @param offset Byte offset into the file. 77   @param offset Byte offset into the file.
78   @param cont The awaiting coroutine's continuation. It must 78   @param cont The awaiting coroutine's continuation. It must
79   stay valid until `cont.h` is resumed through @p ex. 79   stay valid until `cont.h` is resumed through @p ex.
80   @param ex Executor for dispatching the completion. 80   @param ex Executor for dispatching the completion.
81   @param buf The buffer to read into. 81   @param buf The buffer to read into.
82   @param token Stop token for cancellation. 82   @param token Stop token for cancellation.
83   @param ec Output error code. 83   @param ec Output error code.
84   @param bytes_out Output bytes transferred. 84   @param bytes_out Output bytes transferred.
85   @return Coroutine handle to resume immediately. 85   @return Coroutine handle to resume immediately.
86   */ 86   */
87   virtual std::coroutine_handle<> read_some_at( 87   virtual std::coroutine_handle<> read_some_at(
88   std::uint64_t offset, 88   std::uint64_t offset,
89   capy::continuation& cont, 89   capy::continuation& cont,
90   capy::executor_ref ex, 90   capy::executor_ref ex,
91   buffer_param buf, 91   buffer_param buf,
92   std::stop_token token, 92   std::stop_token token,
93   std::error_code* ec, 93   std::error_code* ec,
94   std::size_t* bytes_out) = 0; 94   std::size_t* bytes_out) = 0;
95   95  
96   /** Initiate a write at the given offset. 96   /** Initiate a write at the given offset.
97   97  
98   @param offset Byte offset into the file. 98   @param offset Byte offset into the file.
99   @param cont The awaiting coroutine's continuation. It must 99   @param cont The awaiting coroutine's continuation. It must
100   stay valid until `cont.h` is resumed through @p ex. 100   stay valid until `cont.h` is resumed through @p ex.
101   @param ex Executor for dispatching the completion. 101   @param ex Executor for dispatching the completion.
102   @param buf The buffer to write from. 102   @param buf The buffer to write from.
103   @param token Stop token for cancellation. 103   @param token Stop token for cancellation.
104   @param ec Output error code. 104   @param ec Output error code.
105   @param bytes_out Output bytes transferred. 105   @param bytes_out Output bytes transferred.
106   @return Coroutine handle to resume immediately. 106   @return Coroutine handle to resume immediately.
107   */ 107   */
108   virtual std::coroutine_handle<> write_some_at( 108   virtual std::coroutine_handle<> write_some_at(
109   std::uint64_t offset, 109   std::uint64_t offset,
110   capy::continuation& cont, 110   capy::continuation& cont,
111   capy::executor_ref ex, 111   capy::executor_ref ex,
112   buffer_param buf, 112   buffer_param buf,
113   std::stop_token token, 113   std::stop_token token,
114   std::error_code* ec, 114   std::error_code* ec,
115   std::size_t* bytes_out) = 0; 115   std::size_t* bytes_out) = 0;
116   116  
117   /// Return the platform file descriptor or handle. 117   /// Return the platform file descriptor or handle.
118   virtual native_handle_type native_handle() const noexcept = 0; 118   virtual native_handle_type native_handle() const noexcept = 0;
119   119  
120   /// Cancel pending asynchronous operations. 120   /// Cancel pending asynchronous operations.
121   virtual void cancel() noexcept = 0; 121   virtual void cancel() noexcept = 0;
122   122  
123   /// Return the file size in bytes. 123   /// Return the file size in bytes.
124   virtual std::uint64_t size() const = 0; 124   virtual std::uint64_t size() const = 0;
125   125  
126   /** Resize the file to @p new_size bytes. 126   /** Resize the file to @p new_size bytes.
127   127  
128   @param new_size The requested size in bytes. 128   @param new_size The requested size in bytes.
129   129  
130   @return The error code, empty on success. 130   @return The error code, empty on success.
131   */ 131   */
132   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0; 132   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0;
133   133  
134   /** Synchronize file data to stable storage. 134   /** Synchronize file data to stable storage.
135   135  
136   @return The error code, empty on success. 136   @return The error code, empty on success.
137   */ 137   */
138   virtual std::error_code sync_data() noexcept = 0; 138   virtual std::error_code sync_data() noexcept = 0;
139   139  
140   /** Synchronize file data and metadata to stable storage. 140   /** Synchronize file data and metadata to stable storage.
141   141  
142   @return The error code, empty on success. 142   @return The error code, empty on success.
143   */ 143   */
144   virtual std::error_code sync_all() noexcept = 0; 144   virtual std::error_code sync_all() noexcept = 0;
145   145  
146   /// Release ownership of the native handle. 146   /// Release ownership of the native handle.
147   virtual native_handle_type release() = 0; 147   virtual native_handle_type release() = 0;
148   148  
149   /** Adopt an existing native handle. 149   /** Adopt an existing native handle.
150   150  
151   @param handle The native handle to adopt. The implementation takes 151   @param handle The native handle to adopt. The implementation takes
152   ownership and closes it. 152   ownership and closes it.
153   153  
154   @return The error code, empty on success. 154   @return The error code, empty on success.
155   */ 155   */
156   virtual std::error_code assign(native_handle_type handle) noexcept = 0; 156   virtual std::error_code assign(native_handle_type handle) noexcept = 0;
157   }; 157   };
158   158  
159   /** Awaitable for async read-at operations. */ 159   /** Awaitable for async read-at operations. */
160   template<class MutableBufferSequence> 160   template<class MutableBufferSequence>
161   struct read_some_at_awaitable 161   struct read_some_at_awaitable
162   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>> 162   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>>
163   { 163   {
164   private: 164   private:
165   friend random_access_file; 165   friend random_access_file;
166   friend detail::bytes_op_base< 166   friend detail::bytes_op_base<
167   read_some_at_awaitable<MutableBufferSequence>>; 167   read_some_at_awaitable<MutableBufferSequence>>;
168   168  
169   random_access_file& f_; 169   random_access_file& f_;
170   std::uint64_t offset_; 170   std::uint64_t offset_;
171   MutableBufferSequence buffers_; 171   MutableBufferSequence buffers_;
172   mutable capy::continuation cont_; 172   mutable capy::continuation cont_;
173   173  
HITCBC 174   349 read_some_at_awaitable( 174   349 read_some_at_awaitable(
175   random_access_file& f, 175   random_access_file& f,
176   std::uint64_t offset, 176   std::uint64_t offset,
177   MutableBufferSequence 177   MutableBufferSequence
178   buffers) noexcept(std:: 178   buffers) noexcept(std::
179   is_nothrow_move_constructible_v< 179   is_nothrow_move_constructible_v<
180   MutableBufferSequence>) 180   MutableBufferSequence>)
HITCBC 181   349 : f_(f) 181   349 : f_(f)
HITCBC 182   349 , offset_(offset) 182   349 , offset_(offset)
HITCBC 183   349 , buffers_(std::move(buffers)) 183   349 , buffers_(std::move(buffers))
184   { 184   {
HITCBC 185   349 } 185   349 }
186   186  
187   std::coroutine_handle<> 187   std::coroutine_handle<>
HITCBC 188   343 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 188   343 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
189   { 189   {
190   // The continuation lives in the awaiting frame, which stays 190   // The continuation lives in the awaiting frame, which stays
191   // put until resumption -- unlike the per-call op, which is 191   // put until resumption -- unlike the per-call op, which is
192   // freed before the coroutine runs. 192   // freed before the coroutine runs.
HITCBC 193   343 cont_.h = h; 193   343 cont_.h = h;
HITCBC 194   686 return f_.get().read_some_at( 194   686 return f_.get().read_some_at(
HITCBC 195   343 offset_, cont_, ex, buffers_, this->token_, &this->ec_, 195   343 offset_, cont_, ex, buffers_, this->token_, &this->ec_,
HITCBC 196   686 &this->bytes_); 196   686 &this->bytes_);
197   } 197   }
198   }; 198   };
199   199  
200   /** Awaitable for async write-at operations. */ 200   /** Awaitable for async write-at operations. */
201   template<class ConstBufferSequence> 201   template<class ConstBufferSequence>
202   struct write_some_at_awaitable 202   struct write_some_at_awaitable
203   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>> 203   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>>
204   { 204   {
205   private: 205   private:
206   friend random_access_file; 206   friend random_access_file;
207   friend detail::bytes_op_base< 207   friend detail::bytes_op_base<
208   write_some_at_awaitable<ConstBufferSequence>>; 208   write_some_at_awaitable<ConstBufferSequence>>;
209   209  
210   random_access_file& f_; 210   random_access_file& f_;
211   std::uint64_t offset_; 211   std::uint64_t offset_;
212   ConstBufferSequence buffers_; 212   ConstBufferSequence buffers_;
213   mutable capy::continuation cont_; 213   mutable capy::continuation cont_;
214   214  
HITCBC 215   97 write_some_at_awaitable( 215   97 write_some_at_awaitable(
216   random_access_file& f, 216   random_access_file& f,
217   std::uint64_t offset, 217   std::uint64_t offset,
218   ConstBufferSequence 218   ConstBufferSequence
219   buffers) noexcept(std:: 219   buffers) noexcept(std::
220   is_nothrow_move_constructible_v< 220   is_nothrow_move_constructible_v<
221   ConstBufferSequence>) 221   ConstBufferSequence>)
HITCBC 222   97 : f_(f) 222   97 : f_(f)
HITCBC 223   97 , offset_(offset) 223   97 , offset_(offset)
HITCBC 224   97 , buffers_(std::move(buffers)) 224   97 , buffers_(std::move(buffers))
225   { 225   {
HITCBC 226   97 } 226   97 }
227   227  
228   std::coroutine_handle<> 228   std::coroutine_handle<>
HITCBC 229   93 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 229   93 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
230   { 230   {
HITCBC 231   93 cont_.h = h; 231   93 cont_.h = h;
HITCBC 232   186 return f_.get().write_some_at( 232   186 return f_.get().write_some_at(
HITCBC 233   93 offset_, cont_, ex, buffers_, this->token_, &this->ec_, 233   93 offset_, cont_, ex, buffers_, this->token_, &this->ec_,
HITCBC 234   186 &this->bytes_); 234   186 &this->bytes_);
235   } 235   }
236   }; 236   };
237   237  
238   public: 238   public:
239   /** Destructor. 239   /** Destructor.
240   240  
241   Closes the file if open, cancelling any pending operations. 241   Closes the file if open, cancelling any pending operations.
242   */ 242   */
243   ~random_access_file() override; 243   ~random_access_file() override;
244   244  
245   /** Construct from an execution context. 245   /** Construct from an execution context.
246   246  
247   @param ctx The execution context that owns this file. 247   @param ctx The execution context that owns this file.
248   */ 248   */
249   explicit random_access_file(capy::execution_context& ctx); 249   explicit random_access_file(capy::execution_context& ctx);
250   250  
251   /** Construct from an executor. 251   /** Construct from an executor.
252   252  
253   @param ex The executor whose context owns this file. 253   @param ex The executor whose context owns this file.
254   */ 254   */
255   template<class Ex> 255   template<class Ex>
256   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) && 256   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) &&
257   capy::Executor<Ex> 257   capy::Executor<Ex>
HITCBC 258   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context()) 258   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context())
259   { 259   {
HITCBC 260   2 } 260   2 }
261   261  
262   /** Move constructor. */ 262   /** Move constructor. */
HITCBC 263   2 random_access_file(random_access_file&& other) noexcept 263   2 random_access_file(random_access_file&& other) noexcept
HITCBC 264   2 : io_object(std::move(other)) 264   2 : io_object(std::move(other))
265   { 265   {
HITCBC 266   2 } 266   2 }
267   267  
268   /** Move assignment operator. */ 268   /** Move assignment operator. */
269   random_access_file& operator=(random_access_file&& other) noexcept 269   random_access_file& operator=(random_access_file&& other) noexcept
270   { 270   {
271   if (this != &other) 271   if (this != &other)
272   { 272   {
273   close(); 273   close();
274   h_ = std::move(other.h_); 274   h_ = std::move(other.h_);
275   } 275   }
276   return *this; 276   return *this;
277   } 277   }
278   278  
279   /// Copy construction is disabled; the handle is uniquely owned. 279   /// Copy construction is disabled; the handle is uniquely owned.
280   random_access_file(random_access_file const&) = delete; 280   random_access_file(random_access_file const&) = delete;
281   /// Copy assignment is disabled; the handle is uniquely owned. 281   /// Copy assignment is disabled; the handle is uniquely owned.
282   random_access_file& operator=(random_access_file const&) = delete; 282   random_access_file& operator=(random_access_file const&) = delete;
283   283  
284   /** Open a file. 284   /** Open a file.
285   285  
286   Failures such as a missing file or insufficient permissions 286   Failures such as a missing file or insufficient permissions
287   are expected runtime conditions and are reported through the 287   are expected runtime conditions and are reported through the
288   returned error code. If the file is already open, it is 288   returned error code. If the file is already open, it is
289   closed first. 289   closed first.
290   290  
291   @param path The filesystem path to open. 291   @param path The filesystem path to open.
292   @param mode Bitmask of @ref file_base::flags specifying 292   @param mode Bitmask of @ref file_base::flags specifying
293   access mode and creation behavior. 293   access mode and creation behavior.
294   294  
295   @return The error code, empty on success. 295   @return The error code, empty on success.
296   */ 296   */
297   [[nodiscard]] std::error_code open( 297   [[nodiscard]] std::error_code open(
298   std::filesystem::path const& path, 298   std::filesystem::path const& path,
299   file_base::flags mode = file_base::read_only) noexcept; 299   file_base::flags mode = file_base::read_only) noexcept;
300   300  
301   /** Close the file. 301   /** Close the file.
302   302  
303   Releases file resources. Pending operations complete through the 303   Releases file resources. Pending operations complete through the
304   same path as @ref cancel: one still in flight completes with 304   same path as @ref cancel: one still in flight completes with
305   `errc::operation_canceled`. An operation whose result is already 305   `errc::operation_canceled`. An operation whose result is already
306   decided reports that result. 306   decided reports that result.
307   */ 307   */
308   void close() noexcept; 308   void close() noexcept;
309   309  
310   /** Check if the file is open. 310   /** Check if the file is open.
311   311  
312   @return `true` if the file holds an open handle. 312   @return `true` if the file holds an open handle.
313   */ 313   */
HITCBC 314   1116 bool is_open() const noexcept 314   1116 bool is_open() const noexcept
315   { 315   {
316   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 316   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
317   return h_ && get().native_handle() != ~native_handle_type(0); 317   return h_ && get().native_handle() != ~native_handle_type(0);
318   #else 318   #else
HITCBC 319   1116 return h_ && get().native_handle() >= 0; 319   1116 return h_ && get().native_handle() >= 0;
320   #endif 320   #endif
321   } 321   }
322   322  
323   /** Read data at the given offset. 323   /** Read data at the given offset.
324   324  
325   @param offset Byte offset into the file. 325   @param offset Byte offset into the file.
326   @param buffers The buffer sequence to read into. 326   @param buffers The buffer sequence to read into.
327   327  
328   @return An awaitable yielding `(error_code, std::size_t)`. 328   @return An awaitable yielding `(error_code, std::size_t)`.
329   329  
330   A closed file reports `errc::bad_file_descriptor`. 330   A closed file reports `errc::bad_file_descriptor`.
331   */ 331   */
332   template<capy::MutableBufferSequence MB> 332   template<capy::MutableBufferSequence MB>
HITCBC 333   349 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 333   349 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
334   { 334   {
HITCBC 335   349 read_some_at_awaitable<MB> aw(*this, offset, buffers); 335   349 read_some_at_awaitable<MB> aw(*this, offset, buffers);
HITCBC 336   349 if (!is_open()) 336   349 if (!is_open())
HITCBC 337   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 337   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 338   349 return aw; 338   349 return aw;
339   } 339   }
340   340  
341   /** Write data at the given offset. 341   /** Write data at the given offset.
342   342  
343   @param offset Byte offset into the file. 343   @param offset Byte offset into the file.
344   @param buffers The buffer sequence to write from. 344   @param buffers The buffer sequence to write from.
345   345  
346   @return An awaitable yielding `(error_code, std::size_t)`. 346   @return An awaitable yielding `(error_code, std::size_t)`.
347   347  
348   A closed file reports `errc::bad_file_descriptor`. 348   A closed file reports `errc::bad_file_descriptor`.
349   */ 349   */
350   template<capy::ConstBufferSequence CB> 350   template<capy::ConstBufferSequence CB>
HITCBC 351   97 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 351   97 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
352   { 352   {
HITCBC 353   97 write_some_at_awaitable<CB> aw(*this, offset, buffers); 353   97 write_some_at_awaitable<CB> aw(*this, offset, buffers);
HITCBC 354   97 if (!is_open()) 354   97 if (!is_open())
HITCBC 355   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 355   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 356   97 return aw; 356   97 return aw;
357   } 357   }
358   358  
359   /** Cancel pending asynchronous operations. */ 359   /** Cancel pending asynchronous operations. */
360   void cancel() noexcept; 360   void cancel() noexcept;
361   361  
362   /** Get the native file descriptor or handle. */ 362   /** Get the native file descriptor or handle. */
363   native_handle_type native_handle() const noexcept; 363   native_handle_type native_handle() const noexcept;
364   364  
365   /** Return the file size in bytes. 365   /** Return the file size in bytes.
366   366  
367   @return The current size of the file, in bytes. 367   @return The current size of the file, in bytes.
368   368  
369   @throws std::system_error If the file is not open, or if the 369   @throws std::system_error If the file is not open, or if the
370   underlying size query fails. 370   underlying size query fails.
371   */ 371   */
372   std::uint64_t size() const; 372   std::uint64_t size() const;
373   373  
374   /** Resize the file to @p new_size bytes. 374   /** Resize the file to @p new_size bytes.
375   375  
376   Failures such as insufficient disk space are reported 376   Failures such as insufficient disk space are reported
377   through the returned error code. A closed file reports 377   through the returned error code. A closed file reports
378   `errc::bad_file_descriptor`. 378   `errc::bad_file_descriptor`.
379   379  
380   @param new_size The new file size. 380   @param new_size The new file size.
381   381  
382   @return The error code, empty on success. 382   @return The error code, empty on success.
383   */ 383   */
384   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept; 384   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept;
385   385  
386   /** Synchronize file data to stable storage. 386   /** Synchronize file data to stable storage.
387   387  
388   Write-back failures such as device I/O errors surface here 388   Write-back failures such as device I/O errors surface here
389   and are reported through the returned error code. A closed 389   and are reported through the returned error code. A closed
390   file reports `errc::bad_file_descriptor`. 390   file reports `errc::bad_file_descriptor`.
391   391  
392   @return The error code, empty on success. 392   @return The error code, empty on success.
393   */ 393   */
394   [[nodiscard]] std::error_code sync_data() noexcept; 394   [[nodiscard]] std::error_code sync_data() noexcept;
395   395  
396   /** Synchronize file data and metadata to stable storage. 396   /** Synchronize file data and metadata to stable storage.
397   397  
398   Write-back failures such as device I/O errors surface here 398   Write-back failures such as device I/O errors surface here
399   and are reported through the returned error code. A closed 399   and are reported through the returned error code. A closed
400   file reports `errc::bad_file_descriptor`. 400   file reports `errc::bad_file_descriptor`.
401   401  
402   @return The error code, empty on success. 402   @return The error code, empty on success.
403   */ 403   */
404   [[nodiscard]] std::error_code sync_all() noexcept; 404   [[nodiscard]] std::error_code sync_all() noexcept;
405   405  
406   /** Release ownership of the native handle. 406   /** Release ownership of the native handle.
407   407  
408   The file object becomes not-open. The caller is 408   The file object becomes not-open. The caller is
409   responsible for closing the returned handle. 409   responsible for closing the returned handle.
410   410  
411   `release()` cancels pending operations first. On Windows, the 411   `release()` cancels pending operations first. On Windows, the
412   object keeps the handle and this throws if one is still in 412   object keeps the handle and this throws if one is still in
413   flight. It does the same if Windows refuses to detach the 413   flight. It does the same if Windows refuses to detach the
414   handle from the execution context's completion port. Call 414   handle from the execution context's completion port. Call
415   `release()` again once the cancelled operations have 415   `release()` again once the cancelled operations have
416   completed. Detaching requires Windows 8.1 or later. 416   completed. Detaching requires Windows 8.1 or later.
417   417  
418   @return The native file descriptor or handle. 418   @return The native file descriptor or handle.
419   419  
420   @throws std::system_error `errc::bad_file_descriptor` if the 420   @throws std::system_error `errc::bad_file_descriptor` if the
421   file is not open. On Windows, 421   file is not open. On Windows,
422   `errc::device_or_resource_busy` if an operation is still in 422   `errc::device_or_resource_busy` if an operation is still in
423   flight, or `errc::operation_not_supported` if the handle 423   flight, or `errc::operation_not_supported` if the handle
424   cannot be detached. 424   cannot be detached.
425   */ 425   */
426   native_handle_type release(); 426   native_handle_type release();
427   427  
428   /** Adopt an existing native handle. 428   /** Adopt an existing native handle.
429   429  
430   The object must be closed. To replace a held file, `close()` 430   The object must be closed. To replace a held file, `close()`
431   or `release()` it first. On success the object takes 431   or `release()` it first. On success the object takes
432   ownership of @p handle. Handles created elsewhere may be 432   ownership of @p handle. Handles created elsewhere may be
433   unsuitable for asynchronous I/O. `assign()` reports most such 433   unsuitable for asynchronous I/O. `assign()` reports most such
434   failures through the returned error code. 434   failures through the returned error code.
435   435  
436   @param handle The native file descriptor or handle. 436   @param handle The native file descriptor or handle.
437   437  
438   @return An error code describing the outcome. 438   @return An error code describing the outcome.
439   `error::already_open` if this object is open. 439   `error::already_open` if this object is open.
440   `errc::bad_file_descriptor` if @p handle is invalid. 440   `errc::bad_file_descriptor` if @p handle is invalid.
441   `errc::operation_not_supported` if a file object cannot 441   `errc::operation_not_supported` if a file object cannot
442   use it. On Windows, the rejected handles are a pipe, a 442   use it. On Windows, the rejected handles are a pipe, a
443   socket, a console, a directory, a handle opened without 443   socket, a console, a directory, a handle opened without
444   `FILE_FLAG_OVERLAPPED`, or one 444   `FILE_FLAG_OVERLAPPED`, or one
445   already in skip-completion-port-on-success mode. On 445   already in skip-completion-port-on-success mode. On
446   Windows, `errc::invalid_argument` when @p handle is 446   Windows, `errc::invalid_argument` when @p handle is
447   bound to another completion port. Any other failure is 447   bound to another completion port. Any other failure is
448   the code reported by the system. Otherwise, the code is 448   the code reported by the system. Otherwise, the code is
449   empty. 449   empty.
450   450  
451   @par Exception Safety 451   @par Exception Safety
452   Throws nothing. On failure the object is unchanged and the 452   Throws nothing. On failure the object is unchanged and the
453   caller still owns @p handle. 453   caller still owns @p handle.
454   454  
455   @note On POSIX, the rejected descriptors are, in practice, a 455   @note On POSIX, the rejected descriptors are, in practice, a
456   directory, a pipe, a socket, or any other anonymous inode. 456   directory, a pipe, a socket, or any other anonymous inode.
457   Adopt a pipe, a socket, or an anonymous inode into a 457   Adopt a pipe, a socket, or an anonymous inode into a
458   @ref posix_stream_descriptor instead. `assign()` accepts a 458   @ref posix_stream_descriptor instead. `assign()` accepts a
459   non-seekable character device such as a tty. Its first 459   non-seekable character device such as a tty. Its first
460   read or write then fails with `ESPIPE` on the epoll, 460   read or write then fails with `ESPIPE` on the epoll,
461   kqueue, and select I/O backends. 461   kqueue, and select I/O backends.
462   462  
463   @see release 463   @see release
464   */ 464   */
465   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept; 465   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept;
466   466  
467   protected: 467   protected:
468   /** Construct from a pre-built handle (for `native_random_access_file`). 468   /** Construct from a pre-built handle (for `native_random_access_file`).
469   469  
470   @param h The pre-built handle to adopt. 470   @param h The pre-built handle to adopt.
471   */ 471   */
HITCBC 472   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {} 472   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {}
473   473  
474   private: 474   private:
HITCBC 475   1817 inline implementation& get() const noexcept 475   1817 inline implementation& get() const noexcept
476   { 476   {
HITCBC 477   1817 return *static_cast<implementation*>(h_.get()); 477   1817 return *static_cast<implementation*>(h_.get());
478   } 478   }
479   }; 479   };
480   480  
481   } // namespace boost::corosio 481   } // namespace boost::corosio
482   482  
483   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 483   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP