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