async_op_base is a public interface:

This utility simplifies the authoring of composed
operations, see documentation for details.
This commit is contained in:
Vinnie Falco
2019-01-07 13:35:19 -08:00
parent b46953f1bd
commit 5292df6e72
24 changed files with 1032 additions and 608 deletions

View File

@@ -5,6 +5,7 @@ Version 202
* Tidy up basic_stream_socket docs
* Refactor async_op_base
* Use async_op_base
* async_op_base is a public interface
--------------------------------------------------------------------------------

View File

@@ -38,6 +38,27 @@ composed operations:
[table Asynchronous Helpers
[[Name][Description]]
[[
[link beast.ref.boost__beast__async_op_base `async_op_base`]
[link beast.ref.boost__beast__stable_async_op_base `stable_async_op_base`]
][
This class is designed to be used as a base class when authoriing
composed asynchronous operations expressed as an intermediate
completion handler. This eliminates the need for the extensive
boilerplate to propgate the associated executor, associated
allocator, and legacy completion handler hooks.
]]
[[
[link beast.ref.boost__beast__allocate_stable `allocate_stable`]
][
For composed operation algorithms which need stable storage for
temporary objects, this function may be used. Memory for the
stable storage is allocated using the allocator associated with
the final completion handler. The implementation automatically
destroys the temporary object before the final completion handler
is invoked, or when the intermediate completion handler is
destroyed.
]]
[[
[link beast.ref.boost__beast__bind_handler `bind_handler`]
][

View File

@@ -177,6 +177,7 @@
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__async_op_base">async_op_base</link></member>
<member><link linkend="beast.ref.boost__beast__basic_flat_buffer">basic_flat_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__basic_multi_buffer">basic_multi_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__basic_stream_socket">basic_stream_socket</link></member>
@@ -206,6 +207,7 @@
<member><link linkend="beast.ref.boost__beast__static_buffer">static_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__static_buffer_base">static_buffer_base</link></member>
<member><link linkend="beast.ref.boost__beast__static_string">static_string</link></member>
<member><link linkend="beast.ref.boost__beast__stable_async_op_base">stable_async_op_base</link></member>
<member><link linkend="beast.ref.boost__beast__stream_socket">stream_socket</link></member>
<member><link linkend="beast.ref.boost__beast__string_param">string_param</link></member>
<member><link linkend="beast.ref.boost__beast__string_view">string_view</link></member>
@@ -220,6 +222,7 @@
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__allocate_stable">allocate_stable</link></member>
<member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member>
<member><link linkend="beast.ref.boost__beast__bind_back_handler">bind_back_handler</link></member>
<member><link linkend="beast.ref.boost__beast__bind_front_handler">bind_front_handler</link></member>

View File

@@ -11,7 +11,7 @@
#define BOOST_BEAST_CORE_IMPL_FLAT_STREAM_HPP
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
@@ -24,7 +24,7 @@ namespace beast {
template<class NextLayer>
template<class ConstBufferSequence, class Handler>
class flat_stream<NextLayer>::write_op
: public detail::async_op_base<Handler,
: public async_op_base<Handler,
detail::get_executor_type<flat_stream>>
, public net::coroutine
@@ -59,7 +59,7 @@ public:
flat_stream<NextLayer>& s,
ConstBufferSequence const& b,
Handler_&& h)
: detail::async_op_base<Handler,
: async_op_base<Handler,
detail::get_executor_type<flat_stream>>(
s.get_executor(),
std::forward<Handler_>(h))

View File

@@ -11,11 +11,11 @@
#define BOOST_BEAST_CORE_IMPL_ICY_STREAM_HPP
#include <boost/beast/_experimental/core/detail/dynamic_buffer_ref.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_adaptor.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/buffers_ref.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/handler_ptr.hpp>
@@ -121,7 +121,7 @@ public:
template<class NextLayer>
template<class MutableBufferSequence, class Handler>
class icy_stream<NextLayer>::read_op
: public beast::detail::stable_async_op_base<Handler,
: public beast::stable_async_op_base<Handler,
beast::detail::get_executor_type<icy_stream>>
, public net::coroutine
{
@@ -151,10 +151,10 @@ public:
Handler_&& h,
icy_stream& s,
MutableBufferSequence const& b)
: beast::detail::stable_async_op_base<Handler,
: beast::stable_async_op_base<Handler,
beast::detail::get_executor_type<icy_stream>>(
s.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<data>(*this, s, b))
, d_(beast::allocate_stable<data>(*this, s, b))
{
(*this)({}, 0);
}

View File

@@ -0,0 +1,760 @@
//
// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_ASYNC_OP_BASE_HPP
#define BOOST_BEAST_CORE_ASYNC_OP_BASE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/core/exchange.hpp>
#include <boost/core/empty_value.hpp>
#include <utility>
namespace boost {
namespace beast {
/** Base class to provide completion handler boilerplate for composed operations.
A function object submitted to intermediate initiating functions during
a composed operation may derive from this type to inherit all of the
boilerplate to forward the executor, allocator, and legacy customization
points associated with the completion handler invoked at the end of the
composed operation.
The composed operation must be typical; that is, associated with one
executor of an I/O object, and invoking a caller-provided completion
handler when the operation is finished. Classes derived from
@ref async_op_base will acquire these properties:
@li Ownership of the final completion handler provided upon construction.
@li If the final handler has an associated allocator, this allocator will
be propagated to the composed operation subclass. Otherwise, the
associated allocator will be the type specified in the allocator
template parameter, or the default of `std::allocator<void>` if the
parameter is omitted.
@li If the final handler has an associated executor, then it will be used
as the executor associated with the composed operation. Otherwise,
the specified `Executor1` will be the type of executor associated
with the composed operation.
@li An instance of `net::executor_work_guard` for the instance of `Executor1`
shall be maintained until either the final handler is invoked, or the
operation base is destroyed, whichever comes first.
@li Calls to the legacy customization points
`asio_handler_invoke`,
`asio_handler_allocate`,
`asio_handler_deallocate`, and
`asio_handler_is_continuation`,
which use argument-dependent lookup, will be forwarded to the
legacy customization points associated with the handler.
@par Example
The following code demonstrates how @ref async_op_base may be be used to
assist authoring an asynchronous initiating function, by providing all of
the boilerplate to manage the final completion handler in a way that
maintains the allocator and executor associations:
@code
// Asynchronously read into a buffer until the buffer is full, or an error occurs
template<class AsyncReadStream, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void (error_code, std::size_t))
async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
{
using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
using base_type = async_op_base<handler_type, typename AsyncReadStream::executor_type>;
struct read_op : base_type
{
AsyncReadStream& stream_;
net::mutable_buffer buffer_;
std::size_t total_bytes_transferred_;
read_op(
AsyncReadStream& stream,
net::mutable_buffer buffer,
handler_type& handler)
: base_type(stream.get_executor(), std::move(handler))
, stream_(stream)
, buffer_(buffer)
, total_bytes_transferred_(0)
{
(*this)({}, 0, false); // start the operation
}
void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
{
// Adjust the count of bytes and advance our buffer
total_bytes_transferred_ += bytes_transferred;
buffer_ = buffer_ + bytes_transferred;
// Keep reading until buffer is full or an error occurs
if(! ec && buffer_.size() > 0)
return stream_.async_read_some(buffer_, std::move(*this));
// If this is first invocation, we have to post to the executor. Otherwise the
// handler would be invoked before the call to async_read returns, which is disallowed.
if(! is_continuation)
{
// Issue a zero-sized read so our handler runs "as-if" posted using net::post().
// This technique is used to reduce the number of function template instantiations.
return stream_.async_read_some(net::mutable_buffer(buffer_.data(), 0), std::move(*this));
}
// Call the completion handler with the result
this->invoke(ec, total_bytes_transferred_);
}
};
net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
read_op(stream, buffer, init.completion_handler);
return init.result.get();
}
@endcode
Data members of composed operations implemented as completion handlers
do not have stable addresses, as the composed operation object is move
constructed upon each call to an initiating function. For most operations
this is not a problem. For complex operations requiring stable temporary
storage, the class @ref stable_async_op_base is provided which offers
additional functionality:
@li The free function @ref allocate_stable may be used to allocate
one or more temporary objects associated with the composed operation.
@li Memory for stable temporary objects is allocated using the allocator
associated with the composed operation.
@li Stable temporary objects are automatically destroyed, and the memory
freed using the associated allocator, either before the final completion
handler is invoked (a Networking requirement) or when the composed operation
is destroyed, whichever occurs first.
@par Temporary Storage Example
The following example demonstrates how a composed operation may store a
temporary object.
@code
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@tparam Executor1 The type of the executor used when the handler has no
associated executor. An instance of this type must be provided upon
construction. The implementation will maintain an executor work guard
and a copy of this instance.
@tparam Allocator The allocator type to use if the handler does not
have an associated allocator. If this parameter is omitted, then
`std::allocator<void>` will be used. If the specified allocator is
not default constructible, an instance of the type must be provided
upon construction.
@see @ref stable_async_op_base
*/
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class async_op_base
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<Allocator>
#endif
{
static_assert(
net::is_executor<Executor1>::value,
"Executor requirements not met");
Handler h_;
net::executor_work_guard<Executor1> wg_;
virtual
void
before_invoke_hook()
{
}
public:
/// Move Constructor
async_op_base(async_op_base&& other) = default;
/** The type of allocator associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the associated allocator of the derived class will
be this type.
*/
using allocator_type =
net::associated_allocator_t<Handler, Allocator>;
/** The type of executor associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the associated executor of the derived class will
be this type.
*/
using executor_type =
net::associated_executor_t<Handler, Executor1>;
/** Returns the allocator associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the object returned from this function will be used
as the associated allocator of the derived class.
*/
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_,
boost::empty_value<Allocator>::get());
}
/** Returns the executor associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the object returned from this function will be used
as the associated executor of the derived class.
*/
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, wg_.get_executor());
}
/// Returns the handler associated with this object
Handler const&
handler() const noexcept
{
return h_;
}
/** Returns ownership of the handler associated with this object
This function is used to transfer ownership of the handler to
the caller, by move-construction. After the move, the only
valid operations on the base object are move construction and
destruction.
*/
Handler
release_handler()
{
return std::move(h_);
}
protected:
/** Constructor
@param handler The final completion handler. The type of this
object must meet the requirements of <em>CompletionHandler</em>.
@param ex1 The executor associated with the implied I/O object
target of the operation. The implementation shall maintain an
executor work guard for the lifetime of the operation, or until
the final completion handler is invoked, whichever is shorter.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler>
async_op_base(
Executor1 const& ex1,
Handler&& handler,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
async_op_base
>::value>::type
>
async_op_base(
Executor1 const& ex1,
Handler_&& handler)
: h_(std::forward<Handler_>(handler))
, wg_(ex1)
{
}
template<class Handler_>
async_op_base(
Executor1 const& ex1,
Handler_&& handler,
Allocator const& alloc)
: boost::empty_value<Allocator>(alloc)
, h_(std::forward<Handler_>(handler))
, wg_(ex1)
{
}
#endif
/** Invoke the final completion handler.
This invokes the final completion handler with the specified
arguments forwarded. It is undefined to call this member
function more than once.
Any temporary objects allocated with @ref allocate_stable will
be automatically destroyed before the final completion handler
is invoked.
*/
template<class... Args>
void
invoke(Args&&... args)
{
this->before_invoke_hook();
wg_.reset();
h_(std::forward<Args>(args)...);
}
#if ! BOOST_BEAST_DOXYGEN
public:
template<
class Handler_,
class Executor1_,
class Allocator_,
class Function>
friend
void asio_handler_invoke(
Function&& f,
async_op_base<
Handler_, Executor1_, Allocator_>* p);
friend
void* asio_handler_allocate(
std::size_t size, async_op_base* p)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(p->h_));
}
friend
void asio_handler_deallocate(
void* mem, std::size_t size,
async_op_base* p)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(mem, size,
std::addressof(p->h_));
}
friend
bool asio_handler_is_continuation(
async_op_base* p)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(p->h_));
}
#endif
};
//------------------------------------------------------------------------------
/** Base class to provide completion handler boilerplate for composed operations.
A function object submitted to intermediate initiating functions during
a composed operation may derive from this type to inherit all of the
boilerplate to forward the executor, allocator, and legacy customization
points associated with the completion handler invoked at the end of the
composed operation.
The composed operation must be typical; that is, associated with one
executor of an I/O object, and invoking a caller-provided completion
handler when the operation is finished. Classes derived from
@ref async_op_base will acquire these properties:
@li Ownership of the final completion handler provided upon construction.
@li If the final handler has an associated allocator, this allocator will
be propagated to the composed operation subclass. Otherwise, the
associated allocator will be the type specified in the allocator
template parameter, or the default of `std::allocator<void>` if the
parameter is omitted.
@li If the final handler has an associated executor, then it will be used
as the executor associated with the composed operation. Otherwise,
the specified `Executor1` will be the type of executor associated
with the composed operation.
@li An instance of `net::executor_work_guard` for the instance of `Executor1`
shall be maintained until either the final handler is invoked, or the
operation base is destroyed, whichever comes first.
@li Calls to the legacy customization points
`asio_handler_invoke`,
`asio_handler_allocate`,
`asio_handler_deallocate`, and
`asio_handler_is_continuation`,
which use argument-dependent lookup, will be forwarded to the
legacy customization points associated with the handler.
Data members of composed operations implemented as completion handlers
do not have stable addresses, as the composed operation object is move
constructed upon each call to an initiating function. For most operations
this is not a problem. For complex operations requiring stable temporary
storage, the class @ref stable_async_op_base is provided which offers
additional functionality:
@li The free function @ref allocate_stable may be used to allocate
one or more temporary objects associated with the composed operation.
@li Memory for stable temporary objects is allocated using the allocator
associated with the composed operation.
@li Stable temporary objects are automatically destroyed, and the memory
freed using the associated allocator, either before the final completion
handler is invoked (a Networking requirement) or when the composed operation
is destroyed, whichever occurs first.
@par Example
The following code demonstrates how @ref stable_async_op_base may be be used to
assist authoring an asynchronous initiating function, by providing all of
the boilerplate to manage the final completion handler in a way that maintains
the allocator and executor associations. Furthermore, the operation shown
allocates temporary memory using @ref allocate_stable for the timer and
message, whose addresses must not change between intermediate operations:
@code
// Asynchronously send a message multiple times, once per second
template <class AsyncWriteStream, class T, class WriteHandler>
auto async_write_messages(
AsyncWriteStream& stream,
T const& message,
std::size_t repeat_count,
WriteHandler&& handler) ->
typename net::async_result<
typename std::decay<WriteHandler>::type,
void(error_code)>::return_type
{
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
using base_type = stable_async_op_base<handler_type, typename AsyncWriteStream::executor_type>;
struct op : base_type
{
// This object must have a stable address
struct temporary_data
{
std::string const message;
net::steady_timer timer;
temporary_data(std::string message_, net::io_context& ctx)
: message(std::move(message_))
, timer(ctx)
{
}
};
AsyncWriteStream& stream_;
std::size_t repeats_;
temporary_data& data_;
enum { starting, waiting, writing } state_;
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
: base_type(stream.get_executor(), std::move(handler))
, stream_(stream)
, repeats_(repeats)
, state_(starting)
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
{
(*this)(); // start the operation
}
void operator()(error_code ec = {}, std::size_t = 0)
{
if (!ec)
{
switch (state_)
{
case starting:
// 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.
if(repeats_ == 0)
return net::post(std::move(*this));
case writing:
if (repeats_ > 0)
{
--repeats_;
state_ = waiting;
data_.timer.expires_after(std::chrono::seconds(1));
// Composed operation not yet complete.
return 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
this->invoke(ec);
}
};
net::async_completion<WriteHandler, void(error_code)> completion(handler);
std::ostringstream os;
os << message;
op(stream, repeat_count, os.str(), completion.completion_handler);
return completion.result.get();
}
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@tparam Executor1 The type of the executor used when the handler has no
associated executor. An instance of this type must be provided upon
construction. The implementation will maintain an executor work guard
and a copy of this instance.
@tparam Allocator The allocator type to use if the handler does not
have an associated allocator. If this parameter is omitted, then
`std::allocator<void>` will be used. If the specified allocator is
not default constructible, an instance of the type must be provided
upon construction.
@see @ref allocate_stable, @ref async_op_base
*/
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class stable_async_op_base
: public async_op_base<
Handler, Executor1, Allocator>
{
detail::stable_base* list_ = nullptr;
void
before_invoke_hook() override
{
detail::stable_base::destroy_list(list_);
}
public:
/// Move Constructor
stable_async_op_base(stable_async_op_base&& other)
: async_op_base<Handler, Executor1, Allocator>(
std::move(other))
, list_(boost::exchange(other.list_, nullptr))
{
}
protected:
/** Constructor
@param handler The final completion handler. The type of this
object must meet the requirements of <em>CompletionHandler</em>.
@param ex1 The executor associated with the implied I/O object
target of the operation. The implementation shall maintain an
executor work guard for the lifetime of the operation, or until
the final completion handler is invoked, whichever is shorter.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler>
stable_async_op_base(
Executor1 const& ex1,
Handler&& handler,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
stable_async_op_base
>::value>::type
>
stable_async_op_base(
Executor1 const& ex1,
Handler_&& handler)
: async_op_base<
Handler, Executor1, Allocator>(
ex1, std::forward<Handler_>(handler))
{
}
template<class Handler_>
stable_async_op_base(
Executor1 const& ex1,
Handler_&& handler,
Allocator const& alloc)
: async_op_base<
Handler, Executor1, Allocator>(
ex1, std::forward<Handler_>(handler))
{
}
#endif
/** Destructor
If the completion handler was not invoked, then any
state objects allocated with @ref allocate_stable will
be destroyed here.
*/
~stable_async_op_base()
{
detail::stable_base::destroy_list(list_);
}
/** Allocate a temporary object to hold operation state.
The object will be destroyed just before the completion
handler is invoked, or when the operation base is destroyed.
*/
template<
class State,
class Handler_,
class Executor1_,
class Allocator_,
class... Args>
friend
State&
allocate_stable(
stable_async_op_base<
Handler_, Executor1_, Allocator_>& base,
Args&&... args);
};
#if ! BOOST_BEAST_DOXYGEN
template<
class Handler,
class Executor1,
class Allocator,
class Function>
void asio_handler_invoke(
Function&& f,
async_op_base<
Handler, Executor1, Allocator>* p)
{
using net::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(p->h_));
}
#endif
/** Allocate a temporary object to hold stable asynchronous operation state.
The object will be destroyed just before the completion
handler is invoked, or when the base is destroyed.
@see @ref stable_async_op_base
*/
template<
class State,
class Handler,
class Executor1,
class Allocator,
class... Args>
State&
allocate_stable(
stable_async_op_base<
Handler, Executor1, Allocator>& base,
Args&&... args)
{
struct state;
using allocator_type = typename stable_async_op_base<
Handler, Executor1, Allocator>::allocator_type;
using A = typename detail::allocator_traits<
allocator_type>::template rebind_alloc<state>;
struct state final
: detail::stable_base
, boost::empty_value<allocator_type>
{
State value;
explicit
state(
allocator_type const& alloc,
detail::stable_base*& list,
Args&&... args)
: detail::stable_base(list)
, boost::empty_value<allocator_type>(
boost::empty_init_t{}, alloc)
, value{std::forward<Args>(args)...}
{
}
void destroy() override
{
A a(this->get());
detail::allocator_traits<A>::destroy(a, this);
detail::allocator_traits<A>::deallocate(a, this, 1);
}
};
struct deleter
{
allocator_type alloc;
state* ptr;
~deleter()
{
if(ptr)
{
A a(alloc);
detail::allocator_traits<A>::deallocate(a, ptr, 1);
}
}
};
A a(base.get_allocator());
deleter d{base.get_allocator(), nullptr};
d.ptr = detail::allocator_traits<A>::allocate(a, 1);
detail::allocator_traits<A>::construct(a, d.ptr,
d.alloc,
base.list_,
std::forward<Args>(args)...);
return boost::exchange(d.ptr, nullptr)->value;
}
} // beast
} // boost
#endif

View File

@@ -10,327 +10,12 @@
#ifndef BOOST_BEAST_CORE_DETAIL_ASYNC_OP_BASE_HPP
#define BOOST_BEAST_CORE_DETAIL_ASYNC_OP_BASE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/core/exchange.hpp>
#include <boost/core/empty_value.hpp>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
//------------------------------------------------------------------------------
/** Base class to provide completion handler boilerplate for composed operations.
A function object submitted to intermediate initiating functions during
a composed operation may derive from this type to inherit all of the
boilerplate to forward the executor, allocator, and legacy customization
points associated with the completion handler invoked at the end of the
composed operation.
The composed operation must be typical; that is, associated with the executor
of a particular I/O object, and invoking a caller-provided completion handler
when the operation is finished. Specifically, classes derived from
@ref async_op_base will acquire these properties:
@li Ownership of the final completion handler provided upon construction.
@li If the final handler has an associated allocator, this allocator will
be propagated to the composed operation subclass. Otherwise, the
associated allocator will be the type specified in the allocator
template parameter, or the default of `std::allocator<void>` if the
parameter is omitted.
@li If the final handler has an associated executor, then it will be used
as the executor associated with the composed operation. Otherwise,
the specified `Executor1` will be the type of executor associated
with the composed operation.
@li An instance of `net::executor_work_guard` for the instance of `Executor1`
shall be maintained until either the final handler is invoked, or the
operation base is destroyed, whichever comes first.
@li Calls to the legacy customization points
`asio_handler_invoke`,
`asio_handler_allocate`,
`asio_handler_deallocate`, and
`asio_handler_is_continuation`,
which use argument-dependent lookup, will be forwarded to the
legacy customization points associated with the handler.
Data members of composed operations implemented as completion handlers
do not have stable addresses, as the composed operation object is move
constructed upon each call to an initiating function. For most operations
this is not a problem. For complex operations requiring stable temporary
storage, the class @ref stable_async_op_base is provided which offers
additional functionality:
@li The free function @ref allocate_stable may be used to allocate
one or more temporary objects associated with the composed operation.
@li Memory for stable temporary objects is allocated using the allocator
associated with the composed operation.
@li Stable temporary objects are automatically destroyed, and the memory
freed using the associated allocator, either before the final completion
handler is invoked (a Networking requirement) or when the composed operation
is destroyed, whichever occurs first.
@par Temporary Storage Example
The following example demonstrates how a composed operation may store a
temporary object.
@code
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@tparam Executor1 The type of the executor used when the handler has no
associated executor. An instance of this type must be provided upon
construction. The implementation will maintain an executor work guard
and a copy of this instance.
@tparam Allocator The allocator type to use if the handler does not
have an associated allocator. If this parameter is omitted, then
`std::allocator<void>` will be used. If the specified allocator is
not default constructible, an instance of the type must be provided
upon construction.
@see @ref allocate_stable
*/
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class async_op_base
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<Allocator>
#endif
{
static_assert(
net::is_executor<Executor1>::value,
"Executor requirements not met");
Handler h_;
net::executor_work_guard<Executor1> wg_;
virtual
void
before_invoke_hook()
{
}
public:
/// Move Constructor
async_op_base(async_op_base&& other) = default;
/** The type of allocator associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the associated allocator of the derived class will
be this type.
*/
using allocator_type =
net::associated_allocator_t<Handler, Allocator>;
/** The type of executor associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the associated executor of the derived class will
be this type.
*/
using executor_type =
net::associated_executor_t<Handler, Executor1>;
/** Returns the allocator associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the object returned from this function will be used
as the associated allocator of the derived class.
*/
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_,
boost::empty_value<Allocator>::get());
}
/** Returns the executor associated with this object.
If a class derived from @ref async_op_base is a completion
handler, then the object returned from this function will be used
as the associated executor of the derived class.
*/
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, wg_.get_executor());
}
/// Returns the handler associated with this object
Handler const&
handler() const noexcept
{
return h_;
}
/** Returns ownership of the handler associated with this object
This function is used to transfer ownership of the handler to
the caller, by move-construction. After the move, the only
valid operations on the base object are move construction and
destruction.
*/
Handler
release_handler()
{
return std::move(h_);
}
protected:
/** Constructor
@param target The target of the operation. This object must
have class type, with a member function named `get_executor`
publicly accessible whose return type meets the requirements
of <em>Executor</em>.
@param handler The final completion handler. The type of this
object must meet the requirements of <em>CompletionHandler</em>.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler>
async_op_base(
Executor1 const& ex1,
Handler&& handler,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
async_op_base
>::value>::type
>
async_op_base(
Executor1 const& ex1,
Handler_&& handler)
: h_(std::forward<Handler_>(handler))
, wg_(ex1)
{
}
template<class Handler_>
async_op_base(
Executor1 const& ex1,
Handler_&& handler,
Allocator const& alloc)
: boost::empty_value<Allocator>(alloc)
, h_(std::forward<Handler_>(handler))
, wg_(ex1)
{
}
#endif
/** Invoke the final completion handler.
This invokes the final completion handler with the specified
arguments forwarded. It is undefined to call this member
function more than once.
*/
template<class... Args>
void
invoke(Args&&... args)
{
this->before_invoke_hook();
wg_.reset();
h_(std::forward<Args>(args)...);
}
#if ! BOOST_BEAST_DOXYGEN
public:
template<
class Handler_,
class Executor1_,
class Allocator_,
class Function>
friend
void asio_handler_invoke(
Function&& f,
async_op_base<
Handler_, Executor1_, Allocator_>* p);
friend
void* asio_handler_allocate(
std::size_t size, async_op_base* p)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(p->h_));
}
friend
void asio_handler_deallocate(
void* mem, std::size_t size,
async_op_base* p)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(mem, size,
std::addressof(p->h_));
}
friend
bool asio_handler_is_continuation(
async_op_base* p)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(p->h_));
}
#endif
};
#if ! BOOST_BEAST_DOXYGEN
template<
class Handler,
class Executor1,
class Allocator,
class Function>
void asio_handler_invoke(
Function&& f,
async_op_base<
Handler, Executor1, Allocator>* p)
{
using net::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(p->h_));
}
#endif
//------------------------------------------------------------------------------
// namspace detail {
class stable_async_op_detail
{
protected:
struct stable_base
{
static
@@ -354,166 +39,6 @@ protected:
{
}
};
};
// } detail
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class stable_async_op_base
: public async_op_base<
Handler, Executor1, Allocator>
#if ! BOOST_BEAST_DOXYGEN
, private stable_async_op_detail
#endif
{
stable_base* list_ = nullptr;
void
before_invoke_hook() override
{
stable_base::destroy_list(list_);
}
public:
/// Move Constructor
stable_async_op_base(stable_async_op_base&& other)
: async_op_base<Handler, Executor1, Allocator>(
std::move(other))
, list_(boost::exchange(other.list_, nullptr))
{
}
protected:
/** Constructor
@param target The target of the operation. This object must
have class type, with a member function named `get_executor`
publicly accessible whose return type meets the requirements
of <em>Executor</em>.
@param handler The final completion handler. The type of this
object must meet the requirements of <em>CompletionHandler</em>.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler>
stable_async_op_base(
Executor1 const& ex1,
Handler&& handler,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
stable_async_op_base
>::value>::type
>
stable_async_op_base(
Executor1 const& ex1,
Handler_&& handler)
: async_op_base<
Handler, Executor1, Allocator>(
ex1, std::forward<Handler_>(handler))
{
}
template<class Handler_>
stable_async_op_base(
Executor1 const& ex1,
Handler_&& handler,
Allocator const& alloc)
: async_op_base<
Handler, Executor1, Allocator>(
ex1, std::forward<Handler_>(handler))
{
}
#endif
/** Destructor
If the completion handler was not invoked, then any
state objects allocated with @ref allocate_stable will
be destroyed here.
*/
~stable_async_op_base()
{
stable_base::destroy_list(list_);
}
/** Allocate a temporary object to hold operation state.
The object will be destroyed just before the completion
handler is invoked, or when the operation base is destroyed.
*/
template<
class State,
class Handler_,
class Executor1_,
class Allocator_,
class... Args>
friend
State&
allocate_stable(
stable_async_op_base<
Handler_, Executor1_, Allocator_>& base,
Args&&... args);
};
/** Allocate a temporary object to hold stable asynchronous operation state.
The object will be destroyed just before the completion
handler is invoked, or when the base is destroyed.
*/
template<
class State,
class Handler,
class Executor1,
class Allocator,
class... Args>
State&
allocate_stable(
stable_async_op_base<
Handler, Executor1, Allocator>& base,
Args&&... args)
{
using base_type = stable_async_op_base<
Handler, Executor1, Allocator>;
using stable_base =
typename base_type::stable_base;
struct state final
: stable_base
{
State value;
explicit
state(
stable_base*& list,
Args&&... args)
: stable_base(list)
, value(std::forward<Args>(args)...)
{
}
void destroy() override
{
delete this;
}
};
return (::new state(base.list_,
std::forward<Args>(args)...))->value;
}
} // detail
} // beast

View File

@@ -11,9 +11,9 @@
#define BOOST_BEAST_DETAIL_IMPL_READ_HPP
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/throw_exception.hpp>

View File

@@ -10,8 +10,8 @@
#ifndef BOOST_BEAST_HANDLER_PTR_HPP
#define BOOST_BEAST_HANDLER_PTR_HPP
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/assert.hpp>
#include <type_traits>
#include <utility>

View File

@@ -10,8 +10,8 @@
#ifndef BOOST_BEAST_CORE_IMPL_BASIC_STREAM_SOCKET_HPP
#define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_SOCKET_HPP
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
@@ -92,7 +92,7 @@ struct basic_stream_socket<
template<class Protocol, class Executor>
template<class Buffers, class Handler>
class basic_stream_socket<Protocol, Executor>::read_op
: public detail::async_op_base<Handler, Executor>
: public async_op_base<Handler, Executor>
, public boost::asio::coroutine
{
typename basic_stream_socket<
@@ -106,7 +106,7 @@ public:
basic_stream_socket& s,
Buffers const& b,
Handler_&& h)
: detail::async_op_base<Handler, Executor>(
: async_op_base<Handler, Executor>(
s.get_executor(), std::forward<Handler_>(h))
, impl_(*s.impl_)
, pg_(impl_.read_pending)
@@ -209,7 +209,7 @@ public:
template<class Protocol, class Executor>
template<class Buffers, class Handler>
class basic_stream_socket<Protocol, Executor>::write_op
: public detail::async_op_base<Handler, Executor>
: public async_op_base<Handler, Executor>
, public boost::asio::coroutine
{
typename basic_stream_socket<
@@ -223,7 +223,7 @@ public:
basic_stream_socket& s,
Buffers const& b,
Handler_&& h)
: detail::async_op_base<Handler, Executor>(
: async_op_base<Handler, Executor>(
s.get_executor(), std::forward<Handler_>(h))
, impl_(*s.impl_)
, pg_(impl_.write_pending)
@@ -328,7 +328,7 @@ namespace detail {
template<
class Protocol, class Executor, class Handler>
class stream_socket_connect_op
: public detail::async_op_base<Handler, Executor>
: public async_op_base<Handler, Executor>
{
using stream_type =
beast::basic_stream_socket<Protocol, Executor>;
@@ -349,7 +349,7 @@ public:
Endpoints const& eps,
Condition cond,
Handler_&& h)
: detail::async_op_base<Handler, Executor>(
: async_op_base<Handler, Executor>(
s.get_executor(), std::forward<Handler_>(h))
, impl_(*s.impl_)
, pg0_(impl_.read_pending)
@@ -376,7 +376,7 @@ public:
Iterator begin, Iterator end,
Condition cond,
Handler_&& h)
: detail::async_op_base<Handler, Executor>(
: async_op_base<Handler, Executor>(
s.get_executor(), std::forward<Handler_>(h))
, impl_(*s.impl_)
, pg0_(impl_.read_pending)

View File

@@ -10,11 +10,11 @@
#ifndef BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_HPP
#define BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_HPP
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/read_size.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/asio/post.hpp>
#include <boost/throw_exception.hpp>
@@ -26,7 +26,7 @@ template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler>
class buffered_read_stream<
Stream, DynamicBuffer>::read_some_op
: public detail::async_op_base<
: public async_op_base<
Handler, detail::get_executor_type<buffered_read_stream>>
{
buffered_read_stream& s_;
@@ -42,7 +42,7 @@ public:
Handler_&& h,
buffered_read_stream& s,
MutableBufferSequence const& b)
: detail::async_op_base<
: async_op_base<
Handler, detail::get_executor_type<buffered_read_stream>>(
s.get_executor(), std::forward<Handler_>(h))
, s_(s)

View File

@@ -12,10 +12,10 @@
#if BOOST_BEAST_USE_WIN32_FILE
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/http/serializer.hpp>
#include <boost/asio/async_result.hpp>
@@ -333,7 +333,7 @@ template<
class Protocol, class Handler,
bool isRequest, class Fields>
class write_some_win32_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, typename net::basic_stream_socket<
Protocol>::executor_type>
{
@@ -350,7 +350,7 @@ public:
net::basic_stream_socket<Protocol>& s,
serializer<isRequest,
basic_file_body<file_win32>,Fields>& sr)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, typename net::basic_stream_socket<
Protocol>::executor_type>(
s.get_executor(),

View File

@@ -14,8 +14,7 @@
#include <boost/beast/http/error.hpp>
#include <boost/beast/http/parser.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/detail/read.hpp>
#include <boost/asio/error.hpp>
@@ -150,7 +149,7 @@ template<
bool isRequest, class Body, class Allocator,
class Handler>
class read_msg_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<Stream>>
, public net::coroutine
{
@@ -185,10 +184,10 @@ public:
DynamicBuffer& b,
message_type& m,
Handler_&& h)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<Stream>>(
s.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<data>(
, d_(beast::allocate_stable<data>(
*this, s, m))
{
http::async_read(d_.s, b, d_.p, std::move(*this));

View File

@@ -11,12 +11,12 @@
#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP
#include <boost/beast/http/type_traits.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/ostream.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/write.hpp>
@@ -34,7 +34,7 @@ template<
class Stream, class Handler,
bool isRequest, class Body, class Fields>
class write_some_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<Stream>>
{
Stream& s_;
@@ -72,7 +72,7 @@ public:
Handler_&& h,
Stream& s,
serializer<isRequest, Body, Fields>& sr)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<Stream>>(
s.get_executor(), std::forward<Handler_>(h))
, s_(s)
@@ -154,7 +154,7 @@ template<
class Stream, class Handler, class Predicate,
bool isRequest, class Body, class Fields>
class write_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<Stream>>
, public net::coroutine
{
@@ -168,7 +168,7 @@ public:
Handler_&& h,
Stream& s,
serializer<isRequest, Body, Fields>& sr)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<Stream>>(
s.get_executor(), std::forward<Handler_>(h))
, s_(s)
@@ -215,7 +215,7 @@ template<
class Stream, class Handler,
bool isRequest, class Body, class Fields>
class write_msg_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<Stream>>
{
Stream& s_;
@@ -229,11 +229,11 @@ public:
Stream& s,
Handler_&& h,
Args&&... args)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<Stream>>(
s.get_executor(), std::forward<Handler_>(h))
, s_(s)
, sr_(beast::detail::allocate_stable<
, sr_(beast::allocate_stable<
serializer<isRequest, Body, Fields>>(
*this, std::forward<Args>(args)...))
{

View File

@@ -16,8 +16,8 @@
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/buffer.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
@@ -36,7 +36,7 @@ namespace websocket {
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer, deflateSupported>::response_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -68,10 +68,10 @@ public:
Handler_&& h,
stream<NextLayer, deflateSupported>& ws,
Args&&... args)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<data>(
, d_(beast::allocate_stable<data>(
*this, ws, std::forward<Args>(args)...))
{
}
@@ -107,7 +107,7 @@ public:
template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler>
class stream<NextLayer, deflateSupported>::accept_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -136,10 +136,10 @@ public:
Handler_&& h,
stream<NextLayer, deflateSupported>& ws,
Args&&... args)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<data>(
, d_(beast::allocate_stable<data>(
*this, ws, std::forward<Args>(args)...))
{
}

View File

@@ -11,9 +11,9 @@
#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP
#include <boost/beast/websocket/teardown.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/asio/coroutine.hpp>
@@ -35,7 +35,7 @@ namespace websocket {
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer, deflateSupported>::close_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -67,10 +67,10 @@ public:
Handler_&& h,
stream<NextLayer, deflateSupported>& ws,
close_reason const& cr)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<state>(
, d_(beast::allocate_stable<state>(
*this, ws, cr))
{
}

View File

@@ -15,8 +15,8 @@
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
@@ -34,7 +34,7 @@ namespace websocket {
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer, deflateSupported>::handshake_op
: public beast::detail::stable_async_op_base<Handler,
: public beast::stable_async_op_base<Handler,
beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -71,10 +71,10 @@ public:
handshake_op(
Handler_&& h,
stream& ws, Args&&... args)
: beast::detail::stable_async_op_base<Handler,
: beast::stable_async_op_base<Handler,
beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<data>(
, d_(beast::allocate_stable<data>(
*this, ws, std::forward<Args>(args)...))
{
}

View File

@@ -10,10 +10,10 @@
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
#define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/beast/websocket/detail/frame.hpp>
#include <boost/asio/coroutine.hpp>
@@ -33,7 +33,7 @@ namespace websocket {
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer, deflateSupported>::ping_op
: public beast::detail::stable_async_op_base<
: public beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -66,10 +66,10 @@ public:
stream<NextLayer, deflateSupported>& ws,
detail::opcode op,
ping_data const& payload)
: beast::detail::stable_async_op_base<
: beast::stable_async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, d_(beast::detail::allocate_stable<state>(
, d_(beast::allocate_stable<state>(
*this, ws, op, payload))
{
}

View File

@@ -11,12 +11,12 @@
#define BOOST_BEAST_WEBSOCKET_IMPL_READ_IPP
#include <boost/beast/websocket/teardown.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/buffer.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/config.hpp>
@@ -77,7 +77,7 @@ template<
class MutableBufferSequence,
class Handler>
class stream<NextLayer, deflateSupported>::read_some_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -98,7 +98,7 @@ public:
Handler_&& h,
stream<NextLayer, deflateSupported>& ws,
MutableBufferSequence const& bs)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, ws_(ws)
@@ -672,7 +672,7 @@ template<
class DynamicBuffer,
class Handler>
class stream<NextLayer, deflateSupported>::read_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -690,7 +690,7 @@ public:
DynamicBuffer& b,
std::size_t limit,
bool some)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, ws_(ws)

View File

@@ -10,9 +10,9 @@
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/post.hpp>
@@ -26,7 +26,7 @@ namespace detail {
template<class Handler>
class teardown_tcp_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<
net::ip::tcp::socket>>
, public net::coroutine
@@ -43,7 +43,7 @@ public:
Handler_&& h,
socket_type& s,
role_type role)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<
net::ip::tcp::socket>>(s.get_executor(),
std::forward<Handler_>(h))

View File

@@ -10,6 +10,7 @@
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
@@ -17,7 +18,6 @@
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/get_executor_type.hpp>
@@ -136,7 +136,7 @@ do_context_takeover_write(role_type role)
template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler>
class stream<NextLayer, deflateSupported>::write_some_op
: public beast::detail::async_op_base<
: public beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>
, public net::coroutine
{
@@ -161,7 +161,7 @@ public:
stream<NextLayer, deflateSupported>& ws,
bool fin,
Buffers const& bs)
: beast::detail::async_op_base<
: beast::async_op_base<
Handler, beast::detail::get_executor_type<stream>>(
ws.get_executor(), std::forward<Handler_>(h))
, ws_(ws)

View File

@@ -16,7 +16,6 @@ add_executable (tests-beast-core
${EXTRAS_FILES}
${TEST_MAIN}
Jamfile
_detail_async_op_base.cpp
_detail_base64.cpp
_detail_buffer.cpp
_detail_clamp.cpp
@@ -25,6 +24,7 @@ add_executable (tests-beast-core
_detail_tuple.cpp
_detail_variant.cpp
_detail_varint.cpp
async_op_base.cpp
buffer_test.hpp
file_test.hpp
basic_stream_socket.cpp

View File

@@ -8,7 +8,6 @@
#
local SOURCES =
_detail_async_op_base.cpp
_detail_base64.cpp
_detail_buffer.cpp
_detail_clamp.cpp
@@ -17,6 +16,7 @@ local SOURCES =
_detail_tuple.cpp
_detail_variant.cpp
_detail_varint.cpp
async_op_base.cpp
basic_stream_socket.cpp
bind_handler.cpp
buffer_traits.cpp

View File

@@ -8,23 +8,24 @@
//
// Test that header file is self-contained.
#include <boost/beast/core/detail/async_op_base.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_executor.hpp>
#include <boost/asio/write.hpp>
#include <boost/core/ignore_unused.hpp>
//------------------------------------------------------------------------------
namespace boost {
namespace beast {
namespace detail {
//namespace {
namespace {
struct ex1_type
{
@@ -158,9 +159,8 @@ asio_handler_is_continuation(
return false;
}
//} // (anon)
} // (anon)
} // detail
} // beast
} // boost
@@ -169,18 +169,18 @@ namespace asio {
template<class Allocator>
struct associated_allocator<
boost::beast::detail::handler<
boost::beast::detail::no_ex,
boost::beast::detail::intrusive_alloc>,
boost::beast::handler<
boost::beast::no_ex,
boost::beast::intrusive_alloc>,
Allocator>
{
using type =
boost::beast::detail::intrusive_alloc::allocator_type;
boost::beast::intrusive_alloc::allocator_type;
static type get(
boost::beast::detail::handler<
boost::beast::detail::no_ex,
boost::beast::detail::intrusive_alloc> const& h,
boost::beast::handler<
boost::beast::no_ex,
boost::beast::intrusive_alloc> const& h,
Allocator const& a = Allocator()) noexcept
{
return type{};
@@ -189,18 +189,18 @@ struct associated_allocator<
template<class Executor>
struct associated_executor<
boost::beast::detail::handler<
boost::beast::detail::intrusive_ex,
boost::beast::detail::no_alloc>,
boost::beast::handler<
boost::beast::intrusive_ex,
boost::beast::no_alloc>,
Executor>
{
using type =
boost::beast::detail::intrusive_ex::executor_type;
boost::beast::intrusive_ex::executor_type;
static type get(
boost::beast::detail::handler<
boost::beast::detail::intrusive_ex,
boost::beast::detail::no_alloc> const& h,
boost::beast::handler<
boost::beast::intrusive_ex,
boost::beast::no_alloc> const& h,
Executor const& a = Executor()) noexcept
{
return type{};
@@ -209,12 +209,12 @@ struct associated_executor<
template<class Allocator>
struct associated_allocator<
boost::beast::detail::legacy_handler, Allocator>
boost::beast::legacy_handler, Allocator>
{
using type = std::allocator<int>;
static type get(
boost::beast::detail::legacy_handler const& h,
boost::beast::legacy_handler const& h,
Allocator const& a = Allocator()) noexcept
{
return type{};
@@ -223,13 +223,13 @@ struct associated_allocator<
template<class Executor>
struct associated_executor<
boost::beast::detail::legacy_handler, Executor>
boost::beast::legacy_handler, Executor>
{
using type = typename
boost::beast::detail::legacy_handler::executor;
boost::beast::legacy_handler::executor;
static type get(
boost::beast::detail::legacy_handler const&,
boost::beast::legacy_handler const&,
Executor const& = Executor()) noexcept
{
return type{};
@@ -243,7 +243,6 @@ struct associated_executor<
namespace boost {
namespace beast {
namespace detail {
class async_op_base_test : public beast::unit_test::suite
{
@@ -510,45 +509,162 @@ public:
//--------------------------------------------------------------------------
#if 0
// VFALCO TODO: This will become the example in the javadoc
template<
class AsyncReadStream,
class MutableBufferSequence,
class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void (error_code, std::size_t))
async_read(
AsyncReadStream& stream,
MutableBufferSequence const& buffers,
ReadHandler&& handler)
// Asynchronously read into a buffer until the buffer is full, or an error occurs
template<class AsyncReadStream, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void (error_code, std::size_t))
async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
{
net::async_completion<CompletionToken, void(error_code, std::size_t)> init{handler};
using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
using base_type = async_op_base<handler_type, typename AsyncReadStream::executor_type>;
using base = async_op_base<
BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t)),
get_executor_type<AsyncReadStream>>;
struct read_op : base
struct op : base_type
{
AsyncReadStream& stream_;
buffers_suffix<MutableBufferSequence> buffers_;
net::mutable_buffer buffer_;
std::size_t total_bytes_transferred_;
void operator()(error_code ec, std::size_t bytes_transferred)
op(
AsyncReadStream& stream,
net::mutable_buffer buffer,
handler_type& handler)
: base_type(stream.get_executor(), std::move(handler))
, stream_(stream)
, buffer_(buffer)
, total_bytes_transferred_(0)
{
(*this)({}, 0, false); // start the operation
}
void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
{
// Adjust the count of bytes and advance our buffer
total_bytes_transferred_ += bytes_transferred;
buffer_ = buffer_ + bytes_transferred;
// Keep reading until buffer is full or an error occurs
if(! ec && buffer_.size() > 0)
return stream_.async_read_some(buffer_, std::move(*this));
// If this is first invocation, we have to post to the executor. Otherwise the
// handler would be invoked before the call to async_read returns, which is disallowed.
if(! is_continuation)
{
// Issue a zero-sized read so our handler runs "as-if" posted using net::post().
// This technique is used to reduce the number of function template instantiations.
return stream_.async_read_some(net::mutable_buffer(buffer_.data(), 0), std::move(*this));
}
// Call the completion handler with the result
this->invoke(ec, total_bytes_transferred_);
}
};
read_op(stream, buffers, std::forward<ReadHandler>(handler));
net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
op(stream, buffer, init.completion_handler);
return init.result.get();
}
#endif
// Asynchronously send a message multiple times, once per second
template <class AsyncWriteStream, class T, class WriteHandler>
auto async_write_messages(
AsyncWriteStream& stream,
T const& message,
std::size_t repeat_count,
WriteHandler&& handler) ->
typename net::async_result<
typename std::decay<WriteHandler>::type,
void(error_code)>::return_type
{
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
using base_type = stable_async_op_base<handler_type, typename AsyncWriteStream::executor_type>;
struct op : base_type
{
// This object must have a stable address
struct temporary_data
{
std::string const message;
net::steady_timer timer;
temporary_data(std::string message_, net::io_context& ctx)
: message(std::move(message_))
, timer(ctx)
{
}
};
enum { starting, waiting, writing } state_;
AsyncWriteStream& stream_;
std::size_t repeats_;
temporary_data& data_;
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
: base_type(stream.get_executor(), std::move(handler))
, state_(starting)
, stream_(stream)
, repeats_(repeats)
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
{
(*this)(); // start the operation
}
void operator()(error_code ec = {}, std::size_t = 0)
{
if (!ec)
{
switch (state_)
{
case starting:
// 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.
if(repeats_ == 0)
return net::post(std::move(*this));
case writing:
if (repeats_ > 0)
{
--repeats_;
state_ = waiting;
data_.timer.expires_after(std::chrono::seconds(1));
// Composed operation not yet complete.
return 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
this->invoke(ec);
}
};
net::async_completion<WriteHandler, void(error_code)> completion(handler);
std::ostringstream os;
os << message;
op(stream, repeat_count, os.str(), completion.completion_handler);
return completion.result.get();
}
void
testJavadocs()
{
struct handler
{
void operator()(error_code = {}, std::size_t = 0)
{
}
};
BEAST_EXPECT((&async_op_base_test::async_read<test::stream, handler>));
BEAST_EXPECT((&async_op_base_test::async_write_messages<test::stream, std::string, handler>));
}
//--------------------------------------------------------------------------
@@ -564,6 +680,5 @@ public:
BEAST_DEFINE_TESTSUITE(beast,core,async_op_base);
} // detail
} // beast
} // boost