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