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