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