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