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