99.20% Lines (124/125) 100.00% Functions (17/17)
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, an object_ref keepalive on
  60 + each op for lifetime while its pool work is in flight.
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 std::enable_shared_from_this<posix_stream_file>  
87   : public stream_file::implementation 87   : public stream_file::implementation
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   532 file_op() = default; 110   550 file_op() = default;
111   111  
HITCBC 112   274 void reset() noexcept 112   284 void reset() noexcept
113   { 113   {
HITCBC 114   274 iovec_count = 0; 114   284 iovec_count = 0;
HITCBC 115   274 errn = 0; 115   284 errn = 0;
HITCBC 116   274 bytes_transferred = 0; 116   284 bytes_transferred = 0;
HITCBC 117   274 is_read = false; 117   284 is_read = false;
HITCBC 118   274 cancelled.store(false, std::memory_order_relaxed); 118   284 cancelled.store(false, std::memory_order_relaxed);
HITCBC 119   274 stop_cb.reset(); 119   284 stop_cb.reset();
HITCBC 120 - 274 impl_ptr.reset(); 120 + 284 object_ref_.reset();
HITCBC 121   274 ec_out = nullptr; 121   284 ec_out = nullptr;
HITCBC 122   274 bytes_out = nullptr; 122   284 bytes_out = nullptr;
HITCBC 123   274 } 123   284 }
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 + detail::object_ref 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 + /// Recycle into the owning service's pool. Defined out-of-line
  139 + /// after posix_stream_file_service for its complete type.
  140 + void retire() noexcept override;
  141 +
  142 + /** Reset op slots for recycling.
  143 +
  144 + `close_file()` already drove fd_ to its closed value before
  145 + the refcount reached zero; each op's own `reset()` runs again
  146 + before its next use, so only the `stop_cb` invariant is worth
  147 + asserting here.
  148 +
  149 + @pre refs_ == 0, fd closed, no op in flight.
  150 + */
HITGNC   151 + 64 void reuse() noexcept
  152 + {
HITGNC   153 + 64 BOOST_COROSIO_ASSERT(fd_ == -1);
HITGNC   154 + 64 BOOST_COROSIO_ASSERT(!read_op_.stop_cb);
HITGNC   155 + 64 BOOST_COROSIO_ASSERT(!write_op_.stop_cb);
HITGNC   156 + 64 }
  157 +
138   // -- io_stream::implementation -- 158   // -- io_stream::implementation --
139   159  
140   std::coroutine_handle<> read_some( 160   std::coroutine_handle<> read_some(
141   std::coroutine_handle<>, 161   std::coroutine_handle<>,
142   capy::executor_ref, 162   capy::executor_ref,
143   buffer_param, 163   buffer_param,
144   std::stop_token, 164   std::stop_token,
145   std::error_code*, 165   std::error_code*,
146   std::size_t*) override; 166   std::size_t*) override;
147   167  
148   std::coroutine_handle<> write_some( 168   std::coroutine_handle<> write_some(
149   std::coroutine_handle<>, 169   std::coroutine_handle<>,
150   capy::executor_ref, 170   capy::executor_ref,
151   buffer_param, 171   buffer_param,
152   std::stop_token, 172   std::stop_token,
153   std::error_code*, 173   std::error_code*,
154   std::size_t*) override; 174   std::size_t*) override;
155   175  
156   // -- stream_file::implementation -- 176   // -- stream_file::implementation --
157   177  
HITCBC 158   795 native_handle_type native_handle() const noexcept override 178   941 native_handle_type native_handle() const noexcept override
159   { 179   {
HITCBC 160   795 return fd_; 180   941 return fd_;
161   } 181   }
162   182  
HITCBC 163   767 void cancel() noexcept override 183   988 void cancel() noexcept override
164   { 184   {
HITCBC 165   767 read_op_.request_cancel(); 185   988 read_op_.request_cancel();
HITCBC 166   767 write_op_.request_cancel(); 186   988 write_op_.request_cancel();
HITCBC 167   767 } 187   988 }
168   188  
169   std::uint64_t size() const override; 189   std::uint64_t size() const override;
170   std::error_code resize(std::uint64_t new_size) noexcept override; 190   std::error_code resize(std::uint64_t new_size) noexcept override;
171   std::error_code sync_data() noexcept override; 191   std::error_code sync_data() noexcept override;
172   std::error_code sync_all() noexcept override; 192   std::error_code sync_all() noexcept override;
173   native_handle_type release() override; 193   native_handle_type release() override;
174   std::error_code assign(native_handle_type handle) noexcept override; 194   std::error_code assign(native_handle_type handle) noexcept override;
175   capy::io_result<std::uint64_t> 195   capy::io_result<std::uint64_t>
176   seek(std::int64_t offset, file_base::seek_basis origin) noexcept override; 196   seek(std::int64_t offset, file_base::seek_basis origin) noexcept override;
177   197  
178   // -- Internal -- 198   // -- Internal --
179   199  
180   /** Open the file and store the fd. */ 200   /** Open the file and store the fd. */
181   std::error_code 201   std::error_code
182   open_file(std::filesystem::path const& path, file_base::flags mode); 202   open_file(std::filesystem::path const& path, file_base::flags mode);
183   203  
184   /** Close the file descriptor. */ 204   /** Close the file descriptor. */
185   void close_file() noexcept; 205   void close_file() noexcept;
186   206  
187   private: 207   private:
188   posix_stream_file_service& svc_; 208   posix_stream_file_service& svc_;
189   int fd_ = -1; 209   int fd_ = -1;
190   std::uint64_t offset_ = 0; 210   std::uint64_t offset_ = 0;
191   211  
192   file_op read_op_; 212   file_op read_op_;
193   file_op write_op_; 213   file_op write_op_;
194   pool_op read_pool_op_; 214   pool_op read_pool_op_;
195   pool_op write_pool_op_; 215   pool_op write_pool_op_;
196   216  
197   static void do_read_work(pool_work_item*) noexcept; 217   static void do_read_work(pool_work_item*) noexcept;
198   static void do_write_work(pool_work_item*) noexcept; 218   static void do_write_work(pool_work_item*) noexcept;
199   }; 219   };
200   220  
201   // --------------------------------------------------------------------------- 221   // ---------------------------------------------------------------------------
202   // Inline implementation 222   // Inline implementation
203   // --------------------------------------------------------------------------- 223   // ---------------------------------------------------------------------------
204   224  
HITCBC 205   266 inline posix_stream_file::posix_stream_file( 225   275 inline posix_stream_file::posix_stream_file(
HITCBC 206   266 posix_stream_file_service& svc) noexcept 226   275 posix_stream_file_service& svc) noexcept
HITCBC 207   266 : svc_(svc) 227   275 : svc_(svc)
208   { 228   {
HITCBC 209   266 } 229   275 }
210   230  
211   inline std::error_code 231   inline std::error_code
HITCBC 212   247 posix_stream_file::open_file( 232   320 posix_stream_file::open_file(
213   std::filesystem::path const& path, file_base::flags mode) 233   std::filesystem::path const& path, file_base::flags mode)
214   { 234   {
HITCBC 215   247 close_file(); 235   320 close_file();
216   236  
HITCBC 217   247 int oflags = 0; 237   320 int oflags = 0;
218   238  
219   // Access mode 239   // Access mode
HITCBC 220   247 unsigned access = static_cast<unsigned>(mode) & 3u; 240   320 unsigned access = static_cast<unsigned>(mode) & 3u;
HITCBC 221   247 if (access == static_cast<unsigned>(file_base::read_write)) 241   320 if (access == static_cast<unsigned>(file_base::read_write))
HITCBC 222   21 oflags |= O_RDWR; 242   87 oflags |= O_RDWR;
HITCBC 223   226 else if (access == static_cast<unsigned>(file_base::write_only)) 243   233 else if (access == static_cast<unsigned>(file_base::write_only))
HITCBC 224   81 oflags |= O_WRONLY; 244   81 oflags |= O_WRONLY;
225   else 245   else
HITCBC 226   145 oflags |= O_RDONLY; 246   152 oflags |= O_RDONLY;
227   247  
228   // Creation flags 248   // Creation flags
HITCBC 229   247 if ((mode & file_base::create) != file_base::flags(0)) 249   320 if ((mode & file_base::create) != file_base::flags(0))
HITCBC 230   40 oflags |= O_CREAT; 250   106 oflags |= O_CREAT;
HITCBC 231   247 if ((mode & file_base::exclusive) != file_base::flags(0)) 251   320 if ((mode & file_base::exclusive) != file_base::flags(0))
HITCBC 232   2 oflags |= O_EXCL; 252   2 oflags |= O_EXCL;
HITCBC 233   247 if ((mode & file_base::truncate) != file_base::flags(0)) 253   320 if ((mode & file_base::truncate) != file_base::flags(0))
HITCBC 234   17 oflags |= O_TRUNC; 254   83 oflags |= O_TRUNC;
HITCBC 235   247 if ((mode & file_base::append) != file_base::flags(0)) 255   320 if ((mode & file_base::append) != file_base::flags(0))
HITCBC 236   8 oflags |= O_APPEND; 256   8 oflags |= O_APPEND;
HITCBC 237   247 if ((mode & file_base::sync_all_on_write) != file_base::flags(0)) 257   320 if ((mode & file_base::sync_all_on_write) != file_base::flags(0))
HITCBC 238   2 oflags |= O_SYNC; 258   2 oflags |= O_SYNC;
239   259  
HITCBC 240   247 int fd = ::open(path.c_str(), oflags, 0666); 260   320 int fd = ::open(path.c_str(), oflags, 0666);
HITCBC 241   247 if (fd < 0) 261   320 if (fd < 0)
HITCBC 242   9 return make_err(errno); 262   9 return make_err(errno);
243   263  
HITCBC 244   238 fd_ = fd; 264   311 fd_ = fd;
HITCBC 245   238 offset_ = 0; 265   311 offset_ = 0;
246   266  
247   // Append mode: position at end-of-file (preadv/pwritev use 267   // Append mode: position at end-of-file (preadv/pwritev use
248   // explicit offsets, so O_APPEND alone is not sufficient). 268   // explicit offsets, so O_APPEND alone is not sufficient).
HITCBC 249   238 if ((mode & file_base::append) != file_base::flags(0)) 269   311 if ((mode & file_base::append) != file_base::flags(0))
250   { 270   {
251   struct stat st; 271   struct stat st;
HITCBC 252   8 if (::fstat(fd, &st) < 0) 272   8 if (::fstat(fd, &st) < 0)
253   { 273   {
HITCBC 254   5 int err = errno; 274   5 int err = errno;
HITCBC 255   5 ::close(fd); 275   5 ::close(fd);
HITCBC 256   5 fd_ = -1; 276   5 fd_ = -1;
HITCBC 257   5 return make_err(err); 277   5 return make_err(err);
258   } 278   }
HITCBC 259   3 offset_ = static_cast<std::uint64_t>(st.st_size); 279   3 offset_ = static_cast<std::uint64_t>(st.st_size);
260   } 280   }
261   281  
262   #ifdef POSIX_FADV_SEQUENTIAL 282   #ifdef POSIX_FADV_SEQUENTIAL
HITCBC 263   233 ::posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL); 283   306 ::posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL);
264   #endif 284   #endif
265   285  
HITCBC 266   233 return {}; 286   306 return {};
267   } 287   }
268   288  
269   inline void 289   inline void
HITCBC 270   1018 posix_stream_file::close_file() noexcept 290   1312 posix_stream_file::close_file() noexcept
271   { 291   {
HITCBC 272   1018 if (fd_ >= 0) 292   1312 if (fd_ >= 0)
273   { 293   {
HITCBC 274   237 ::close(fd_); 294   310 ::close(fd_);
HITCBC 275   237 fd_ = -1; 295   310 fd_ = -1;
276   } 296   }
HITCBC 277   1018 } 297   1312 }
278   298  
279   inline std::uint64_t 299   inline std::uint64_t
HITCBC 280   17 posix_stream_file::size() const 300   17 posix_stream_file::size() const
281   { 301   {
282   struct stat st; 302   struct stat st;
HITCBC 283   17 if (::fstat(fd_, &st) < 0) 303   17 if (::fstat(fd_, &st) < 0)
HITCBC 284   5 throw_system_error(make_err(errno), "stream_file::size"); 304   5 throw_system_error(make_err(errno), "stream_file::size");
HITCBC 285   12 return static_cast<std::uint64_t>(st.st_size); 305   12 return static_cast<std::uint64_t>(st.st_size);
286   } 306   }
287   307  
288   inline std::error_code 308   inline std::error_code
HITCBC 289   12 posix_stream_file::resize(std::uint64_t new_size) noexcept 309   12 posix_stream_file::resize(std::uint64_t new_size) noexcept
290   { 310   {
HITCBC 291   12 if (new_size > 311   12 if (new_size >
HITCBC 292   12 static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)())) 312   12 static_cast<std::uint64_t>((std::numeric_limits<off_t>::max)()))
HITCBC 293   2 return make_err(EOVERFLOW); 313   2 return make_err(EOVERFLOW);
HITCBC 294   10 if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0) 314   10 if (::ftruncate(fd_, static_cast<off_t>(new_size)) < 0)
HITCBC 295   7 return make_err(errno); 315   7 return make_err(errno);
HITCBC 296   3 return {}; 316   3 return {};
297   } 317   }
298   318  
299   inline std::error_code 319   inline std::error_code
HITCBC 300   10 posix_stream_file::sync_data() noexcept 320   10 posix_stream_file::sync_data() noexcept
301   { 321   {
302   #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 322   #if BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
HITCBC 303   10 if (::fdatasync(fd_) < 0) 323   10 if (::fdatasync(fd_) < 0)
304   #else // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 324   #else // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
305   if (::fsync(fd_) < 0) 325   if (::fsync(fd_) < 0)
306   #endif // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO 326   #endif // BOOST_COROSIO_HAS_POSIX_SYNCHRONIZED_IO
HITCBC 307   7 return make_err(errno); 327   7 return make_err(errno);
HITCBC 308   3 return {}; 328   3 return {};
309   } 329   }
310   330  
311   inline std::error_code 331   inline std::error_code
HITCBC 312   10 posix_stream_file::sync_all() noexcept 332   10 posix_stream_file::sync_all() noexcept
313   { 333   {
HITCBC 314   10 if (::fsync(fd_) < 0) 334   10 if (::fsync(fd_) < 0)
HITCBC 315   7 return make_err(errno); 335   7 return make_err(errno);
HITCBC 316   3 return {}; 336   3 return {};
317   } 337   }
318   338  
319   inline native_handle_type 339   inline native_handle_type
HITCBC 320   2 posix_stream_file::release() 340   2 posix_stream_file::release()
321   { 341   {
HITCBC 322   2 int fd = fd_; 342   2 int fd = fd_;
HITCBC 323   2 fd_ = -1; 343   2 fd_ = -1;
HITCBC 324   2 offset_ = 0; 344   2 offset_ = 0;
HITCBC 325   2 return fd; 345   2 return fd;
326   } 346   }
327   347  
328   inline std::error_code 348   inline std::error_code
HITCBC 329   6 posix_stream_file::assign(native_handle_type handle) noexcept 349   6 posix_stream_file::assign(native_handle_type handle) noexcept
330   { 350   {
HITCBC 331   6 close_file(); 351   6 close_file();
HITCBC 332   6 fd_ = handle; 352   6 fd_ = handle;
HITCBC 333   6 offset_ = 0; 353   6 offset_ = 0;
HITCBC 334   6 return {}; 354   6 return {};
335   } 355   }
336   356  
337   inline capy::io_result<std::uint64_t> 357   inline capy::io_result<std::uint64_t>
HITCBC 338   30 posix_stream_file::seek( 358   30 posix_stream_file::seek(
339   std::int64_t offset, file_base::seek_basis origin) noexcept 359   std::int64_t offset, file_base::seek_basis origin) noexcept
340   { 360   {
341   // We track offset_ ourselves (not the kernel fd offset) 361   // We track offset_ ourselves (not the kernel fd offset)
342   // because preadv/pwritev use explicit offsets. 362   // because preadv/pwritev use explicit offsets.
343   std::int64_t new_pos; 363   std::int64_t new_pos;
344   364  
HITCBC 345   30 if (origin == file_base::seek_set) 365   30 if (origin == file_base::seek_set)
346   { 366   {
HITCBC 347   14 new_pos = offset; 367   14 new_pos = offset;
348   } 368   }
HITCBC 349   16 else if (origin == file_base::seek_cur) 369   16 else if (origin == file_base::seek_cur)
350   { 370   {
HITCBC 351   5 new_pos = static_cast<std::int64_t>(offset_) + offset; 371   5 new_pos = static_cast<std::int64_t>(offset_) + offset;
352   } 372   }
353   else 373   else
354   { 374   {
355   struct stat st; 375   struct stat st;
HITCBC 356   11 if (::fstat(fd_, &st) < 0) 376   11 if (::fstat(fd_, &st) < 0)
HITCBC 357   5 return {make_err(errno), 0}; 377   5 return {make_err(errno), 0};
HITCBC 358   6 new_pos = st.st_size + offset; 378   6 new_pos = st.st_size + offset;
359   } 379   }
360   380  
HITCBC 361   25 if (new_pos < 0) 381   25 if (new_pos < 0)
HITCBC 362   6 return {make_err(EINVAL), 0}; 382   6 return {make_err(EINVAL), 0};
HITCBC 363   19 if (new_pos > 383   19 if (new_pos >
HITCBC 364   19 static_cast<std::int64_t>((std::numeric_limits<off_t>::max)())) 384   19 static_cast<std::int64_t>((std::numeric_limits<off_t>::max)()))
MISUBC 365   ✗ return {make_err(EOVERFLOW), 0}; 385   ✗ return {make_err(EOVERFLOW), 0};
366   386  
HITCBC 367   19 offset_ = static_cast<std::uint64_t>(new_pos); 387   19 offset_ = static_cast<std::uint64_t>(new_pos);
368   388  
HITCBC 369   19 return {std::error_code{}, offset_}; 389   19 return {std::error_code{}, offset_};
370   } 390   }
371   391  
372   // -- file_op completion handler -- 392   // -- file_op completion handler --
373   // (read_some, write_some, do_read_work, do_write_work are 393   // (read_some, write_some, do_read_work, do_write_work are
374   // defined in posix_stream_file_service.hpp after the service) 394   // defined in posix_stream_file_service.hpp after the service)
375   395  
376   inline void 396   inline void
HITCBC 377   248 posix_stream_file::file_op::operator()() 397   258 posix_stream_file::file_op::operator()()
378   { 398   {
HITCBC 379   248 stop_cb.reset(); 399   258 stop_cb.reset();
380   400  
381   // Empty buffers never reach the pool (diverted at initiation), so 401   // Empty buffers never reach the pool (diverted at initiation), so
382   // empty_buffer stays false and a 0-byte read is a genuine EOF. 402   // empty_buffer stays false and a 0-byte read is a genuine EOF.
HITCBC 383   482 decode_io_result( 403   502 decode_io_result(
HITCBC 384   248 ec_out, bytes_out, cancelled.load(std::memory_order_acquire), 404   258 ec_out, bytes_out, cancelled.load(std::memory_order_acquire),
HITCBC 385   248 errn != 0 ? make_err(errn) : std::error_code{}, is_read, 405   258 errn != 0 ? make_err(errn) : std::error_code{}, is_read,
386   bytes_transferred, /*empty_buffer=*/false); 406   bytes_transferred, /*empty_buffer=*/false);
387   407  
388 - // Move impl_ptr to a local so members remain valid through 408 + // Move object_ref_ to a local so members remain valid through
389 - // dispatch — impl_ptr may be the last shared_ptr keeping 409 + // dispatch — this may be the last reference keeping the parent
390 - // the parent posix_stream_file (which embeds this file_op) alive. 410 + // posix_stream_file (which embeds this file_op) alive.
HITCBC 391 - 248 auto prevent_destroy = std::move(impl_ptr); 411 + 258 auto prevent_destroy = std::move(object_ref_);
HITCBC 392   248 ex.on_work_finished(); 412   258 ex.on_work_finished();
HITCBC 393   248 cont.h = h; 413   258 cont.h = h;
HITCBC 394   248 dispatch_coro(ex, cont).resume(); 414   258 dispatch_coro(ex, cont).resume();
HITCBC 395   248 } 415   258 }
396   416  
397   inline void 417   inline void
HITCBC 398   2 posix_stream_file::file_op::destroy() 418   2 posix_stream_file::file_op::destroy()
399   { 419   {
HITCBC 400   2 stop_cb.reset(); 420   2 stop_cb.reset();
HITCBC 401   2 auto local_ex = ex; 421   2 auto local_ex = ex;
HITCBC 402 - 2 impl_ptr.reset(); 422 + 2 object_ref_.reset();
HITCBC 403   2 local_ex.on_work_finished(); 423   2 local_ex.on_work_finished();
HITCBC 404   2 } 424   2 }
405   425  
406   } // namespace boost::corosio::detail 426   } // namespace boost::corosio::detail
407   427  
408   #endif // BOOST_COROSIO_POSIX 428   #endif // BOOST_COROSIO_POSIX
409   429  
410   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP 430   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_STREAM_FILE_HPP