TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
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_NATIVE_NATIVE_STREAM_FILE_HPP
11 : #define BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP
12 :
13 : #include <boost/corosio/stream_file.hpp>
14 : #include <boost/corosio/backend.hpp>
15 :
16 : #ifndef BOOST_COROSIO_MRDOCS
17 : #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
18 : BOOST_COROSIO_HAS_KQUEUE
19 : #include <boost/corosio/native/detail/posix/posix_stream_file_service.hpp>
20 : #endif
21 :
22 : #if BOOST_COROSIO_HAS_IO_URING
23 : #include <boost/corosio/native/detail/io_uring/io_uring_stream_file.hpp>
24 : #endif
25 :
26 : #if BOOST_COROSIO_HAS_IOCP
27 : #include <boost/corosio/native/detail/iocp/win_file_service.hpp>
28 : #endif
29 : #endif // !BOOST_COROSIO_MRDOCS
30 :
31 : namespace boost::corosio {
32 :
33 : /** A sequential file with devirtualized async I/O operations.
34 :
35 : This class template inherits from @ref stream_file and shadows
36 : `read_some` / `write_some` with versions that call the backend
37 : implementation directly, allowing the compiler to inline through
38 : the entire call chain.
39 :
40 : Non-async operations (`open`, `close`, `size`, `resize`, `seek`,
41 : `sync_data`, `sync_all`) remain unchanged and dispatch through
42 : the compiled library.
43 :
44 : A `native_stream_file` IS-A `stream_file` and can be passed to
45 : any function expecting `stream_file&` or `io_stream&`, in which
46 : case virtual dispatch is used transparently.
47 :
48 : @note On POSIX platforms, file I/O is dispatched to a thread
49 : pool regardless of the chosen reactor backend, so all three
50 : reactor tags (`epoll`, `select`, `kqueue`) resolve to the same
51 : underlying implementation. The `Backend` template parameter
52 : exists for API symmetry with @ref native_tcp_socket and friends.
53 : The vtable savings are smaller relative to the thread-pool /
54 : overlapped-I/O cost than they are for socket operations.
55 :
56 : @tparam Backend A backend tag value (e.g., `epoll`, `iocp`).
57 :
58 : @par Thread Safety
59 : Same as @ref stream_file.
60 :
61 : @par Example
62 : @code
63 : #include <boost/corosio/native/native_stream_file.hpp>
64 :
65 : native_io_context<epoll> ctx;
66 : native_stream_file<epoll> f(ctx);
67 : if (auto ec = f.open("data.bin", file_base::read_only))
68 : co_return;
69 : char buf[4096];
70 : auto [ec, n] = co_await f.read_some(
71 : capy::mutable_buffer(buf, sizeof(buf)));
72 : @endcode
73 :
74 : @see stream_file, epoll_t, iocp_t
75 : */
76 : template<auto Backend>
77 : class native_stream_file : public stream_file
78 : {
79 : using backend_type = decltype(Backend);
80 : using impl_type = typename backend_type::stream_file_type;
81 : using service_type = typename backend_type::stream_file_service_type;
82 :
83 HIT 8 : impl_type& get_impl() noexcept
84 : {
85 8 : return *static_cast<impl_type*>(h_.get());
86 : }
87 :
88 : template<class MutableBufferSequence>
89 : struct native_read_awaitable
90 : {
91 : native_stream_file& self_;
92 : MutableBufferSequence buffers_;
93 : std::stop_token token_;
94 : mutable std::error_code ec_;
95 : mutable std::size_t bytes_transferred_ = 0;
96 :
97 4 : native_read_awaitable(
98 : native_stream_file& self,
99 : MutableBufferSequence buffers) noexcept
100 4 : : self_(self)
101 4 : , buffers_(std::move(buffers))
102 : {
103 4 : }
104 :
105 4 : bool await_ready() const noexcept
106 : {
107 : // A pre-set ec_ means the initiator failed before
108 : // dispatch (e.g. a closed object).
109 4 : return static_cast<bool>(ec_) || token_.stop_requested();
110 : }
111 :
112 4 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
113 : {
114 4 : if (token_.stop_requested())
115 MIS 0 : return {make_error_code(std::errc::operation_canceled), 0};
116 HIT 4 : return {ec_, bytes_transferred_};
117 : }
118 :
119 4 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
120 : -> std::coroutine_handle<>
121 : {
122 4 : token_ = env->stop_token;
123 12 : return self_.get_impl().read_some(
124 12 : h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
125 : }
126 : };
127 :
128 : template<class ConstBufferSequence>
129 : struct native_write_awaitable
130 : {
131 : native_stream_file& self_;
132 : ConstBufferSequence buffers_;
133 : std::stop_token token_;
134 : mutable std::error_code ec_;
135 : mutable std::size_t bytes_transferred_ = 0;
136 :
137 4 : native_write_awaitable(
138 : native_stream_file& self,
139 : ConstBufferSequence buffers) noexcept
140 4 : : self_(self)
141 4 : , buffers_(std::move(buffers))
142 : {
143 4 : }
144 :
145 4 : bool await_ready() const noexcept
146 : {
147 : // A pre-set ec_ means the initiator failed before
148 : // dispatch (e.g. a closed object).
149 4 : return static_cast<bool>(ec_) || token_.stop_requested();
150 : }
151 :
152 4 : [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
153 : {
154 4 : if (token_.stop_requested())
155 MIS 0 : return {make_error_code(std::errc::operation_canceled), 0};
156 HIT 4 : return {ec_, bytes_transferred_};
157 : }
158 :
159 4 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
160 : -> std::coroutine_handle<>
161 : {
162 4 : token_ = env->stop_token;
163 12 : return self_.get_impl().write_some(
164 12 : h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
165 : }
166 : };
167 :
168 : public:
169 : /** Construct a native stream file from an execution context.
170 :
171 : @param ctx The execution context that will own this file.
172 : */
173 12 : explicit native_stream_file(capy::execution_context& ctx)
174 12 : : io_object(create_handle<service_type>(ctx))
175 : {
176 12 : }
177 :
178 : /** Construct a native stream file from an executor.
179 :
180 : @param ex The executor whose context will own this file.
181 : */
182 : template<class Ex>
183 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_stream_file>) &&
184 : capy::Executor<Ex>
185 : explicit native_stream_file(Ex const& ex) : native_stream_file(ex.context())
186 : {
187 : }
188 :
189 : /// Move construct.
190 : native_stream_file(native_stream_file&&) noexcept = default;
191 :
192 : /// Move assign.
193 : native_stream_file& operator=(native_stream_file&&) noexcept = default;
194 :
195 : native_stream_file(native_stream_file const&) = delete;
196 : native_stream_file& operator=(native_stream_file const&) = delete;
197 :
198 : /** Asynchronously read data from the file.
199 :
200 : Calls the backend implementation directly, bypassing virtual
201 : dispatch. Otherwise identical to @ref io_stream::read_some.
202 : */
203 : template<capy::MutableBufferSequence MB>
204 4 : [[nodiscard]] auto read_some(MB const& buffers)
205 : {
206 4 : return native_read_awaitable<MB>(*this, buffers);
207 : }
208 :
209 : /** Asynchronously write data to the file.
210 :
211 : Calls the backend implementation directly, bypassing virtual
212 : dispatch. Otherwise identical to @ref io_stream::write_some.
213 : */
214 : template<capy::ConstBufferSequence CB>
215 4 : [[nodiscard]] auto write_some(CB const& buffers)
216 : {
217 4 : return native_write_awaitable<CB>(*this, buffers);
218 : }
219 : };
220 :
221 : } // namespace boost::corosio
222 :
223 : #endif // BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP
|