100.00% Lines (38/38) 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/file_base.hpp> 19   #include <boost/corosio/file_base.hpp>
20   #include <boost/corosio/io/io_object.hpp> 20   #include <boost/corosio/io/io_object.hpp>
21   #include <boost/capy/io_result.hpp> 21   #include <boost/capy/io_result.hpp>
22   #include <boost/capy/ex/executor_ref.hpp> 22   #include <boost/capy/ex/executor_ref.hpp>
23   #include <boost/capy/ex/execution_context.hpp> 23   #include <boost/capy/ex/execution_context.hpp>
24   #include <boost/capy/ex/io_env.hpp> 24   #include <boost/capy/ex/io_env.hpp>
25   #include <boost/capy/concept/executor.hpp> 25   #include <boost/capy/concept/executor.hpp>
26   #include <boost/capy/buffers.hpp> 26   #include <boost/capy/buffers.hpp>
27   27  
28   #include <concepts> 28   #include <concepts>
29   #include <coroutine> 29   #include <coroutine>
30   #include <cstddef> 30   #include <cstddef>
31   #include <cstdint> 31   #include <cstdint>
32   #include <type_traits> 32   #include <type_traits>
33   #include <filesystem> 33   #include <filesystem>
34   #include <stop_token> 34   #include <stop_token>
35   #include <system_error> 35   #include <system_error>
36   36  
37   namespace boost::corosio { 37   namespace boost::corosio {
38   38  
39 - /** An asynchronous random-access file for coroutine I/O. 39 + /** Reads and writes a file at arbitrary offsets, from a coroutine.
40   40  
41   Provides asynchronous read and write operations at explicit 41   Provides asynchronous read and write operations at explicit
42   byte offsets, without maintaining an implicit file position. 42   byte offsets, without maintaining an implicit file position.
43   43  
44   On POSIX platforms, file I/O is dispatched to a thread pool 44   On POSIX platforms, file I/O is dispatched to a thread pool
45   (blocking `preadv`/`pwritev`) with completion posted back to 45   (blocking `preadv`/`pwritev`) with completion posted back to
46   the scheduler. On Windows, true overlapped I/O is used via IOCP. 46   the scheduler. On Windows, true overlapped I/O is used via IOCP.
47   47  
48   @par Thread Safety 48   @par Thread Safety
49   Distinct objects: Safe.@n 49   Distinct objects: Safe.@n
50 - Shared objects: Unsafe. Multiple concurrent reads and writes 50 + Shared objects: Unsafe. Coroutines sharing the same file object may
51 - are supported from coroutines sharing the same file object, 51 + run multiple concurrent reads and writes. Non-async operations such as open, close, size, and resize require external synchronization.
52 - but external synchronization is required for non-async  
53 - operations (open, close, size, resize, etc.).  
54   52  
55   @par Example 53   @par Example
56   @par !example random_access_file 54   @par !example random_access_file
57   */ 55   */
58   class BOOST_COROSIO_DECL random_access_file : public io_object 56   class BOOST_COROSIO_DECL random_access_file : public io_object
59   { 57   {
60   public: 58   public:
61 - /** Platform-specific random-access file implementation interface. 59 + /** Declares the offset-based file operations a platform backend
  60 + must implement.
62   61  
63   Backends derive from this to provide offset-based file I/O. 62   Backends derive from this to provide offset-based file I/O.
64   */ 63   */
65   struct implementation : io_object::implementation 64   struct implementation : io_object::implementation
66   { 65   {
67   /** Initiate a read at the given offset. 66   /** Initiate a read at the given offset.
68   67  
69   @param offset Byte offset into the file. 68   @param offset Byte offset into the file.
70   @param h Coroutine handle to resume on completion. 69   @param h Coroutine handle to resume on completion.
71   @param ex Executor for dispatching the completion. 70   @param ex Executor for dispatching the completion.
72   @param buf The buffer to read into. 71   @param buf The buffer to read into.
73   @param token Stop token for cancellation. 72   @param token Stop token for cancellation.
74   @param ec Output error code. 73   @param ec Output error code.
75   @param bytes_out Output bytes transferred. 74   @param bytes_out Output bytes transferred.
76   @return Coroutine handle to resume immediately. 75   @return Coroutine handle to resume immediately.
77   */ 76   */
78   virtual std::coroutine_handle<> read_some_at( 77   virtual std::coroutine_handle<> read_some_at(
79   std::uint64_t offset, 78   std::uint64_t offset,
80   std::coroutine_handle<> h, 79   std::coroutine_handle<> h,
81   capy::executor_ref ex, 80   capy::executor_ref ex,
82   buffer_param buf, 81   buffer_param buf,
83   std::stop_token token, 82   std::stop_token token,
84   std::error_code* ec, 83   std::error_code* ec,
85   std::size_t* bytes_out) = 0; 84   std::size_t* bytes_out) = 0;
86   85  
87   /** Initiate a write at the given offset. 86   /** Initiate a write at the given offset.
88   87  
89   @param offset Byte offset into the file. 88   @param offset Byte offset into the file.
90   @param h Coroutine handle to resume on completion. 89   @param h Coroutine handle to resume on completion.
91   @param ex Executor for dispatching the completion. 90   @param ex Executor for dispatching the completion.
92   @param buf The buffer to write from. 91   @param buf The buffer to write from.
93   @param token Stop token for cancellation. 92   @param token Stop token for cancellation.
94   @param ec Output error code. 93   @param ec Output error code.
95   @param bytes_out Output bytes transferred. 94   @param bytes_out Output bytes transferred.
96   @return Coroutine handle to resume immediately. 95   @return Coroutine handle to resume immediately.
97   */ 96   */
98   virtual std::coroutine_handle<> write_some_at( 97   virtual std::coroutine_handle<> write_some_at(
99   std::uint64_t offset, 98   std::uint64_t offset,
100   std::coroutine_handle<> h, 99   std::coroutine_handle<> h,
101   capy::executor_ref ex, 100   capy::executor_ref ex,
102   buffer_param buf, 101   buffer_param buf,
103   std::stop_token token, 102   std::stop_token token,
104   std::error_code* ec, 103   std::error_code* ec,
105   std::size_t* bytes_out) = 0; 104   std::size_t* bytes_out) = 0;
106   105  
107   /// Return the platform file descriptor or handle. 106   /// Return the platform file descriptor or handle.
108   virtual native_handle_type native_handle() const noexcept = 0; 107   virtual native_handle_type native_handle() const noexcept = 0;
109   108  
110   /// Cancel pending asynchronous operations. 109   /// Cancel pending asynchronous operations.
111   virtual void cancel() noexcept = 0; 110   virtual void cancel() noexcept = 0;
112   111  
113   /// Return the file size in bytes. 112   /// Return the file size in bytes.
114   virtual std::uint64_t size() const = 0; 113   virtual std::uint64_t size() const = 0;
115   114  
116 - /// Resize the file to @p new_size bytes. 115 + /** Resize the file to @p new_size bytes.
  116 +
  117 + @param new_size The requested size in bytes.
  118 +
  119 + @return The error code, empty on success.
  120 + */
117   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0; 121   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0;
118   122  
119 - /// Synchronize file data to stable storage. 123 + /** Synchronize file data to stable storage.
  124 +
  125 + @return The error code, empty on success.
  126 + */
120   virtual std::error_code sync_data() noexcept = 0; 127   virtual std::error_code sync_data() noexcept = 0;
121   128  
122 - /// Synchronize file data and metadata to stable storage. 129 + /** Synchronize file data and metadata to stable storage.
  130 +
  131 + @return The error code, empty on success.
  132 + */
123   virtual std::error_code sync_all() noexcept = 0; 133   virtual std::error_code sync_all() noexcept = 0;
124   134  
125   /// Release ownership of the native handle. 135   /// Release ownership of the native handle.
126   virtual native_handle_type release() = 0; 136   virtual native_handle_type release() = 0;
127   137  
128 - /// Adopt an existing native handle. 138 + /** Adopt an existing native handle.
  139 +
  140 + @param handle The native handle to adopt. The implementation takes
  141 + ownership and closes it.
  142 +
  143 + @return The error code, empty on success.
  144 + */
129   virtual std::error_code assign(native_handle_type handle) noexcept = 0; 145   virtual std::error_code assign(native_handle_type handle) noexcept = 0;
130   }; 146   };
131   147  
132   /** Awaitable for async read-at operations. */ 148   /** Awaitable for async read-at operations. */
133   template<class MutableBufferSequence> 149   template<class MutableBufferSequence>
134   struct read_some_at_awaitable 150   struct read_some_at_awaitable
135   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>> 151   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>>
136   { 152   {
  153 + private:
  154 + friend random_access_file;
  155 + friend detail::bytes_op_base<
  156 + read_some_at_awaitable<MutableBufferSequence>>;
  157 +
137   random_access_file& f_; 158   random_access_file& f_;
138   std::uint64_t offset_; 159   std::uint64_t offset_;
139   MutableBufferSequence buffers_; 160   MutableBufferSequence buffers_;
140   161  
HITCBC 141   343 read_some_at_awaitable( 162   343 read_some_at_awaitable(
142   random_access_file& f, 163   random_access_file& f,
143   std::uint64_t offset, 164   std::uint64_t offset,
144   MutableBufferSequence 165   MutableBufferSequence
145   buffers) noexcept(std:: 166   buffers) noexcept(std::
146   is_nothrow_move_constructible_v< 167   is_nothrow_move_constructible_v<
147   MutableBufferSequence>) 168   MutableBufferSequence>)
HITCBC 148   343 : f_(f) 169   343 : f_(f)
HITCBC 149   343 , offset_(offset) 170   343 , offset_(offset)
HITCBC 150   343 , buffers_(std::move(buffers)) 171   343 , buffers_(std::move(buffers))
151   { 172   {
HITCBC 152   343 } 173   343 }
153   174  
154   std::coroutine_handle<> 175   std::coroutine_handle<>
HITCBC 155   337 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 176   337 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
156   { 177   {
HITCBC 157   674 return f_.get().read_some_at( 178   674 return f_.get().read_some_at(
HITCBC 158   337 offset_, h, ex, buffers_, this->token_, &this->ec_, 179   337 offset_, h, ex, buffers_, this->token_, &this->ec_,
HITCBC 159   674 &this->bytes_); 180   674 &this->bytes_);
160   } 181   }
161   }; 182   };
162   183  
163   /** Awaitable for async write-at operations. */ 184   /** Awaitable for async write-at operations. */
164   template<class ConstBufferSequence> 185   template<class ConstBufferSequence>
165   struct write_some_at_awaitable 186   struct write_some_at_awaitable
166   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>> 187   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>>
167   { 188   {
  189 + private:
  190 + friend random_access_file;
  191 + friend detail::bytes_op_base<
  192 + write_some_at_awaitable<ConstBufferSequence>>;
  193 +
168   random_access_file& f_; 194   random_access_file& f_;
169   std::uint64_t offset_; 195   std::uint64_t offset_;
170   ConstBufferSequence buffers_; 196   ConstBufferSequence buffers_;
171   197  
HITCBC 172   93 write_some_at_awaitable( 198   93 write_some_at_awaitable(
173   random_access_file& f, 199   random_access_file& f,
174   std::uint64_t offset, 200   std::uint64_t offset,
175   ConstBufferSequence 201   ConstBufferSequence
176   buffers) noexcept(std:: 202   buffers) noexcept(std::
177   is_nothrow_move_constructible_v< 203   is_nothrow_move_constructible_v<
178   ConstBufferSequence>) 204   ConstBufferSequence>)
HITCBC 179   93 : f_(f) 205   93 : f_(f)
HITCBC 180   93 , offset_(offset) 206   93 , offset_(offset)
HITCBC 181   93 , buffers_(std::move(buffers)) 207   93 , buffers_(std::move(buffers))
182   { 208   {
HITCBC 183   93 } 209   93 }
184   210  
185   std::coroutine_handle<> 211   std::coroutine_handle<>
HITCBC 186   89 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 212   89 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
187   { 213   {
HITCBC 188   178 return f_.get().write_some_at( 214   178 return f_.get().write_some_at(
HITCBC 189   89 offset_, h, ex, buffers_, this->token_, &this->ec_, 215   89 offset_, h, ex, buffers_, this->token_, &this->ec_,
HITCBC 190   178 &this->bytes_); 216   178 &this->bytes_);
191   } 217   }
192   }; 218   };
193   219  
194   public: 220   public:
195   /** Destructor. 221   /** Destructor.
196   222  
197   Closes the file if open, cancelling any pending operations. 223   Closes the file if open, cancelling any pending operations.
198   */ 224   */
199   ~random_access_file() override; 225   ~random_access_file() override;
200   226  
201   /** Construct from an execution context. 227   /** Construct from an execution context.
202   228  
203 - @param ctx The execution context that will own this file. 229 + @param ctx The execution context that owns this file.
204   */ 230   */
205   explicit random_access_file(capy::execution_context& ctx); 231   explicit random_access_file(capy::execution_context& ctx);
206   232  
207   /** Construct from an executor. 233   /** Construct from an executor.
208   234  
209 - @param ex The executor whose context will own this file. 235 + @param ex The executor whose context owns this file.
210   */ 236   */
211   template<class Ex> 237   template<class Ex>
212   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) && 238   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) &&
213   capy::Executor<Ex> 239   capy::Executor<Ex>
HITCBC 214   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context()) 240   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context())
215   { 241   {
HITCBC 216   2 } 242   2 }
217   243  
218   /** Move constructor. */ 244   /** Move constructor. */
HITCBC 219   2 random_access_file(random_access_file&& other) noexcept 245   2 random_access_file(random_access_file&& other) noexcept
HITCBC 220   2 : io_object(std::move(other)) 246   2 : io_object(std::move(other))
221   { 247   {
HITCBC 222   2 } 248   2 }
223   249  
224   /** Move assignment operator. */ 250   /** Move assignment operator. */
225   random_access_file& operator=(random_access_file&& other) noexcept 251   random_access_file& operator=(random_access_file&& other) noexcept
226   { 252   {
227   if (this != &other) 253   if (this != &other)
228   { 254   {
229   close(); 255   close();
230   h_ = std::move(other.h_); 256   h_ = std::move(other.h_);
231   } 257   }
232   return *this; 258   return *this;
233   } 259   }
234   260  
235 - random_access_file(random_access_file const&) = delete; 261 + /// Copy construction is disabled; the handle is uniquely owned.
  262 + random_access_file(random_access_file const&) = delete;
  263 + /// Copy assignment is disabled; the handle is uniquely owned.
236   random_access_file& operator=(random_access_file const&) = delete; 264   random_access_file& operator=(random_access_file const&) = delete;
237   265  
238   /** Open a file. 266   /** Open a file.
239   267  
240   Failures such as a missing file or insufficient permissions 268   Failures such as a missing file or insufficient permissions
241   are expected runtime conditions and are reported through the 269   are expected runtime conditions and are reported through the
242   returned error code. If the file is already open, it is 270   returned error code. If the file is already open, it is
243   closed first. 271   closed first.
244   272  
245   @param path The filesystem path to open. 273   @param path The filesystem path to open.
246   @param mode Bitmask of @ref file_base::flags specifying 274   @param mode Bitmask of @ref file_base::flags specifying
247   access mode and creation behavior. 275   access mode and creation behavior.
248   276  
249   @return The error code, empty on success. 277   @return The error code, empty on success.
250   */ 278   */
251   [[nodiscard]] std::error_code open( 279   [[nodiscard]] std::error_code open(
252   std::filesystem::path const& path, 280   std::filesystem::path const& path,
253   file_base::flags mode = file_base::read_only) noexcept; 281   file_base::flags mode = file_base::read_only) noexcept;
254   282  
255   /** Close the file. 283   /** Close the file.
256   284  
257 - Releases file resources. Any pending operations complete 285 + Releases file resources. Pending operations complete through the
258 - with `errc::operation_canceled`. 286 + same path as @ref cancel: one still in flight completes with
  287 + `errc::operation_canceled`. An operation whose result is already
  288 + decided reports that result.
259   */ 289   */
260   void close() noexcept; 290   void close() noexcept;
261   291  
262 - /** Check if the file is open. */ 292 + /** Check if the file is open.
  293 +
  294 + @return `true` if the file holds an open handle.
  295 + */
HITCBC 263   1082 bool is_open() const noexcept 296   1082 bool is_open() const noexcept
264   { 297   {
265   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 298   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
266   return h_ && get().native_handle() != ~native_handle_type(0); 299   return h_ && get().native_handle() != ~native_handle_type(0);
267   #else 300   #else
HITCBC 268   1082 return h_ && get().native_handle() >= 0; 301   1082 return h_ && get().native_handle() >= 0;
269   #endif 302   #endif
270   } 303   }
271   304  
272   /** Read data at the given offset. 305   /** Read data at the given offset.
273   306  
274   @param offset Byte offset into the file. 307   @param offset Byte offset into the file.
275   @param buffers The buffer sequence to read into. 308   @param buffers The buffer sequence to read into.
276   309  
277   @return An awaitable yielding `(error_code, std::size_t)`. 310   @return An awaitable yielding `(error_code, std::size_t)`.
278   311  
279   A closed file reports `errc::bad_file_descriptor`. 312   A closed file reports `errc::bad_file_descriptor`.
280   */ 313   */
281   template<capy::MutableBufferSequence MB> 314   template<capy::MutableBufferSequence MB>
HITCBC 282   343 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 315   343 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
283   { 316   {
HITCBC 284   343 read_some_at_awaitable<MB> aw(*this, offset, buffers); 317   343 read_some_at_awaitable<MB> aw(*this, offset, buffers);
HITCBC 285   343 if (!is_open()) 318   343 if (!is_open())
HITCBC 286   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 319   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 287   343 return aw; 320   343 return aw;
288   } 321   }
289   322  
290   /** Write data at the given offset. 323   /** Write data at the given offset.
291   324  
292   @param offset Byte offset into the file. 325   @param offset Byte offset into the file.
293   @param buffers The buffer sequence to write from. 326   @param buffers The buffer sequence to write from.
294   327  
295   @return An awaitable yielding `(error_code, std::size_t)`. 328   @return An awaitable yielding `(error_code, std::size_t)`.
296   329  
297   A closed file reports `errc::bad_file_descriptor`. 330   A closed file reports `errc::bad_file_descriptor`.
298   */ 331   */
299   template<capy::ConstBufferSequence CB> 332   template<capy::ConstBufferSequence CB>
HITCBC 300   93 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 333   93 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
301   { 334   {
HITCBC 302   93 write_some_at_awaitable<CB> aw(*this, offset, buffers); 335   93 write_some_at_awaitable<CB> aw(*this, offset, buffers);
HITCBC 303   93 if (!is_open()) 336   93 if (!is_open())
HITCBC 304   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 337   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 305   93 return aw; 338   93 return aw;
306   } 339   }
307   340  
308   /** Cancel pending asynchronous operations. */ 341   /** Cancel pending asynchronous operations. */
309   void cancel() noexcept; 342   void cancel() noexcept;
310   343  
311   /** Get the native file descriptor or handle. */ 344   /** Get the native file descriptor or handle. */
312   native_handle_type native_handle() const noexcept; 345   native_handle_type native_handle() const noexcept;
313   346  
314   /** Return the file size in bytes. 347   /** Return the file size in bytes.
  348 +
  349 + @return The current size of the file, in bytes.
315   350  
316   @throws std::system_error If the file is not open, or if the 351   @throws std::system_error If the file is not open, or if the
317   underlying size query fails. 352   underlying size query fails.
318   */ 353   */
319   std::uint64_t size() const; 354   std::uint64_t size() const;
320   355  
321   /** Resize the file to @p new_size bytes. 356   /** Resize the file to @p new_size bytes.
322   357  
323   Failures such as insufficient disk space are reported 358   Failures such as insufficient disk space are reported
324   through the returned error code. A closed file reports 359   through the returned error code. A closed file reports
325   `errc::bad_file_descriptor`. 360   `errc::bad_file_descriptor`.
326   361  
327   @param new_size The new file size. 362   @param new_size The new file size.
328   363  
329   @return The error code, empty on success. 364   @return The error code, empty on success.
330   */ 365   */
331   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept; 366   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept;
332   367  
333   /** Synchronize file data to stable storage. 368   /** Synchronize file data to stable storage.
334   369  
335   Write-back failures such as device I/O errors surface here 370   Write-back failures such as device I/O errors surface here
336   and are reported through the returned error code. A closed 371   and are reported through the returned error code. A closed
337   file reports `errc::bad_file_descriptor`. 372   file reports `errc::bad_file_descriptor`.
338   373  
339   @return The error code, empty on success. 374   @return The error code, empty on success.
340   */ 375   */
341   [[nodiscard]] std::error_code sync_data() noexcept; 376   [[nodiscard]] std::error_code sync_data() noexcept;
342   377  
343   /** Synchronize file data and metadata to stable storage. 378   /** Synchronize file data and metadata to stable storage.
344   379  
345   Write-back failures such as device I/O errors surface here 380   Write-back failures such as device I/O errors surface here
346   and are reported through the returned error code. A closed 381   and are reported through the returned error code. A closed
347   file reports `errc::bad_file_descriptor`. 382   file reports `errc::bad_file_descriptor`.
348   383  
349   @return The error code, empty on success. 384   @return The error code, empty on success.
350   */ 385   */
351   [[nodiscard]] std::error_code sync_all() noexcept; 386   [[nodiscard]] std::error_code sync_all() noexcept;
352   387  
353   /** Release ownership of the native handle. 388   /** Release ownership of the native handle.
354   389  
355   The file object becomes not-open. The caller is 390   The file object becomes not-open. The caller is
356   responsible for closing the returned handle. 391   responsible for closing the returned handle.
357   392  
358   @return The native file descriptor or handle. 393   @return The native file descriptor or handle.
359   394  
360   @throws std::system_error `errc::bad_file_descriptor` if the 395   @throws std::system_error `errc::bad_file_descriptor` if the
361   file is not open. 396   file is not open.
362   */ 397   */
363   native_handle_type release(); 398   native_handle_type release();
364   399  
365   /** Adopt an existing native handle. 400   /** Adopt an existing native handle.
366   401  
367   Closes any currently open file before adopting. 402   Closes any currently open file before adopting.
368   The file object takes ownership of the handle. Handles 403   The file object takes ownership of the handle. Handles
369   created elsewhere may be unsuitable for asynchronous I/O; 404   created elsewhere may be unsuitable for asynchronous I/O;
370   such failures are reported through the returned error code. 405   such failures are reported through the returned error code.
371   406  
372   @param handle The native file descriptor or handle. 407   @param handle The native file descriptor or handle.
373   408  
374   @return The error code, empty on success. 409   @return The error code, empty on success.
375   */ 410   */
376   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept; 411   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept;
377   412  
378   protected: 413   protected:
379   /// Construct from a pre-built handle (for native_random_access_file). 414   /// Construct from a pre-built handle (for native_random_access_file).
HITCBC 380   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {} 415   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {}
381   416  
382   private: 417   private:
HITCBC 383   1765 inline implementation& get() const noexcept 418   1765 inline implementation& get() const noexcept
384   { 419   {
HITCBC 385   1765 return *static_cast<implementation*>(h_.get()); 420   1765 return *static_cast<implementation*>(h_.get());
386   } 421   }
387   }; 422   };
388   423  
389   } // namespace boost::corosio 424   } // namespace boost::corosio
390   425  
391   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 426   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP