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