94.06% Lines (95/101) 100.00% Functions (21/21)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_IO_ANY_WRITE_STREAM_HPP 10   #ifndef BOOST_CAPY_IO_ANY_WRITE_STREAM_HPP
11   #define BOOST_CAPY_IO_ANY_WRITE_STREAM_HPP 11   #define BOOST_CAPY_IO_ANY_WRITE_STREAM_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/detail/await_suspend_helper.hpp> 14   #include <boost/capy/detail/await_suspend_helper.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/buffers/buffer_array.hpp> 16   #include <boost/capy/buffers/buffer_array.hpp>
17   #include <boost/capy/concept/io_awaitable.hpp> 17   #include <boost/capy/concept/io_awaitable.hpp>
18   #include <boost/capy/concept/write_stream.hpp> 18   #include <boost/capy/concept/write_stream.hpp>
19   #include <coroutine> 19   #include <coroutine>
20   #include <boost/capy/ex/io_env.hpp> 20   #include <boost/capy/ex/io_env.hpp>
21   #include <boost/capy/io_result.hpp> 21   #include <boost/capy/io_result.hpp>
22   22  
23   #include <concepts> 23   #include <concepts>
24   #include <coroutine> 24   #include <coroutine>
25   #include <cstddef> 25   #include <cstddef>
26   #include <exception> 26   #include <exception>
27   #include <new> 27   #include <new>
28   #include <span> 28   #include <span>
29   #include <stop_token> 29   #include <stop_token>
30   #include <system_error> 30   #include <system_error>
31   #include <utility> 31   #include <utility>
32   32  
33   namespace boost { 33   namespace boost {
34   namespace capy { 34   namespace capy {
35   35  
36   /** Type-erased wrapper for any WriteStream. 36   /** Type-erased wrapper for any WriteStream.
37   37  
38   This class provides type erasure for any type satisfying the 38   This class provides type erasure for any type satisfying the
39   @ref WriteStream concept, enabling runtime polymorphism for 39   @ref WriteStream concept, enabling runtime polymorphism for
40   write operations. It uses cached awaitable storage to achieve 40   write operations. It uses cached awaitable storage to achieve
41   zero steady-state allocation after construction. 41   zero steady-state allocation after construction.
42   42  
43   The wrapper supports two construction modes: 43   The wrapper supports two construction modes:
44   - **Owning**: Pass by value to transfer ownership. The wrapper 44   - **Owning**: Pass by value to transfer ownership. The wrapper
45   allocates storage and owns the stream. 45   allocates storage and owns the stream.
46   - **Reference**: Pass a pointer to wrap without ownership. The 46   - **Reference**: Pass a pointer to wrap without ownership. The
47   pointed-to stream must outlive this wrapper. 47   pointed-to stream must outlive this wrapper.
48   48  
49   @par Awaitable Preallocation 49   @par Awaitable Preallocation
50   The constructor preallocates storage for the type-erased awaitable. 50   The constructor preallocates storage for the type-erased awaitable.
51   This reserves all virtual address space at server startup 51   This reserves all virtual address space at server startup
52   so memory usage can be measured up front, rather than 52   so memory usage can be measured up front, rather than
53   allocating piecemeal as traffic arrives. 53   allocating piecemeal as traffic arrives.
54   54  
55   @par Immediate Completion 55   @par Immediate Completion
56   Operations complete immediately without suspending when the 56   Operations complete immediately without suspending when the
57   buffer sequence is empty, or when the underlying stream's 57   buffer sequence is empty, or when the underlying stream's
58   awaitable reports readiness via `await_ready`. 58   awaitable reports readiness via `await_ready`.
59   59  
60   @par Thread Safety 60   @par Thread Safety
61   Not thread-safe. Concurrent operations on the same wrapper 61   Not thread-safe. Concurrent operations on the same wrapper
62   are undefined behavior. 62   are undefined behavior.
63   63  
64   @par Example 64   @par Example
65   @code 65   @code
66   // Owning - takes ownership of the stream 66   // Owning - takes ownership of the stream
67   any_write_stream stream(socket{ioc}); 67   any_write_stream stream(socket{ioc});
68   68  
69   // Reference - wraps without ownership 69   // Reference - wraps without ownership
70   socket sock(ioc); 70   socket sock(ioc);
71   any_write_stream stream(&sock); 71   any_write_stream stream(&sock);
72   72  
73   const_buffer buf(data, size); 73   const_buffer buf(data, size);
74   auto [ec, n] = co_await stream.write_some(std::span(&buf, 1)); 74   auto [ec, n] = co_await stream.write_some(std::span(&buf, 1));
75   @endcode 75   @endcode
76   76  
77   @see any_read_stream, any_stream, WriteStream 77   @see any_read_stream, any_stream, WriteStream
78   */ 78   */
79   class any_write_stream 79   class any_write_stream
80   { 80   {
81   struct vtable; 81   struct vtable;
82   82  
83   template<WriteStream S> 83   template<WriteStream S>
84   struct vtable_for_impl; 84   struct vtable_for_impl;
85   85  
86   // ordered for cache line coherence 86   // ordered for cache line coherence
87   void* stream_ = nullptr; 87   void* stream_ = nullptr;
88   vtable const* vt_ = nullptr; 88   vtable const* vt_ = nullptr;
89   void* cached_awaitable_ = nullptr; 89   void* cached_awaitable_ = nullptr;
90   void* storage_ = nullptr; 90   void* storage_ = nullptr;
91   bool awaitable_active_ = false; 91   bool awaitable_active_ = false;
92   92  
93   public: 93   public:
94   /** Destructor. 94   /** Destructor.
95   95  
96   Destroys the owned stream (if any) and releases the cached 96   Destroys the owned stream (if any) and releases the cached
97   awaitable storage. 97   awaitable storage.
98   */ 98   */
99   ~any_write_stream(); 99   ~any_write_stream();
100   100  
101   /** Construct a default instance. 101   /** Construct a default instance.
102   102  
103   Constructs an empty wrapper. Operations on a default-constructed 103   Constructs an empty wrapper. Operations on a default-constructed
104   wrapper result in undefined behavior. 104   wrapper result in undefined behavior.
105   */ 105   */
HITCBC 106   1 any_write_stream() = default; 106   1 any_write_stream() = default;
107   107  
108   /** Non-copyable. 108   /** Non-copyable.
109   109  
110   The awaitable cache is per-instance and cannot be shared. 110   The awaitable cache is per-instance and cannot be shared.
111   */ 111   */
112   any_write_stream(any_write_stream const&) = delete; 112   any_write_stream(any_write_stream const&) = delete;
113   any_write_stream& operator=(any_write_stream const&) = delete; 113   any_write_stream& operator=(any_write_stream const&) = delete;
114   114  
115   /** Construct by moving. 115   /** Construct by moving.
116   116  
117   Transfers ownership of the wrapped stream (if owned) and 117   Transfers ownership of the wrapped stream (if owned) and
118   cached awaitable storage from `other`. After the move, `other` is 118   cached awaitable storage from `other`. After the move, `other` is
119   in a default-constructed state. 119   in a default-constructed state.
120   120  
121   @param other The wrapper to move from. 121   @param other The wrapper to move from.
122   */ 122   */
HITCBC 123   2 any_write_stream(any_write_stream&& other) noexcept 123   2 any_write_stream(any_write_stream&& other) noexcept
HITCBC 124   2 : stream_(std::exchange(other.stream_, nullptr)) 124   2 : stream_(std::exchange(other.stream_, nullptr))
HITCBC 125   2 , vt_(std::exchange(other.vt_, nullptr)) 125   2 , vt_(std::exchange(other.vt_, nullptr))
HITCBC 126   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr)) 126   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr))
HITCBC 127   2 , storage_(std::exchange(other.storage_, nullptr)) 127   2 , storage_(std::exchange(other.storage_, nullptr))
HITCBC 128   2 , awaitable_active_(std::exchange(other.awaitable_active_, false)) 128   2 , awaitable_active_(std::exchange(other.awaitable_active_, false))
129   { 129   {
HITCBC 130   2 } 130   2 }
131   131  
132   /** Assign by moving. 132   /** Assign by moving.
133   133  
134   Destroys any owned stream and releases existing resources, 134   Destroys any owned stream and releases existing resources,
135   then transfers ownership from `other`. 135   then transfers ownership from `other`.
136   136  
137   @param other The wrapper to move from. 137   @param other The wrapper to move from.
138   @return Reference to this wrapper. 138   @return Reference to this wrapper.
139   */ 139   */
140   any_write_stream& 140   any_write_stream&
141   operator=(any_write_stream&& other) noexcept; 141   operator=(any_write_stream&& other) noexcept;
142   142  
143   /** Construct by taking ownership of a WriteStream. 143   /** Construct by taking ownership of a WriteStream.
144   144  
145   Allocates storage and moves the stream into this wrapper. 145   Allocates storage and moves the stream into this wrapper.
146   The wrapper owns the stream and will destroy it. 146   The wrapper owns the stream and will destroy it.
147   147  
148   @param s The stream to take ownership of. 148   @param s The stream to take ownership of.
149   */ 149   */
150   template<WriteStream S> 150   template<WriteStream S>
151   requires (!std::same_as<std::decay_t<S>, any_write_stream>) 151   requires (!std::same_as<std::decay_t<S>, any_write_stream>)
152   any_write_stream(S s); 152   any_write_stream(S s);
153   153  
154   /** Construct by wrapping a WriteStream without ownership. 154   /** Construct by wrapping a WriteStream without ownership.
155   155  
156   Wraps the given stream by pointer. The stream must remain 156   Wraps the given stream by pointer. The stream must remain
157   valid for the lifetime of this wrapper. 157   valid for the lifetime of this wrapper.
158   158  
159   @param s Pointer to the stream to wrap. 159   @param s Pointer to the stream to wrap.
160   */ 160   */
161   template<WriteStream S> 161   template<WriteStream S>
162   any_write_stream(S* s); 162   any_write_stream(S* s);
163   163  
164   /** Check if the wrapper contains a valid stream. 164   /** Check if the wrapper contains a valid stream.
165   165  
166   @return `true` if wrapping a stream, `false` if default-constructed 166   @return `true` if wrapping a stream, `false` if default-constructed
167   or moved-from. 167   or moved-from.
168   */ 168   */
169   bool 169   bool
HITCBC 170   21 has_value() const noexcept 170   21 has_value() const noexcept
171   { 171   {
HITCBC 172   21 return stream_ != nullptr; 172   21 return stream_ != nullptr;
173   } 173   }
174   174  
175   /** Check if the wrapper contains a valid stream. 175   /** Check if the wrapper contains a valid stream.
176   176  
177   @return `true` if wrapping a stream, `false` if default-constructed 177   @return `true` if wrapping a stream, `false` if default-constructed
178   or moved-from. 178   or moved-from.
179   */ 179   */
180   explicit 180   explicit
HITCBC 181   3 operator bool() const noexcept 181   3 operator bool() const noexcept
182   { 182   {
HITCBC 183   3 return has_value(); 183   3 return has_value();
184   } 184   }
185   185  
186   /** Initiate an asynchronous write operation. 186   /** Initiate an asynchronous write operation.
187   187  
188   Writes data from the provided buffer sequence. The operation 188   Writes data from the provided buffer sequence. The operation
189   completes when at least one byte has been written, or an error 189   completes when at least one byte has been written, or an error
190   occurs. 190   occurs.
191   191  
192   @param buffers The buffer sequence containing data to write. 192   @param buffers The buffer sequence containing data to write.
193   Passed by value to ensure the sequence lives in the 193   Passed by value to ensure the sequence lives in the
194   coroutine frame across suspension points. 194   coroutine frame across suspension points.
195   195  
196   @return An awaitable that await-returns `(error_code,std::size_t)`. 196   @return An awaitable that await-returns `(error_code,std::size_t)`.
197   197  
198   @par Immediate Completion 198   @par Immediate Completion
199   The operation completes immediately without suspending 199   The operation completes immediately without suspending
200   the calling coroutine when: 200   the calling coroutine when:
201   @li The buffer sequence is empty, returning `{error_code{}, 0}`. 201   @li The buffer sequence is empty, returning `{error_code{}, 0}`.
202   @li The underlying stream's awaitable reports immediate 202   @li The underlying stream's awaitable reports immediate
203   readiness via `await_ready`. 203   readiness via `await_ready`.
204   204  
205   @note This is a partial operation and may not process the 205   @note This is a partial operation and may not process the
206   entire buffer sequence. Use the composed @ref write algorithm 206   entire buffer sequence. Use the composed @ref write algorithm
207   for guaranteed complete transfer. 207   for guaranteed complete transfer.
208   208  
209   @par Preconditions 209   @par Preconditions
210   The wrapper must contain a valid stream (`has_value() == true`). 210   The wrapper must contain a valid stream (`has_value() == true`).
211   */ 211   */
212   template<ConstBufferSequence CB> 212   template<ConstBufferSequence CB>
213   auto 213   auto
214   write_some(CB buffers); 214   write_some(CB buffers);
215   215  
216   protected: 216   protected:
217   /** Rebind to a new stream after move. 217   /** Rebind to a new stream after move.
218   218  
219   Updates the internal pointer to reference a new stream object. 219   Updates the internal pointer to reference a new stream object.
220   Used by owning wrappers after move assignment when the owned 220   Used by owning wrappers after move assignment when the owned
221   object has moved to a new location. 221   object has moved to a new location.
222   222  
223   @param new_stream The new stream to bind to. Must be the same 223   @param new_stream The new stream to bind to. Must be the same
224   type as the original stream. 224   type as the original stream.
225   225  
226   @note Terminates if called with a stream of different type 226   @note Terminates if called with a stream of different type
227   than the original. 227   than the original.
228   */ 228   */
229   template<WriteStream S> 229   template<WriteStream S>
230   void 230   void
231   rebind(S& new_stream) noexcept 231   rebind(S& new_stream) noexcept
232   { 232   {
233   if(vt_ != &vtable_for_impl<S>::value) 233   if(vt_ != &vtable_for_impl<S>::value)
234   std::terminate(); 234   std::terminate();
235   stream_ = &new_stream; 235   stream_ = &new_stream;
236   } 236   }
237   }; 237   };
238   238  
239   struct any_write_stream::vtable 239   struct any_write_stream::vtable
240   { 240   {
241   // ordered by call frequency for cache line coherence 241   // ordered by call frequency for cache line coherence
242   void (*construct_awaitable)( 242   void (*construct_awaitable)(
243   void* stream, 243   void* stream,
244   void* storage, 244   void* storage,
245   std::span<const_buffer const> buffers); 245   std::span<const_buffer const> buffers);
246   bool (*await_ready)(void*); 246   bool (*await_ready)(void*);
247   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 247   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
248   io_result<std::size_t> (*await_resume)(void*); 248   io_result<std::size_t> (*await_resume)(void*);
249   void (*destroy_awaitable)(void*) noexcept; 249   void (*destroy_awaitable)(void*) noexcept;
250   std::size_t awaitable_size; 250   std::size_t awaitable_size;
251   std::size_t awaitable_align; 251   std::size_t awaitable_align;
252   void (*destroy)(void*) noexcept; 252   void (*destroy)(void*) noexcept;
253   }; 253   };
254   254  
255   template<WriteStream S> 255   template<WriteStream S>
256   struct any_write_stream::vtable_for_impl 256   struct any_write_stream::vtable_for_impl
257   { 257   {
258   using Awaitable = decltype(std::declval<S&>().write_some( 258   using Awaitable = decltype(std::declval<S&>().write_some(
259   std::span<const_buffer const>{})); 259   std::span<const_buffer const>{}));
260   260  
261   static void 261   static void
HITCBC 262   1 do_destroy_impl(void* stream) noexcept 262   1 do_destroy_impl(void* stream) noexcept
263   { 263   {
HITCBC 264   1 static_cast<S*>(stream)->~S(); 264   1 static_cast<S*>(stream)->~S();
HITCBC 265   1 } 265   1 }
266   266  
267   static void 267   static void
HITCBC 268   75 construct_awaitable_impl( 268   75 construct_awaitable_impl(
269   void* stream, 269   void* stream,
270   void* storage, 270   void* storage,
271   std::span<const_buffer const> buffers) 271   std::span<const_buffer const> buffers)
272   { 272   {
HITCBC 273   75 auto& s = *static_cast<S*>(stream); 273   75 auto& s = *static_cast<S*>(stream);
HITCBC 274   75 ::new(storage) Awaitable(s.write_some(buffers)); 274   75 ::new(storage) Awaitable(s.write_some(buffers));
HITCBC 275   75 } 275   75 }
276   276  
277   static constexpr vtable value = { 277   static constexpr vtable value = {
278   &construct_awaitable_impl, 278   &construct_awaitable_impl,
HITCBC 279   75 +[](void* p) { 279   75 +[](void* p) {
HITCBC 280   75 return static_cast<Awaitable*>(p)->await_ready(); 280   75 return static_cast<Awaitable*>(p)->await_ready();
281   }, 281   },
HITCBC 282   2 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 282   2 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 283   2 return detail::call_await_suspend( 283   2 return detail::call_await_suspend(
HITCBC 284   2 static_cast<Awaitable*>(p), h, env); 284   2 static_cast<Awaitable*>(p), h, env);
285   }, 285   },
HITCBC 286   73 +[](void* p) { 286   73 +[](void* p) {
HITCBC 287   73 return static_cast<Awaitable*>(p)->await_resume(); 287   73 return static_cast<Awaitable*>(p)->await_resume();
288   }, 288   },
HITCBC 289   77 +[](void* p) noexcept { 289   77 +[](void* p) noexcept {
HITCBC 290   12 static_cast<Awaitable*>(p)->~Awaitable(); 290   12 static_cast<Awaitable*>(p)->~Awaitable();
291   }, 291   },
292   sizeof(Awaitable), 292   sizeof(Awaitable),
293   alignof(Awaitable), 293   alignof(Awaitable),
294   &do_destroy_impl 294   &do_destroy_impl
295   }; 295   };
296   }; 296   };
297   297  
298   inline 298   inline
HITCBC 299   95 any_write_stream::~any_write_stream() 299   95 any_write_stream::~any_write_stream()
300   { 300   {
HITCBC 301   95 if(storage_) 301   95 if(storage_)
302   { 302   {
HITCBC 303   1 vt_->destroy(stream_); 303   1 vt_->destroy(stream_);
HITCBC 304   1 ::operator delete(storage_); 304   1 ::operator delete(storage_);
305   } 305   }
HITCBC 306   95 if(cached_awaitable_) 306   95 if(cached_awaitable_)
307   { 307   {
HITCBC 308   85 if(awaitable_active_) 308   85 if(awaitable_active_)
HITCBC 309   1 vt_->destroy_awaitable(cached_awaitable_); 309   1 vt_->destroy_awaitable(cached_awaitable_);
HITCBC 310   85 ::operator delete(cached_awaitable_); 310   85 ::operator delete(cached_awaitable_);
311   } 311   }
HITCBC 312   95 } 312   95 }
313   313  
314   inline any_write_stream& 314   inline any_write_stream&
HITCBC 315   5 any_write_stream::operator=(any_write_stream&& other) noexcept 315   5 any_write_stream::operator=(any_write_stream&& other) noexcept
316   { 316   {
HITCBC 317   5 if(this != &other) 317   5 if(this != &other)
318   { 318   {
HITCBC 319   5 if(storage_) 319   5 if(storage_)
320   { 320   {
MISUBC 321   vt_->destroy(stream_); 321   vt_->destroy(stream_);
MISUBC 322   ::operator delete(storage_); 322   ::operator delete(storage_);
323   } 323   }
HITCBC 324   5 if(cached_awaitable_) 324   5 if(cached_awaitable_)
325   { 325   {
HITCBC 326   2 if(awaitable_active_) 326   2 if(awaitable_active_)
HITCBC 327   1 vt_->destroy_awaitable(cached_awaitable_); 327   1 vt_->destroy_awaitable(cached_awaitable_);
HITCBC 328   2 ::operator delete(cached_awaitable_); 328   2 ::operator delete(cached_awaitable_);
329   } 329   }
HITCBC 330   5 stream_ = std::exchange(other.stream_, nullptr); 330   5 stream_ = std::exchange(other.stream_, nullptr);
HITCBC 331   5 vt_ = std::exchange(other.vt_, nullptr); 331   5 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 332   5 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 332   5 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 333   5 storage_ = std::exchange(other.storage_, nullptr); 333   5 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 334   5 awaitable_active_ = std::exchange(other.awaitable_active_, false); 334   5 awaitable_active_ = std::exchange(other.awaitable_active_, false);
335   } 335   }
HITCBC 336   5 return *this; 336   5 return *this;
337   } 337   }
338   338  
339   template<WriteStream S> 339   template<WriteStream S>
340   requires (!std::same_as<std::decay_t<S>, any_write_stream>) 340   requires (!std::same_as<std::decay_t<S>, any_write_stream>)
HITCBC 341   1 any_write_stream::any_write_stream(S s) 341   1 any_write_stream::any_write_stream(S s)
HITCBC 342   1 : vt_(&vtable_for_impl<S>::value) 342   1 : vt_(&vtable_for_impl<S>::value)
343   { 343   {
344   struct guard { 344   struct guard {
345   any_write_stream* self; 345   any_write_stream* self;
346   bool committed = false; 346   bool committed = false;
HITCBC 347   1 ~guard() { 347   1 ~guard() {
HITCBC 348   1 if(!committed && self->storage_) { 348   1 if(!committed && self->storage_) {
MISUBC 349   self->vt_->destroy(self->stream_); 349   self->vt_->destroy(self->stream_);
MISUBC 350   ::operator delete(self->storage_); 350   ::operator delete(self->storage_);
MISUBC 351   self->storage_ = nullptr; 351   self->storage_ = nullptr;
MISUBC 352   self->stream_ = nullptr; 352   self->stream_ = nullptr;
353   } 353   }
HITCBC 354   1 } 354   1 }
HITCBC 355   1 } g{this}; 355   1 } g{this};
356   356  
HITCBC 357   1 storage_ = ::operator new(sizeof(S)); 357   1 storage_ = ::operator new(sizeof(S));
HITCBC 358   1 stream_ = ::new(storage_) S(std::move(s)); 358   1 stream_ = ::new(storage_) S(std::move(s));
359   359  
360   // Preallocate the awaitable storage 360   // Preallocate the awaitable storage
HITCBC 361   1 cached_awaitable_ = ::operator new(vt_->awaitable_size); 361   1 cached_awaitable_ = ::operator new(vt_->awaitable_size);
362   362  
HITCBC 363   1 g.committed = true; 363   1 g.committed = true;
HITCBC 364   1 } 364   1 }
365   365  
366   template<WriteStream S> 366   template<WriteStream S>
HITCBC 367   86 any_write_stream::any_write_stream(S* s) 367   86 any_write_stream::any_write_stream(S* s)
HITCBC 368   86 : stream_(s) 368   86 : stream_(s)
HITCBC 369   86 , vt_(&vtable_for_impl<S>::value) 369   86 , vt_(&vtable_for_impl<S>::value)
370   { 370   {
371   // Preallocate the awaitable storage 371   // Preallocate the awaitable storage
HITCBC 372   86 cached_awaitable_ = ::operator new(vt_->awaitable_size); 372   86 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 373   86 } 373   86 }
374   374  
375   template<ConstBufferSequence CB> 375   template<ConstBufferSequence CB>
376   auto 376   auto
HITCBC 377   79 any_write_stream::write_some(CB buffers) 377   79 any_write_stream::write_some(CB buffers)
378   { 378   {
379   struct awaitable 379   struct awaitable
380   { 380   {
381   any_write_stream* self_; 381   any_write_stream* self_;
382   const_buffer_array<detail::max_iovec_> ba_; 382   const_buffer_array<detail::max_iovec_> ba_;
383   383  
HITCBC 384   79 awaitable( 384   79 awaitable(
385   any_write_stream* self, 385   any_write_stream* self,
386   CB const& buffers) noexcept 386   CB const& buffers) noexcept
HITCBC 387   79 : self_(self) 387   79 : self_(self)
HITCBC 388   79 , ba_(buffers) 388   79 , ba_(buffers)
389   { 389   {
HITCBC 390   79 } 390   79 }
391   391  
392   bool 392   bool
HITCBC 393   79 await_ready() const noexcept 393   79 await_ready() const noexcept
394   { 394   {
HITCBC 395   79 return ba_.to_span().empty(); 395   79 return ba_.to_span().empty();
396   } 396   }
397   397  
398   std::coroutine_handle<> 398   std::coroutine_handle<>
HITCBC 399   75 await_suspend(std::coroutine_handle<> h, io_env const* env) 399   75 await_suspend(std::coroutine_handle<> h, io_env const* env)
400   { 400   {
HITCBC 401   75 self_->vt_->construct_awaitable( 401   75 self_->vt_->construct_awaitable(
HITCBC 402   75 self_->stream_, 402   75 self_->stream_,
HITCBC 403   75 self_->cached_awaitable_, 403   75 self_->cached_awaitable_,
HITCBC 404   75 ba_.to_span()); 404   75 ba_.to_span());
HITCBC 405   75 self_->awaitable_active_ = true; 405   75 self_->awaitable_active_ = true;
406   406  
HITCBC 407   75 if(self_->vt_->await_ready(self_->cached_awaitable_)) 407   75 if(self_->vt_->await_ready(self_->cached_awaitable_))
HITCBC 408   73 return h; 408   73 return h;
409   409  
HITCBC 410   2 return self_->vt_->await_suspend( 410   2 return self_->vt_->await_suspend(
HITCBC 411   2 self_->cached_awaitable_, h, env); 411   2 self_->cached_awaitable_, h, env);
412   } 412   }
413   413  
414   io_result<std::size_t> 414   io_result<std::size_t>
HITCBC 415   77 await_resume() 415   77 await_resume()
416   { 416   {
HITCBC 417   77 if(!self_->awaitable_active_) 417   77 if(!self_->awaitable_active_)
HITCBC 418   4 return {{}, 0}; 418   4 return {{}, 0};
419   struct guard { 419   struct guard {
420   any_write_stream* self; 420   any_write_stream* self;
HITCBC 421   73 ~guard() { 421   73 ~guard() {
HITCBC 422   73 self->vt_->destroy_awaitable(self->cached_awaitable_); 422   73 self->vt_->destroy_awaitable(self->cached_awaitable_);
HITCBC 423   73 self->awaitable_active_ = false; 423   73 self->awaitable_active_ = false;
HITCBC 424   73 } 424   73 }
HITCBC 425   73 } g{self_}; 425   73 } g{self_};
HITCBC 426   73 return self_->vt_->await_resume( 426   73 return self_->vt_->await_resume(
HITCBC 427   73 self_->cached_awaitable_); 427   73 self_->cached_awaitable_);
HITCBC 428   73 } 428   73 }
429   }; 429   };
HITCBC 430   79 return awaitable{this, buffers}; 430   79 return awaitable{this, buffers};
431   } 431   }
432   432  
433   } // namespace capy 433   } // namespace capy
434   } // namespace boost 434   } // namespace boost
435   435  
436   #endif 436   #endif