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