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