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 - f.open("data.bin", file_base::read_only); 67 + if (auto ec = f.open("data.bin", file_base::read_only))
  68 + co_return;
68   char buf[4096]; 69   char buf[4096];
69   auto [ec, n] = co_await f.read_some( 70   auto [ec, n] = co_await f.read_some(
70   capy::mutable_buffer(buf, sizeof(buf))); 71   capy::mutable_buffer(buf, sizeof(buf)));
71   @endcode 72   @endcode
72   73  
73   @see stream_file, epoll_t, iocp_t 74   @see stream_file, epoll_t, iocp_t
74   */ 75   */
75   template<auto Backend> 76   template<auto Backend>
76   class native_stream_file : public stream_file 77   class native_stream_file : public stream_file
77   { 78   {
78   using backend_type = decltype(Backend); 79   using backend_type = decltype(Backend);
79   using impl_type = typename backend_type::stream_file_type; 80   using impl_type = typename backend_type::stream_file_type;
80   using service_type = typename backend_type::stream_file_service_type; 81   using service_type = typename backend_type::stream_file_service_type;
81   82  
HITCBC 82   4 impl_type& get_impl() noexcept 83   8 impl_type& get_impl() noexcept
83   { 84   {
HITCBC 84   4 return *static_cast<impl_type*>(h_.get()); 85   8 return *static_cast<impl_type*>(h_.get());
85   } 86   }
86   87  
87   template<class MutableBufferSequence> 88   template<class MutableBufferSequence>
88   struct native_read_awaitable 89   struct native_read_awaitable
89   { 90   {
90   native_stream_file& self_; 91   native_stream_file& self_;
91   MutableBufferSequence buffers_; 92   MutableBufferSequence buffers_;
92   std::stop_token token_; 93   std::stop_token token_;
93   mutable std::error_code ec_; 94   mutable std::error_code ec_;
94   mutable std::size_t bytes_transferred_ = 0; 95   mutable std::size_t bytes_transferred_ = 0;
95   96  
HITCBC 96   2 native_read_awaitable( 97   4 native_read_awaitable(
97   native_stream_file& self, 98   native_stream_file& self,
98   MutableBufferSequence buffers) noexcept 99   MutableBufferSequence buffers) noexcept
HITCBC 99   2 : self_(self) 100   4 : self_(self)
HITCBC 100   2 , buffers_(std::move(buffers)) 101   4 , buffers_(std::move(buffers))
101   { 102   {
HITCBC 102   2 } 103   4 }
103   104  
HITCBC 104   2 bool await_ready() const noexcept 105   4 bool await_ready() const noexcept
105   { 106   {
ECB 106 - 2 return token_.stop_requested(); 107 + // A pre-set ec_ means the initiator failed before
  108 + // dispatch (e.g. a closed object).
HITGNC   109 + 4 return static_cast<bool>(ec_) || token_.stop_requested();
107   } 110   }
108   111  
HITCBC 109   2 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 112   4 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
110   { 113   {
HITCBC 111   2 if (token_.stop_requested()) 114   4 if (token_.stop_requested())
MISUBC 112   return {make_error_code(std::errc::operation_canceled), 0}; 115   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 113   2 return {ec_, bytes_transferred_}; 116   4 return {ec_, bytes_transferred_};
114   } 117   }
115   118  
HITCBC 116   2 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)
117   -> std::coroutine_handle<> 120   -> std::coroutine_handle<>
118   { 121   {
HITCBC 119   2 token_ = env->stop_token; 122   4 token_ = env->stop_token;
HITCBC 120   6 return self_.get_impl().read_some( 123   12 return self_.get_impl().read_some(
HITCBC 121   6 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 124   12 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
122   } 125   }
123   }; 126   };
124   127  
125   template<class ConstBufferSequence> 128   template<class ConstBufferSequence>
126   struct native_write_awaitable 129   struct native_write_awaitable
127   { 130   {
128   native_stream_file& self_; 131   native_stream_file& self_;
129   ConstBufferSequence buffers_; 132   ConstBufferSequence buffers_;
130   std::stop_token token_; 133   std::stop_token token_;
131   mutable std::error_code ec_; 134   mutable std::error_code ec_;
132   mutable std::size_t bytes_transferred_ = 0; 135   mutable std::size_t bytes_transferred_ = 0;
133   136  
HITCBC 134   2 native_write_awaitable( 137   4 native_write_awaitable(
135   native_stream_file& self, 138   native_stream_file& self,
136   ConstBufferSequence buffers) noexcept 139   ConstBufferSequence buffers) noexcept
HITCBC 137   2 : self_(self) 140   4 : self_(self)
HITCBC 138   2 , buffers_(std::move(buffers)) 141   4 , buffers_(std::move(buffers))
139   { 142   {
HITCBC 140   2 } 143   4 }
141   144  
HITCBC 142   2 bool await_ready() const noexcept 145   4 bool await_ready() const noexcept
143   { 146   {
ECB 144 - 2 return token_.stop_requested(); 147 + // A pre-set ec_ means the initiator failed before
  148 + // dispatch (e.g. a closed object).
HITGNC   149 + 4 return static_cast<bool>(ec_) || token_.stop_requested();
145   } 150   }
146   151  
HITCBC 147   2 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 152   4 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
148   { 153   {
HITCBC 149   2 if (token_.stop_requested()) 154   4 if (token_.stop_requested())
MISUBC 150   return {make_error_code(std::errc::operation_canceled), 0}; 155   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 151   2 return {ec_, bytes_transferred_}; 156   4 return {ec_, bytes_transferred_};
152   } 157   }
153   158  
HITCBC 154   2 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)
155   -> std::coroutine_handle<> 160   -> std::coroutine_handle<>
156   { 161   {
HITCBC 157   2 token_ = env->stop_token; 162   4 token_ = env->stop_token;
HITCBC 158   6 return self_.get_impl().write_some( 163   12 return self_.get_impl().write_some(
HITCBC 159   6 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 164   12 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
160   } 165   }
161   }; 166   };
162   167  
163   public: 168   public:
164   /** Construct a native stream file from an execution context. 169   /** Construct a native stream file from an execution context.
165   170  
166   @param ctx The execution context that will own this file. 171   @param ctx The execution context that will own this file.
167   */ 172   */
HITCBC 168   10 explicit native_stream_file(capy::execution_context& ctx) 173   12 explicit native_stream_file(capy::execution_context& ctx)
HITCBC 169   10 : io_object(create_handle<service_type>(ctx)) 174   12 : io_object(create_handle<service_type>(ctx))
170   { 175   {
HITCBC 171   10 } 176   12 }
172   177  
173   /** Construct a native stream file from an executor. 178   /** Construct a native stream file from an executor.
174   179  
175   @param ex The executor whose context will own this file. 180   @param ex The executor whose context will own this file.
176   */ 181   */
177   template<class Ex> 182   template<class Ex>
178   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>) &&
179   capy::Executor<Ex> 184   capy::Executor<Ex>
180   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())
181   { 186   {
182   } 187   }
183   188  
184   /// Move construct. 189   /// Move construct.
185   native_stream_file(native_stream_file&&) noexcept = default; 190   native_stream_file(native_stream_file&&) noexcept = default;
186   191  
187   /// Move assign. 192   /// Move assign.
188   native_stream_file& operator=(native_stream_file&&) noexcept = default; 193   native_stream_file& operator=(native_stream_file&&) noexcept = default;
189   194  
190   native_stream_file(native_stream_file const&) = delete; 195   native_stream_file(native_stream_file const&) = delete;
191   native_stream_file& operator=(native_stream_file const&) = delete; 196   native_stream_file& operator=(native_stream_file const&) = delete;
192   197  
193   /** Asynchronously read data from the file. 198   /** Asynchronously read data from the file.
194   199  
195   Calls the backend implementation directly, bypassing virtual 200   Calls the backend implementation directly, bypassing virtual
196   dispatch. Otherwise identical to @ref io_stream::read_some. 201   dispatch. Otherwise identical to @ref io_stream::read_some.
197   */ 202   */
198   template<capy::MutableBufferSequence MB> 203   template<capy::MutableBufferSequence MB>
HITCBC 199 - 2 auto read_some(MB const& buffers) 204 + 4 [[nodiscard]] auto read_some(MB const& buffers)
200   { 205   {
HITCBC 201   2 return native_read_awaitable<MB>(*this, buffers); 206   4 return native_read_awaitable<MB>(*this, buffers);
202   } 207   }
203   208  
204   /** Asynchronously write data to the file. 209   /** Asynchronously write data to the file.
205   210  
206   Calls the backend implementation directly, bypassing virtual 211   Calls the backend implementation directly, bypassing virtual
207   dispatch. Otherwise identical to @ref io_stream::write_some. 212   dispatch. Otherwise identical to @ref io_stream::write_some.
208   */ 213   */
209   template<capy::ConstBufferSequence CB> 214   template<capy::ConstBufferSequence CB>
HITCBC 210 - 2 auto write_some(CB const& buffers) 215 + 4 [[nodiscard]] auto write_some(CB const& buffers)
211   { 216   {
HITCBC 212   2 return native_write_awaitable<CB>(*this, buffers); 217   4 return native_write_awaitable<CB>(*this, buffers);
213   } 218   }
214   }; 219   };
215   220  
216   } // namespace boost::corosio 221   } // namespace boost::corosio
217   222  
218   #endif // BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP 223   #endif // BOOST_COROSIO_NATIVE_NATIVE_STREAM_FILE_HPP