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