99.21% Lines (126/127) 100.00% Functions (16/16)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
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_DETAIL_POSIX_POSIX_STREAM_FILE_HPP 10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP
11   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP 11   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP
12   12  
13   #include <boost/corosio/detail/platform.hpp> 13   #include <boost/corosio/detail/platform.hpp>
14   14  
15   #if BOOST_COROSIO_POSIX 15   #if BOOST_COROSIO_POSIX
16   16  
17   #include <boost/corosio/detail/config.hpp> 17   #include <boost/corosio/detail/config.hpp>
18   #include <boost/corosio/stream_file.hpp> 18   #include <boost/corosio/stream_file.hpp>
19   #include <boost/corosio/file_base.hpp> 19   #include <boost/corosio/file_base.hpp>
20   #include <boost/corosio/detail/intrusive.hpp> 20   #include <boost/corosio/detail/intrusive.hpp>
21   #include <boost/corosio/detail/dispatch_coro.hpp> 21   #include <boost/corosio/detail/dispatch_coro.hpp>
22   #include <boost/corosio/detail/scheduler_op.hpp> 22   #include <boost/corosio/detail/scheduler_op.hpp>
23   #include <boost/corosio/detail/thread_pool.hpp> 23   #include <boost/corosio/detail/thread_pool.hpp>
24   #include <boost/corosio/detail/scheduler.hpp> 24   #include <boost/corosio/detail/scheduler.hpp>
25   #include <boost/corosio/detail/buffer_param.hpp> 25   #include <boost/corosio/detail/buffer_param.hpp>
  26 + #include <boost/corosio/native/detail/coro_op.hpp>
26   #include <boost/corosio/native/detail/make_err.hpp> 27   #include <boost/corosio/native/detail/make_err.hpp>
27   #include <boost/capy/ex/executor_ref.hpp> 28   #include <boost/capy/ex/executor_ref.hpp>
28   #include <boost/capy/error.hpp> 29   #include <boost/capy/error.hpp>
29   #include <boost/capy/buffers.hpp> 30   #include <boost/capy/buffers.hpp>
30   31  
31   #include <atomic> 32   #include <atomic>
32   #include <coroutine> 33   #include <coroutine>
33   #include <cstddef> 34   #include <cstddef>
34   #include <cstdint> 35   #include <cstdint>
35   #include <filesystem> 36   #include <filesystem>
36   #include <limits> 37   #include <limits>
37   #include <memory> 38   #include <memory>
38   #include <optional> 39   #include <optional>
39   #include <stop_token> 40   #include <stop_token>
40   #include <system_error> 41   #include <system_error>
41   42  
42   #include <errno.h> 43   #include <errno.h>
43   #include <fcntl.h> 44   #include <fcntl.h>
44   #include <sys/stat.h> 45   #include <sys/stat.h>
45   #include <sys/uio.h> 46   #include <sys/uio.h>
46   #include <unistd.h> 47   #include <unistd.h>
47   48  
48   /* 49   /*
49   POSIX Stream File Implementation 50   POSIX Stream File Implementation
50   ================================= 51   =================================
51   52  
52   Regular files cannot be monitored by epoll/kqueue/select — the kernel 53   Regular files cannot be monitored by epoll/kqueue/select — the kernel
53   always reports them as ready. Blocking I/O (pread/pwrite) is dispatched 54   always reports them as ready. Blocking I/O (pread/pwrite) is dispatched
54   to a shared thread pool, with completion posted back to the scheduler. 55   to a shared thread pool, with completion posted back to the scheduler.
55   56  
56   This follows the same pattern as posix_resolver: pool_work_item for 57   This follows the same pattern as posix_resolver: pool_work_item for
57   dispatch, scheduler_op for completion, shared_from_this for lifetime. 58   dispatch, scheduler_op for completion, shared_from_this for lifetime.
58   59  
59   Completion Flow 60   Completion Flow
60   --------------- 61   ---------------
61   1. read_some() sets up file_read_op, posts to thread pool 62   1. read_some() sets up file_read_op, posts to thread pool
62   2. Pool thread runs preadv() (blocking) 63   2. Pool thread runs preadv() (blocking)
63   3. Pool thread stores results, posts scheduler_op to scheduler 64   3. Pool thread stores results, posts scheduler_op to scheduler
64   4. Scheduler invokes op() which resumes the coroutine 65   4. Scheduler invokes op() which resumes the coroutine
65   66  
66   Single-Inflight Constraint 67   Single-Inflight Constraint
67   -------------------------- 68   --------------------------
68   Only one asynchronous operation may be in flight at a time on a 69   Only one asynchronous operation may be in flight at a time on a
69   given file object. Concurrent read and write is not supported 70   given file object. Concurrent read and write is not supported
70   because both share offset_ without synchronization. 71   because both share offset_ without synchronization.
71   */ 72   */
72   73  
73   namespace boost::corosio::detail { 74   namespace boost::corosio::detail {
74   75  
75   struct scheduler; 76   struct scheduler;
76   class posix_stream_file_service; 77   class posix_stream_file_service;
77   78  
78   /** Stream file implementation for POSIX backends. 79   /** Stream file implementation for POSIX backends.
79   80  
80   Each instance contains embedded operation objects (read_op_, write_op_) 81   Each instance contains embedded operation objects (read_op_, write_op_)
81   that are reused across calls. This avoids per-operation heap allocation. 82   that are reused across calls. This avoids per-operation heap allocation.
82   */ 83   */
83   class posix_stream_file final 84   class posix_stream_file final
84   : public stream_file::implementation 85   : public stream_file::implementation
85   , public std::enable_shared_from_this<posix_stream_file> 86   , public std::enable_shared_from_this<posix_stream_file>
86   , public intrusive_list<posix_stream_file>::node 87   , public intrusive_list<posix_stream_file>::node
87   { 88   {
88   friend class posix_stream_file_service; 89   friend class posix_stream_file_service;
89   90  
90   public: 91   public:
91   static constexpr std::size_t max_buffers = 16; 92   static constexpr std::size_t max_buffers = 16;
92   93  
93 - /** Operation state for a single file read or write. */ 94 + /** Operation state for a single file read or write.
94 - struct file_op : scheduler_op  
95 - {  
96 - struct canceller  
97 - {  
98 - file_op* op;  
DCB 99 - 2 void operator()() const noexcept  
100 - {  
DCB 101 - 2 op->request_cancel();  
DCB 102 - 2 }  
103 - };  
104 -  
105 - // Coroutine state  
106 - std::coroutine_handle<> h;  
107 - capy::continuation cont;  
108 - capy::executor_ref ex;  
109 -  
110 - // Output pointers  
111 - std::error_code* ec_out = nullptr;  
112 - std::size_t* bytes_out = nullptr;  
113   95  
  96 + The coroutine, cancellation and keepalive machinery is inherited
  97 + from `coro_op`; only the pool-path result state lives here.
  98 + */
  99 + struct file_op : coro_op
  100 + {
114   // Buffer data (copied from buffer_param at submission time) 101   // Buffer data (copied from buffer_param at submission time)
115   iovec iovecs[max_buffers]; 102   iovec iovecs[max_buffers];
116   int iovec_count = 0; 103   int iovec_count = 0;
117   104  
118   // Result storage (populated by worker thread) 105   // Result storage (populated by worker thread)
119   int errn = 0; 106   int errn = 0;
120 - bool is_read = false;  
121 -  
122 - // Thread coordination  
123 - std::atomic<bool> cancelled{false};  
124 - std::optional<std::stop_callback<canceller>> stop_cb;  
125 -  
126 - /// Prevents use-after-free when file is closed with pending ops.  
127 - std::shared_ptr<void> impl_ref;  
128   std::size_t bytes_transferred = 0; 107   std::size_t bytes_transferred = 0;
129   108  
HITCBC 130   190 file_op() = default; 109   228 file_op() = default;
131   110  
HITCBC 132   55 void reset() noexcept 111   77 void reset() noexcept
133   { 112   {
HITCBC 134   55 iovec_count = 0; 113   77 iovec_count = 0;
HITCBC 135   55 errn = 0; 114   77 errn = 0;
HITCBC 136   55 bytes_transferred = 0; 115   77 bytes_transferred = 0;
HITCBC 137   55 is_read = false; 116   77 is_read = false;
HITCBC 138   55 cancelled.store(false, std::memory_order_relaxed); 117   77 cancelled.store(false, std::memory_order_relaxed);
HITCBC 139   55 stop_cb.reset(); 118   77 stop_cb.reset();
HITCBC 140 - 55 impl_ref.reset(); 119 + 77 impl_ptr.reset();
HITCBC 141   55 ec_out = nullptr; 120   77 ec_out = nullptr;
HITCBC 142   55 bytes_out = nullptr; 121   77 bytes_out = nullptr;
HITCBC 143   55 } 122   77 }
144   123  
145   void operator()() override; 124   void operator()() override;
146 -  
147 - void request_cancel() noexcept  
DCB 148 - 532 {  
149 - cancelled.store(true, std::memory_order_release);  
DCB 150 - 532 }  
DCB 151 - 532  
152 - void start(std::stop_token const& token)  
DCB 153 - 39 {  
154 - cancelled.store(false, std::memory_order_release);  
DCB 155 - 39 stop_cb.reset();  
DCB 156 - 39 if (token.stop_possible())  
DCB 157 - 39 stop_cb.emplace(token, canceller{this});  
DCB 158 - 2 }  
ECB 159   39 void destroy() override; 125   void destroy() override;
160   }; 126   };
161   127  
162   /** Pool work item for thread pool dispatch. */ 128   /** Pool work item for thread pool dispatch. */
163   struct pool_op : pool_work_item 129   struct pool_op : pool_work_item
164   { 130   {
165   posix_stream_file* file_ = nullptr; 131   posix_stream_file* file_ = nullptr;
166   std::shared_ptr<posix_stream_file> ref_; 132   std::shared_ptr<posix_stream_file> ref_;
167   }; 133   };
168   134  
169   explicit posix_stream_file(posix_stream_file_service& svc) noexcept; 135   explicit posix_stream_file(posix_stream_file_service& svc) noexcept;
170   136  
171   // -- io_stream::implementation -- 137   // -- io_stream::implementation --
172   138  
173   std::coroutine_handle<> read_some( 139   std::coroutine_handle<> read_some(
174   std::coroutine_handle<>, 140   std::coroutine_handle<>,
175   capy::executor_ref, 141   capy::executor_ref,
176   buffer_param, 142   buffer_param,
177   std::stop_token, 143   std::stop_token,
178   std::error_code*, 144   std::error_code*,
179   std::size_t*) override; 145   std::size_t*) override;
180   146  
181   std::coroutine_handle<> write_some( 147   std::coroutine_handle<> write_some(
182   std::coroutine_handle<>, 148   std::coroutine_handle<>,
183   capy::executor_ref, 149   capy::executor_ref,
184   buffer_param, 150   buffer_param,
185   std::stop_token, 151   std::stop_token,
186   std::error_code*, 152   std::error_code*,
187   std::size_t*) override; 153   std::size_t*) override;
188   154  
189   // -- stream_file::implementation -- 155   // -- stream_file::implementation --
190   156  
HITCBC 191   302 native_handle_type native_handle() const noexcept override 157   393 native_handle_type native_handle() const noexcept override
192   { 158   {
HITCBC 193   302 return fd_; 159   393 return fd_;
194   } 160   }
195   161  
HITCBC 196   265 void cancel() noexcept override 162   313 void cancel() noexcept override
197   { 163   {
HITCBC 198   265 read_op_.request_cancel(); 164   313 read_op_.request_cancel();
HITCBC 199   265 write_op_.request_cancel(); 165   313 write_op_.request_cancel();
HITCBC 200   265 } 166   313 }
201   167  
202   std::uint64_t size() const override; 168   std::uint64_t size() const override;
203   std::error_code resize(std::uint64_t new_size) noexcept override; 169   std::error_code resize(std::uint64_t new_size) noexcept override;
204   std::error_code sync_data() noexcept override; 170   std::error_code sync_data() noexcept override;
205   std::error_code sync_all() noexcept override; 171   std::error_code sync_all() noexcept override;
206   native_handle_type release() override; 172   native_handle_type release() override;
207   std::error_code assign(native_handle_type handle) noexcept override; 173   std::error_code assign(native_handle_type handle) noexcept override;
208   capy::io_result<std::uint64_t> 174   capy::io_result<std::uint64_t>
209   seek(std::int64_t offset, file_base::seek_basis origin) noexcept override; 175   seek(std::int64_t offset, file_base::seek_basis origin) noexcept override;
210   176  
211   // -- Internal -- 177   // -- Internal --
212   178  
213   /** Open the file and store the fd. */ 179   /** Open the file and store the fd. */
214   std::error_code open_file( 180   std::error_code open_file(
215   std::filesystem::path const& path, file_base::flags mode); 181   std::filesystem::path const& path, file_base::flags mode);
216   182  
217   /** Close the file descriptor. */ 183   /** Close the file descriptor. */
218   void close_file() noexcept; 184   void close_file() noexcept;
219   185  
220   private: 186   private:
221   posix_stream_file_service& svc_; 187   posix_stream_file_service& svc_;
222   int fd_ = -1; 188   int fd_ = -1;
223   std::uint64_t offset_ = 0; 189   std::uint64_t offset_ = 0;
224   190  
225   file_op read_op_; 191   file_op read_op_;
226   file_op write_op_; 192   file_op write_op_;
227   pool_op read_pool_op_; 193   pool_op read_pool_op_;
228   pool_op write_pool_op_; 194   pool_op write_pool_op_;
229   195  
230   static void do_read_work(pool_work_item*) noexcept; 196   static void do_read_work(pool_work_item*) noexcept;
231   static void do_write_work(pool_work_item*) noexcept; 197   static void do_write_work(pool_work_item*) noexcept;
232   }; 198   };
233   199  
234   // --------------------------------------------------------------------------- 200   // ---------------------------------------------------------------------------
235   // Inline implementation 201   // Inline implementation
236   // --------------------------------------------------------------------------- 202   // ---------------------------------------------------------------------------
237   203  
238   inline 204   inline
HITCBC 239   95 posix_stream_file::posix_stream_file(posix_stream_file_service& svc) noexcept 205   114 posix_stream_file::posix_stream_file(posix_stream_file_service& svc) noexcept
HITCBC 240   95 : svc_(svc) 206   114 : svc_(svc)
241   { 207   {
HITCBC 242   95 } 208   114 }
243   209  
244   inline std::error_code 210   inline std::error_code
HITCBC 245   73 posix_stream_file::open_file( 211   97 posix_stream_file::open_file(
246   std::filesystem::path const& path, file_base::flags mode) 212   std::filesystem::path const& path, file_base::flags mode)
247   { 213   {
HITCBC 248   73 close_file(); 214   97 close_file();
249   215  
HITCBC 250   73 int oflags = 0; 216   97 int oflags = 0;
251   217  
252   // Access mode 218   // Access mode
HITCBC 253   73 unsigned access = static_cast<unsigned>(mode) & 3u; 219   97 unsigned access = static_cast<unsigned>(mode) & 3u;
HITCBC 254   73 if (access == static_cast<unsigned>(file_base::read_write)) 220   97 if (access == static_cast<unsigned>(file_base::read_write))
HITCBC 255   4 oflags |= O_RDWR; 221   19 oflags |= O_RDWR;
HITCBC 256   69 else if (access == static_cast<unsigned>(file_base::write_only)) 222   78 else if (access == static_cast<unsigned>(file_base::write_only))
HITCBC 257   24 oflags |= O_WRONLY; 223   29 oflags |= O_WRONLY;
258   else 224   else
HITCBC 259   45 oflags |= O_RDONLY; 225   49 oflags |= O_RDONLY;
260   226  
261   // Creation flags 227   // Creation flags
HITCBC 262   73 if ((mode & file_base::create) != file_base::flags(0)) 228   97 if ((mode & file_base::create) != file_base::flags(0))
HITCBC 263   20 oflags |= O_CREAT; 229   40 oflags |= O_CREAT;
HITCBC 264   73 if ((mode & file_base::exclusive) != file_base::flags(0)) 230   97 if ((mode & file_base::exclusive) != file_base::flags(0))
HITCBC 265   2 oflags |= O_EXCL; 231   2 oflags |= O_EXCL;
HITCBC 266   73 if ((mode & file_base::truncate) != file_base::flags(0)) 232   97 if ((mode & file_base::truncate) != file_base::flags(0))
HITCBC 267   17 oflags |= O_TRUNC; 233   17 oflags |= O_TRUNC;
HITCBC 268   73 if ((mode & file_base::append) != file_base::flags(0)) 234   97 if ((mode & file_base::append) != file_base::flags(0))
HITCBC 269   3 oflags |= O_APPEND; 235   8 oflags |= O_APPEND;
HITCBC 270   73 if ((mode & file_base::sync_all_on_write) != file_base::flags(0)) 236   97 if ((mode & file_base::sync_all_on_write) != file_base::flags(0))
HITCBC 271   2 oflags |= O_SYNC; 237   2 oflags |= O_SYNC;
272   238  
HITCBC 273   73 int fd = ::open(path.c_str(), oflags, 0666); 239   97 int fd = ::open(path.c_str(), oflags, 0666);
HITCBC 274   73 if (fd < 0) 240   97 if (fd < 0)
HITCBC 275   4 return make_err(errno); 241   9 return make_err(errno);
276   242  
HITCBC 277   69 fd_ = fd; 243   88 fd_ = fd;
HITCBC 278   69 offset_ = 0; 244   88 offset_ = 0;
279   245  
280   // Append mode: position at end-of-file (preadv/pwritev use 246   // Append mode: position at end-of-file (preadv/pwritev use
281   // explicit offsets, so O_APPEND alone is not sufficient). 247   // explicit offsets, so O_APPEND alone is not sufficient).
HITCBC 282   69 if ((mode & file_base::append) != file_base::flags(0)) 248   88 if ((mode & file_base::append) != file_base::flags(0))
283   { 249   {
284   struct stat st; 250   struct stat st;
HITCBC 285   3 if (::fstat(fd, &st) < 0) 251   8 if (::fstat(fd, &st) < 0)
286   { 252   {
HITGBC 287   int err = errno; 253   5 int err = errno;
HITGBC 288   ::close(fd); 254   5 ::close(fd);
HITGBC 289   fd_ = -1; 255   5 fd_ = -1;
HITGBC 290   return make_err(err); 256   5 return make_err(err);
291   } 257   }
HITCBC 292   3 offset_ = static_cast<std::uint64_t>(st.st_size); 258   3 offset_ = static_cast<std::uint64_t>(st.st_size);
293   } 259   }
294   260  
295   #ifdef POSIX_FADV_SEQUENTIAL 261   #ifdef POSIX_FADV_SEQUENTIAL
HITCBC 296   69 ::posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL); 262   83 ::posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL);
297   #endif 263   #endif
298   264  
HITCBC 299   69 return {}; 265   83 return {};
300   } 266   }
301   267  
302   inline void 268   inline void
HITCBC 303   342 posix_stream_file::close_file() noexcept 269   414 posix_stream_file::close_file() noexcept
304   { 270   {
HITCBC 305   342 if (fd_ >= 0) 271   414 if (fd_ >= 0)
306   { 272   {
HITCBC 307   73 ::close(fd_); 273   87 ::close(fd_);
HITCBC 308   73 fd_ = -1; 274   87 fd_ = -1;
309   } 275   }
HITCBC 310   342 } 276   414 }
311   277  
312   inline std::uint64_t 278   inline std::uint64_t
HITCBC 313   12 posix_stream_file::size() const 279   17 posix_stream_file::size() const
314   { 280   {
315   struct stat st; 281   struct stat st;
HITCBC 316   12 if (::fstat(fd_, &st) < 0) 282   17 if (::fstat(fd_, &st) < 0)
HITGBC 317   throw_system_error(make_err(errno), "stream_file::size"); 283   5 throw_system_error(make_err(errno), "stream_file::size");
HITCBC 318   12 return static_cast<std::uint64_t>(st.st_size); 284   12 return static_cast<std::uint64_t>(st.st_size);
319   } 285   }
320   286  
321   inline std::error_code 287   inline std::error_code
HITCBC 322   7 posix_stream_file::resize(std::uint64_t new_size) noexcept 288   12 posix_stream_file::resize(std::uint64_t new_size) noexcept
323   { 289   {
HITCBC 324   7 if (new_size > 290   12 if (new_size >
HITCBC 325   7 static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)())) 291   12 static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)()))
HITCBC 326   2 return make_err(EOVERFLOW); 292   2 return make_err(EOVERFLOW);
HITCBC 327   5 if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0) 293   10 if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0)
HITCBC 328   2 return make_err(errno); 294   7 return make_err(errno);
HITCBC 329   3 return {}; 295   3 return {};
330   } 296   }
331   297  
332   inline std::error_code 298   inline std::error_code
HITCBC 333   5 posix_stream_file::sync_data() noexcept 299   10 posix_stream_file::sync_data() noexcept
334   { 300   {
335   #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 301   #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
HITCBC 336   5 if (::fdatasync(fd_) < 0) 302   10 if (::fdatasync(fd_) < 0)
337   #else // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 303   #else // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
338   if (::fsync(fd_) < 0) 304   if (::fsync(fd_) < 0)
339   #endif // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 305   #endif // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
HITCBC 340   2 return make_err(errno); 306   7 return make_err(errno);
HITCBC 341   3 return {}; 307   3 return {};
342   } 308   }
343   309  
344   inline std::error_code 310   inline std::error_code
HITCBC 345   5 posix_stream_file::sync_all() noexcept 311   10 posix_stream_file::sync_all() noexcept
346   { 312   {
HITCBC 347   5 if (::fsync(fd_) < 0) 313   10 if (::fsync(fd_) < 0)
HITCBC 348   2 return make_err(errno); 314   7 return make_err(errno);
HITCBC 349   3 return {}; 315   3 return {};
350   } 316   }
351   317  
352   inline native_handle_type 318   inline native_handle_type
HITCBC 353   2 posix_stream_file::release() 319   2 posix_stream_file::release()
354   { 320   {
HITCBC 355   2 int fd = fd_; 321   2 int fd = fd_;
HITCBC 356   2 fd_ = -1; 322   2 fd_ = -1;
HITCBC 357   2 offset_ = 0; 323   2 offset_ = 0;
HITCBC 358   2 return fd; 324   2 return fd;
359   } 325   }
360   326  
361   inline std::error_code 327   inline std::error_code
HITCBC 362   6 posix_stream_file::assign(native_handle_type handle) noexcept 328   6 posix_stream_file::assign(native_handle_type handle) noexcept
363   { 329   {
HITCBC 364   6 close_file(); 330   6 close_file();
HITCBC 365   6 fd_ = handle; 331   6 fd_ = handle;
HITCBC 366   6 offset_ = 0; 332   6 offset_ = 0;
HITCBC 367   6 return {}; 333   6 return {};
368   } 334   }
369   335  
370   inline capy::io_result<std::uint64_t> 336   inline capy::io_result<std::uint64_t>
HITCBC 371   20 posix_stream_file::seek( 337   30 posix_stream_file::seek(
372   std::int64_t offset, file_base::seek_basis origin) noexcept 338   std::int64_t offset, file_base::seek_basis origin) noexcept
373   { 339   {
374   // We track offset_ ourselves (not the kernel fd offset) 340   // We track offset_ ourselves (not the kernel fd offset)
375   // because preadv/pwritev use explicit offsets. 341   // because preadv/pwritev use explicit offsets.
376   std::int64_t new_pos; 342   std::int64_t new_pos;
377   343  
HITCBC 378   20 if (origin == file_base::seek_set) 344   30 if (origin == file_base::seek_set)
379   { 345   {
HITCBC 380   9 new_pos = offset; 346   14 new_pos = offset;
381   } 347   }
HITCBC 382   11 else if (origin == file_base::seek_cur) 348   16 else if (origin == file_base::seek_cur)
383   { 349   {
HITCBC 384   5 new_pos = static_cast<std::int64_t>(offset_) + offset; 350   5 new_pos = static_cast<std::int64_t>(offset_) + offset;
385   } 351   }
386   else 352   else
387   { 353   {
388   struct stat st; 354   struct stat st;
HITCBC 389   6 if (::fstat(fd_, &st) < 0) 355   11 if (::fstat(fd_, &st) < 0)
HITGBC 390   return {make_err(errno), 0}; 356   5 return {make_err(errno), 0};
HITCBC 391   6 new_pos = st.st_size + offset; 357   6 new_pos = st.st_size + offset;
392   } 358   }
393   359  
HITCBC 394   20 if (new_pos < 0) 360   25 if (new_pos < 0)
HITCBC 395   6 return {make_err(EINVAL), 0}; 361   6 return {make_err(EINVAL), 0};
HITCBC 396   14 if (new_pos > 362   19 if (new_pos >
HITCBC 397   14 static_cast<std::int64_t>((std::numeric_limits<off_t>::max)())) 363   19 static_cast<std::int64_t>((std::numeric_limits<off_t>::max)()))
MISUBC 398   return {make_err(EOVERFLOW), 0}; 364   return {make_err(EOVERFLOW), 0};
399   365  
HITCBC 400   14 offset_ = static_cast<std::uint64_t>(new_pos); 366   19 offset_ = static_cast<std::uint64_t>(new_pos);
401   367  
HITCBC 402   14 return {std::error_code{}, offset_}; 368   19 return {std::error_code{}, offset_};
403   } 369   }
404   370  
405   // -- file_op completion handler -- 371   // -- file_op completion handler --
406   // (read_some, write_some, do_read_work, do_write_work are 372   // (read_some, write_some, do_read_work, do_write_work are
407   // defined in posix_stream_file_service.hpp after the service) 373   // defined in posix_stream_file_service.hpp after the service)
408   374  
409   inline void 375   inline void
HITCBC 410   39 posix_stream_file::file_op::operator()() 376   59 posix_stream_file::file_op::operator()()
411   { 377   {
HITCBC 412   39 stop_cb.reset(); 378   59 stop_cb.reset();
413   379  
HITCBC 414   39 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 380   59 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
415   381  
HITCBC 416   39 if (ec_out) 382   59 if (ec_out)
417   { 383   {
HITCBC 418   39 if (was_cancelled) 384   59 if (was_cancelled)
HITCBC 419   2 *ec_out = capy::error::canceled; 385   2 *ec_out = capy::error::canceled;
HITCBC 420   37 else if (errn != 0) 386   57 else if (errn != 0)
HITCBC 421   4 *ec_out = make_err(errn); 387   14 *ec_out = make_err(errn);
HITCBC 422   33 else if (is_read && bytes_transferred == 0) 388   43 else if (is_read && bytes_transferred == 0)
HITCBC 423   3 *ec_out = capy::error::eof; 389   8 *ec_out = capy::error::eof;
424   else 390   else
HITCBC 425   30 *ec_out = {}; 391   35 *ec_out = {};
426   } 392   }
427   393  
HITCBC 428   39 if (bytes_out) 394   59 if (bytes_out)
HITCBC 429   39 *bytes_out = was_cancelled ? 0 : bytes_transferred; 395   59 *bytes_out = was_cancelled ? 0 : bytes_transferred;
430   396  
431 - // Move impl_ref to a local so members remain valid through 397 + // Move impl_ptr to a local so members remain valid through
432 - // dispatch — impl_ref may be the last shared_ptr keeping 398 + // dispatch — impl_ptr may be the last shared_ptr keeping
433   // the parent posix_stream_file (which embeds this file_op) alive. 399   // the parent posix_stream_file (which embeds this file_op) alive.
HITCBC 434 - 39 auto prevent_destroy = std::move(impl_ref); 400 + 59 auto prevent_destroy = std::move(impl_ptr);
HITCBC 435   39 ex.on_work_finished(); 401   59 ex.on_work_finished();
HITCBC 436   39 cont.h = h; 402   59 cont.h = h;
HITCBC 437   39 dispatch_coro(ex, cont).resume(); 403   59 dispatch_coro(ex, cont).resume();
HITCBC 438   39 } 404   59 }
439   405  
440   inline void 406   inline void
HITGBC 441   posix_stream_file::file_op::destroy() 407   2 posix_stream_file::file_op::destroy()
442   { 408   {
HITGBC 443   stop_cb.reset(); 409   2 stop_cb.reset();
HITGBC 444   auto local_ex = ex; 410   2 auto local_ex = ex;
HITGBC 445 - impl_ref.reset(); 411 + 2 impl_ptr.reset();
HITGBC 446   local_ex.on_work_finished(); 412   2 local_ex.on_work_finished();
HITGBC 447   } 413   2 }
448   414  
449   } // namespace boost::corosio::detail 415   } // namespace boost::corosio::detail
450   416  
451   #endif // BOOST_COROSIO_POSIX 417   #endif // BOOST_COROSIO_POSIX
452   418  
453   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP 419   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP