mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
Fix stable_async_base example
This commit is contained in:
@ -3,6 +3,7 @@ Version 229:
|
|||||||
* Rename to buffer_bytes
|
* Rename to buffer_bytes
|
||||||
* Tidy up examples
|
* Tidy up examples
|
||||||
* detect_ssl returns a bool
|
* detect_ssl returns a bool
|
||||||
|
* Fix stable_async_base example
|
||||||
|
|
||||||
API Changes:
|
API Changes:
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
<member><link linkend="beast.ref.boost__beast__file_stdio">file_stdio</link></member>
|
<member><link linkend="beast.ref.boost__beast__file_stdio">file_stdio</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__file_win32">file_win32</link></member>
|
<member><link linkend="beast.ref.boost__beast__file_win32">file_win32</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__flat_stream">flat_stream</link> <emphasis role="green">★</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__flat_stream">flat_stream</link> <emphasis role="green">★</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__handler_ptr">handler_ptr</link></member>
|
|
||||||
<member><link linkend="beast.ref.boost__beast__iequal">iequal</link></member>
|
<member><link linkend="beast.ref.boost__beast__iequal">iequal</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__iless">iless</link></member>
|
<member><link linkend="beast.ref.boost__beast__iless">iless</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__rate_policy_access">rate_policy_access</link> <emphasis role="green">★</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__rate_policy_access">rate_policy_access</link> <emphasis role="green">★</emphasis></member>
|
||||||
|
@ -468,12 +468,19 @@ public:
|
|||||||
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
|
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
|
||||||
using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
|
using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
|
||||||
|
|
||||||
struct op : base_type
|
struct op : base_type, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
// This object must have a stable address
|
// This object must have a stable address
|
||||||
struct temporary_data
|
struct temporary_data
|
||||||
{
|
{
|
||||||
|
// Although std::string is in theory movable, most implementations
|
||||||
|
// use a "small buffer optimization" which means that we might
|
||||||
|
// be submitting a buffer to the write operation and then
|
||||||
|
// moving the string, invalidating the buffer. To prevent
|
||||||
|
// undefined behavior we store the string object itself at
|
||||||
|
// a stable location.
|
||||||
std::string const message;
|
std::string const message;
|
||||||
|
|
||||||
net::steady_timer timer;
|
net::steady_timer timer;
|
||||||
|
|
||||||
temporary_data(std::string message_, net::io_context& ctx)
|
temporary_data(std::string message_, net::io_context& ctx)
|
||||||
@ -486,54 +493,55 @@ public:
|
|||||||
AsyncWriteStream& stream_;
|
AsyncWriteStream& stream_;
|
||||||
std::size_t repeats_;
|
std::size_t repeats_;
|
||||||
temporary_data& data_;
|
temporary_data& data_;
|
||||||
enum { starting, waiting, writing } state_;
|
|
||||||
|
|
||||||
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
|
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
|
||||||
: base_type(std::move(handler), stream.get_executor())
|
: base_type(std::move(handler), stream.get_executor())
|
||||||
, stream_(stream)
|
, stream_(stream)
|
||||||
, repeats_(repeats)
|
, repeats_(repeats)
|
||||||
, state_(starting)
|
|
||||||
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
|
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
|
||||||
{
|
{
|
||||||
(*this)(); // start the operation
|
(*this)(); // start the operation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Including this file provides the keywords for macro-based coroutines
|
||||||
|
#include <boost/asio/yield.hpp>
|
||||||
|
|
||||||
void operator()(error_code ec = {}, std::size_t = 0)
|
void operator()(error_code ec = {}, std::size_t = 0)
|
||||||
{
|
{
|
||||||
if (!ec)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
switch (state_)
|
// If repeats starts at 0 then we must complete immediately. But
|
||||||
|
// we can't call the final handler from inside the initiating
|
||||||
|
// function, so we post our intermediate handler first. We use
|
||||||
|
// net::async_write with an empty buffer instead of calling
|
||||||
|
// net::post to avoid an extra function template instantiation, to
|
||||||
|
// keep compile times lower and make the resulting executable smaller.
|
||||||
|
yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
|
||||||
|
while(! ec && repeats_-- > 0)
|
||||||
{
|
{
|
||||||
case starting:
|
// Send the string. We construct a `const_buffer` here to guarantee
|
||||||
// If repeats starts at 0 then we must complete immediately. But we can't call the final
|
// that we do not create an additional function template instantation
|
||||||
// handler from inside the initiating function, so we post our intermediate handler first.
|
// of net::async_write, since we already instantiated it above for
|
||||||
if(repeats_ == 0)
|
// net::const_buffer.
|
||||||
return net::post(std::move(*this));
|
|
||||||
|
|
||||||
case writing:
|
yield net::async_write(stream_,
|
||||||
if (repeats_ > 0)
|
net::const_buffer(net::buffer(data_.message)), std::move(*this));
|
||||||
{
|
if(ec)
|
||||||
--repeats_;
|
break;
|
||||||
state_ = waiting;
|
|
||||||
data_.timer.expires_after(std::chrono::seconds(1));
|
|
||||||
|
|
||||||
// Composed operation not yet complete.
|
// Set the timer and wait
|
||||||
return data_.timer.async_wait(std::move(*this));
|
data_.timer.expires_after(std::chrono::seconds(1));
|
||||||
}
|
yield data_.timer.async_wait(std::move(*this));
|
||||||
|
|
||||||
// Composed operation complete, continue below.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case waiting:
|
|
||||||
// Composed operation not yet complete.
|
|
||||||
state_ = writing;
|
|
||||||
return net::async_write(stream_, net::buffer(data_.message), std::move(*this));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The base class destroys the temporary data automatically, before invoking the final completion handler
|
// The base class destroys the temporary data automatically,
|
||||||
|
// before invoking the final completion handler
|
||||||
this->complete_now(ec);
|
this->complete_now(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Including this file undefines the macros for the coroutines
|
||||||
|
#include <boost/asio/unyield.hpp>
|
||||||
};
|
};
|
||||||
|
|
||||||
net::async_completion<WriteHandler, void(error_code)> completion(handler);
|
net::async_completion<WriteHandler, void(error_code)> completion(handler);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <boost/beast/_experimental/test/stream.hpp>
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
#include <boost/beast/core/error.hpp>
|
#include <boost/beast/core/error.hpp>
|
||||||
#include <boost/asio/async_result.hpp>
|
#include <boost/asio/async_result.hpp>
|
||||||
|
#include <boost/asio/coroutine.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/asio/system_executor.hpp>
|
#include <boost/asio/system_executor.hpp>
|
||||||
@ -643,12 +644,19 @@ public:
|
|||||||
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
|
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
|
||||||
using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
|
using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
|
||||||
|
|
||||||
struct op : base_type
|
struct op : base_type, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
// This object must have a stable address
|
// This object must have a stable address
|
||||||
struct temporary_data
|
struct temporary_data
|
||||||
{
|
{
|
||||||
|
// Although std::string is in theory movable, most implementations
|
||||||
|
// use a "small buffer optimization" which means that we might
|
||||||
|
// be submitting a buffer to the write operation and then
|
||||||
|
// moving the string, invalidating the buffer. To prevent
|
||||||
|
// undefined behavior we store the string object itself at
|
||||||
|
// a stable location.
|
||||||
std::string const message;
|
std::string const message;
|
||||||
|
|
||||||
net::steady_timer timer;
|
net::steady_timer timer;
|
||||||
|
|
||||||
temporary_data(std::string message_, net::io_context& ctx)
|
temporary_data(std::string message_, net::io_context& ctx)
|
||||||
@ -658,14 +666,12 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { starting, waiting, writing } state_;
|
|
||||||
AsyncWriteStream& stream_;
|
AsyncWriteStream& stream_;
|
||||||
std::size_t repeats_;
|
std::size_t repeats_;
|
||||||
temporary_data& data_;
|
temporary_data& data_;
|
||||||
|
|
||||||
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
|
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
|
||||||
: base_type(std::move(handler), stream.get_executor())
|
: base_type(std::move(handler), stream.get_executor())
|
||||||
, state_(starting)
|
|
||||||
, stream_(stream)
|
, stream_(stream)
|
||||||
, repeats_(repeats)
|
, repeats_(repeats)
|
||||||
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
|
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
|
||||||
@ -673,42 +679,45 @@ public:
|
|||||||
(*this)(); // start the operation
|
(*this)(); // start the operation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Including this file provides the keywords for macro-based coroutines
|
||||||
|
#include <boost/asio/yield.hpp>
|
||||||
|
|
||||||
void operator()(error_code ec = {}, std::size_t = 0)
|
void operator()(error_code ec = {}, std::size_t = 0)
|
||||||
{
|
{
|
||||||
if (!ec)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
switch (state_)
|
// If repeats starts at 0 then we must complete immediately. But
|
||||||
|
// we can't call the final handler from inside the initiating
|
||||||
|
// function, so we post our intermediate handler first. We use
|
||||||
|
// net::async_write with an empty buffer instead of calling
|
||||||
|
// net::post to avoid an extra function template instantiation, to
|
||||||
|
// keep compile times lower and make the resulting executable smaller.
|
||||||
|
yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
|
||||||
|
while(! ec && repeats_-- > 0)
|
||||||
{
|
{
|
||||||
case starting:
|
// Send the string. We construct a `const_buffer` here to guarantee
|
||||||
// If repeats starts at 0 then we must complete immediately. But we can't call the final
|
// that we do not create an additional function template instantation
|
||||||
// handler from inside the initiating function, so we post our intermediate handler first.
|
// of net::async_write, since we already instantiated it above for
|
||||||
if(repeats_ == 0)
|
// net::const_buffer.
|
||||||
return net::post(std::move(*this));
|
|
||||||
|
|
||||||
case writing:
|
yield net::async_write(stream_,
|
||||||
if (repeats_ > 0)
|
net::const_buffer(net::buffer(data_.message)), std::move(*this));
|
||||||
{
|
if(ec)
|
||||||
--repeats_;
|
break;
|
||||||
state_ = waiting;
|
|
||||||
data_.timer.expires_after(std::chrono::seconds(1));
|
|
||||||
|
|
||||||
// Composed operation not yet complete.
|
// Set the timer and wait
|
||||||
return data_.timer.async_wait(std::move(*this));
|
data_.timer.expires_after(std::chrono::seconds(1));
|
||||||
}
|
yield data_.timer.async_wait(std::move(*this));
|
||||||
|
|
||||||
// Composed operation complete, continue below.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case waiting:
|
|
||||||
// Composed operation not yet complete.
|
|
||||||
state_ = writing;
|
|
||||||
return net::async_write(stream_, net::buffer(data_.message), std::move(*this));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The base class destroys the temporary data automatically, before invoking the final completion handler
|
// The base class destroys the temporary data automatically,
|
||||||
|
// before invoking the final completion handler
|
||||||
this->complete_now(ec);
|
this->complete_now(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Including this file undefines the macros for the coroutines
|
||||||
|
#include <boost/asio/unyield.hpp>
|
||||||
};
|
};
|
||||||
|
|
||||||
net::async_completion<WriteHandler, void(error_code)> completion(handler);
|
net::async_completion<WriteHandler, void(error_code)> completion(handler);
|
||||||
|
Reference in New Issue
Block a user