TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_STREAM_FILE_HPP
11 : #define BOOST_COROSIO_STREAM_FILE_HPP
12 :
13 : #include <boost/corosio/detail/config.hpp>
14 : #include <boost/corosio/detail/platform.hpp>
15 : #include <boost/corosio/detail/except.hpp>
16 : #include <boost/corosio/detail/native_handle.hpp>
17 : #include <boost/corosio/file_base.hpp>
18 : #include <boost/corosio/io/io_stream.hpp>
19 : #include <boost/capy/ex/execution_context.hpp>
20 : #include <boost/capy/concept/executor.hpp>
21 :
22 : #include <concepts>
23 : #include <cstdint>
24 : #include <filesystem>
25 :
26 : namespace boost::corosio {
27 :
28 : /** An asynchronous sequential file for coroutine I/O.
29 :
30 : Provides asynchronous read and write operations on a regular
31 : file with an implicit position that advances after each
32 : operation.
33 :
34 : Inherits from @ref io_stream, so `read_some` and `write_some`
35 : are available and work with any algorithm that accepts an
36 : `io_stream&`.
37 :
38 : On POSIX platforms, file I/O is dispatched to a thread pool
39 : (blocking `preadv`/`pwritev`) with completion posted back to
40 : the scheduler. On Windows, true overlapped I/O is used via IOCP.
41 :
42 : @par Thread Safety
43 : Distinct objects: Safe.@n
44 : Shared objects: Unsafe. Only one asynchronous operation
45 : may be in flight at a time.
46 :
47 : @par Example
48 : @code
49 : io_context ioc;
50 : stream_file f(ioc);
51 : f.open("data.bin", file_base::read_only);
52 :
53 : char buf[4096];
54 : auto [ec, n] = co_await f.read_some(
55 : capy::mutable_buffer(buf, sizeof(buf)));
56 : if (ec == capy::cond::eof)
57 : // end of file
58 : @endcode
59 : */
60 : class BOOST_COROSIO_DECL stream_file : public io_stream
61 : {
62 : public:
63 : /** Platform-specific file implementation interface.
64 :
65 : Backends derive from this to provide file I/O.
66 : `read_some` and `write_some` are inherited from
67 : @ref io_stream::implementation.
68 : */
69 : struct implementation : io_stream::implementation
70 : {
71 : /// Return the platform file descriptor or handle.
72 : virtual native_handle_type native_handle() const noexcept = 0;
73 :
74 : /// Cancel pending asynchronous operations.
75 : virtual void cancel() noexcept = 0;
76 :
77 : /// Return the file size in bytes.
78 : virtual std::uint64_t size() const = 0;
79 :
80 : /// Resize the file to @p new_size bytes.
81 : virtual void resize(std::uint64_t new_size) = 0;
82 :
83 : /// Synchronize file data to stable storage.
84 : virtual void sync_data() = 0;
85 :
86 : /// Synchronize file data and metadata to stable storage.
87 : virtual void sync_all() = 0;
88 :
89 : /// Release ownership of the native handle.
90 : virtual native_handle_type release() = 0;
91 :
92 : /// Adopt an existing native handle.
93 : virtual void assign(native_handle_type handle) = 0;
94 :
95 : /** Move the file position.
96 :
97 : @param offset Signed offset from @p origin.
98 : @param origin The reference point for the seek.
99 : @return The new absolute position.
100 : */
101 : virtual std::uint64_t
102 : seek(std::int64_t offset, file_base::seek_basis origin) = 0;
103 : };
104 :
105 : /** Destructor.
106 :
107 : Closes the file if open, cancelling any pending operations.
108 : */
109 : ~stream_file() override;
110 :
111 : /** Construct from an execution context.
112 :
113 : @param ctx The execution context that will own this file.
114 : */
115 : explicit stream_file(capy::execution_context& ctx);
116 :
117 : /** Construct from an executor.
118 :
119 : @param ex The executor whose context will own this file.
120 : */
121 : template<class Ex>
122 : requires(!std::same_as<std::remove_cvref_t<Ex>, stream_file>) &&
123 : capy::Executor<Ex>
124 HIT 1 : explicit stream_file(Ex const& ex) : stream_file(ex.context())
125 : {
126 1 : }
127 :
128 : /** Move constructor.
129 :
130 : Transfers ownership of the file resources.
131 : */
132 1 : stream_file(stream_file&& other) noexcept : io_object(std::move(other)) {}
133 :
134 : /** Move assignment operator.
135 :
136 : Closes any existing file and transfers ownership.
137 : */
138 1 : stream_file& operator=(stream_file&& other) noexcept
139 : {
140 1 : if (this != &other)
141 : {
142 1 : close();
143 1 : h_ = std::move(other.h_);
144 : }
145 1 : return *this;
146 : }
147 :
148 : stream_file(stream_file const&) = delete;
149 : stream_file& operator=(stream_file const&) = delete;
150 :
151 : // read_some() inherited from io_read_stream
152 : // write_some() inherited from io_write_stream
153 :
154 : /** Open a file.
155 :
156 : @param path The filesystem path to open.
157 : @param mode Bitmask of @ref file_base::flags specifying
158 : access mode and creation behavior.
159 :
160 : @throws std::system_error on failure.
161 : */
162 : void open(
163 : std::filesystem::path const& path,
164 : file_base::flags mode = file_base::read_only);
165 :
166 : /** Close the file.
167 :
168 : Releases file resources. Any pending operations complete
169 : with `errc::operation_canceled`.
170 : */
171 : void close();
172 :
173 : /** Check if the file is open.
174 :
175 : @return `true` if the file is open and ready for I/O.
176 : */
177 83 : bool is_open() const noexcept
178 : {
179 : #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
180 : return h_ && get().native_handle() != ~native_handle_type(0);
181 : #else
182 83 : return h_ && get().native_handle() >= 0;
183 : #endif
184 : }
185 :
186 : /** Cancel pending asynchronous operations.
187 :
188 : All outstanding operations complete with
189 : `errc::operation_canceled`.
190 : */
191 : void cancel();
192 :
193 : /** Get the native file descriptor or handle.
194 :
195 : @return The native handle, or -1/INVALID_HANDLE_VALUE
196 : if not open.
197 : */
198 : native_handle_type native_handle() const noexcept;
199 :
200 : /** Return the file size in bytes.
201 :
202 : @throws std::system_error on failure.
203 : */
204 : std::uint64_t size() const;
205 :
206 : /** Resize the file to @p new_size bytes.
207 :
208 : @param new_size The new file size.
209 : @throws std::system_error on failure.
210 : */
211 : void resize(std::uint64_t new_size);
212 :
213 : /** Synchronize file data to stable storage.
214 :
215 : @throws std::system_error on failure.
216 : */
217 : void sync_data();
218 :
219 : /** Synchronize file data and metadata to stable storage.
220 :
221 : @throws std::system_error on failure.
222 : */
223 : void sync_all();
224 :
225 : /** Release ownership of the native handle.
226 :
227 : The file object becomes not-open. The caller is
228 : responsible for closing the returned handle.
229 :
230 : @return The native file descriptor or handle.
231 : */
232 : native_handle_type release();
233 :
234 : /** Adopt an existing native handle.
235 :
236 : Closes any currently open file before adopting.
237 : The file object takes ownership of the handle.
238 :
239 : @param handle The native file descriptor or handle.
240 : @throws std::system_error on failure.
241 : */
242 : void assign(native_handle_type handle);
243 :
244 : /** Move the file position.
245 :
246 : @param offset Signed offset from @p origin.
247 : @param origin The reference point for the seek.
248 : @return The new absolute position.
249 : @throws std::system_error on failure.
250 : */
251 : std::uint64_t
252 : seek(std::int64_t offset,
253 : file_base::seek_basis origin = file_base::seek_set);
254 :
255 : private:
256 116 : inline implementation& get() const noexcept
257 : {
258 116 : return *static_cast<implementation*>(h_.get());
259 : }
260 : };
261 :
262 : } // namespace boost::corosio
263 :
264 : #endif // BOOST_COROSIO_STREAM_FILE_HPP
|