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