From 9e44ae7be5ab40374038def598d9d6ed48d38e92 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 5 Jan 2019 18:51:47 -0800 Subject: [PATCH] Refactor async_op_base: * Renamed to async_op_base (was operation_base) * Executor1 is explicit * Add stable_async_op_base refinement --- CHANGELOG.md | 1 + .../boost/beast/_experimental/test/stream.hpp | 2 +- .../boost/beast/core/basic_stream_socket.hpp | 19 + .../boost/beast/core/detail/async_op_base.hpp | 509 ++++++++++++++++ .../beast/core/detail/get_executor_type.hpp | 35 ++ include/boost/beast/core/detail/impl/read.hpp | 339 ++++------- .../beast/core/detail/operation_base.hpp | 241 -------- include/boost/beast/core/detail/read.hpp | 5 +- .../beast/core/impl/basic_stream_socket.hpp | 343 +++++------ .../boost/beast/core/impl/buffers_suffix.hpp | 6 - include/boost/beast/http/impl/write.ipp | 416 ++++--------- test/beast/core/CMakeLists.txt | 2 +- test/beast/core/Jamfile | 2 +- test/beast/core/_detail_async_op_base.cpp | 569 ++++++++++++++++++ test/beast/core/_detail_operation_base.cpp | 500 --------------- 15 files changed, 1528 insertions(+), 1461 deletions(-) create mode 100644 include/boost/beast/core/detail/async_op_base.hpp create mode 100644 include/boost/beast/core/detail/get_executor_type.hpp delete mode 100644 include/boost/beast/core/detail/operation_base.hpp create mode 100644 test/beast/core/_detail_async_op_base.cpp delete mode 100644 test/beast/core/_detail_operation_base.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 3632ce6b..3dc2f493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Version 202 * Use cxxstd instead of cxxflags * Update coverage badge images * Tidy up basic_stream_socket docs +* Refactor async_op_base -------------------------------------------------------------------------------- diff --git a/include/boost/beast/_experimental/test/stream.hpp b/include/boost/beast/_experimental/test/stream.hpp index ee5a89b5..63159e95 100644 --- a/include/boost/beast/_experimental/test/stream.hpp +++ b/include/boost/beast/_experimental/test/stream.hpp @@ -272,7 +272,7 @@ public: net::io_context::executor_type; /// Return the executor associated with the object. - net::io_context::executor_type + executor_type get_executor() noexcept { return in_->ioc.get_executor(); diff --git a/include/boost/beast/core/basic_stream_socket.hpp b/include/boost/beast/core/basic_stream_socket.hpp index bfc1baa6..398fc08c 100644 --- a/include/boost/beast/core/basic_stream_socket.hpp +++ b/include/boost/beast/core/basic_stream_socket.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,11 @@ class basic_stream_socket static std::size_t constexpr no_limit = (std::numeric_limits::max)(); +// friend class template declaration in a class template is ignored +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88672 +#if BOOST_WORKAROUND(BOOST_GCC, > 0) +public: +#endif struct impl_type : std::enable_shared_from_this { @@ -205,7 +211,17 @@ class basic_stream_socket void close(); // cancel everything void maybe_kick(); // kick the rate timer if needed void on_timer(); // rate timer completion + + using executor_type = Executor; + Executor + get_executor() const noexcept + { + return ex; + } }; +#if BOOST_WORKAROUND(BOOST_GCC, > 0) +private: +#endif // We use shared ownership for the state so it can // outlive the destruction of the stream_socket object, @@ -230,6 +246,9 @@ class basic_stream_socket template friend class basic_stream_socket; + struct read_timeout_handler; + struct write_timeout_handler; + public: /// The type of the next layer. using next_layer_type = net::basic_stream_socket; diff --git a/include/boost/beast/core/detail/async_op_base.hpp b/include/boost/beast/core/detail/async_op_base.hpp new file mode 100644 index 00000000..2d349077 --- /dev/null +++ b/include/boost/beast/core/detail/async_op_base.hpp @@ -0,0 +1,509 @@ +// +// 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_DETAIL_ASYNC_OP_BASE_HPP +#define BOOST_BEAST_CORE_DETAIL_ASYNC_OP_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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` 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 CompletionHandler. + + @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` 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 +> +class async_op_base +#if ! BOOST_BEAST_DOXYGEN + : private boost::empty_value +#endif +{ + static_assert( + net::is_executor::value, + "Executor requirements not met"); + + Handler h_; + net::executor_work_guard 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; + + /** 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; + + /** 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::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_; + } + +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 Executor. + + @param handler The final completion handler. The type of this + object must meet the requirements of CompletionHandler. + + @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 + async_op_base( + Executor1 const& ex1, + Handler&& handler, + Allocator const& alloc = Allocator()); +#else + template< + class Handler_, + class = typename std::enable_if< + ! std::is_same::type, + async_op_base + >::value>::type + > + async_op_base( + Executor1 const& ex1, + Handler_&& handler) + : h_(std::forward(handler)) + , wg_(ex1) + { + } + + template + async_op_base( + Executor1 const& ex1, + Handler_&& handler, + Allocator const& alloc) + : boost::empty_value(alloc) + , h_(std::forward(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 + void + invoke(Args&&... args) + { + this->before_invoke_hook(); + wg_.reset(); + h_(std::forward(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 + void + destroy_list(stable_base*& list) + { + while(list) + { + auto next = list->next_; + list->destroy(); + list = next; + } + } + + protected: + stable_base* next_; + virtual void destroy() = 0; + virtual ~stable_base() = default; + explicit stable_base(stable_base*& list) + : next_(boost::exchange(list, this)) + { + } + }; +}; + +// } detail + +template< + class Handler, + class Executor1, + class Allocator = std::allocator +> +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( + 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 Executor. + + @param handler The final completion handler. The type of this + object must meet the requirements of CompletionHandler. + + @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 + 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::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)) + { + } + + template + stable_async_op_base( + Executor1 const& ex1, + Handler_&& handler, + Allocator const& alloc) + : async_op_base< + Handler, Executor1, Allocator>( + ex1, std::forward(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)...) + { + } + + void destroy() override + { + delete this; + } + }; + + return (::new state(base.list_, + std::forward(args)...))->value; +} + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/get_executor_type.hpp b/include/boost/beast/core/detail/get_executor_type.hpp new file mode 100644 index 00000000..35d38b53 --- /dev/null +++ b/include/boost/beast/core/detail/get_executor_type.hpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2016-2017 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_DETAIL_GET_EXECUTOR_TYPE +#define BOOST_BEAST_DETAIL_GET_EXECUTOR_TYPE + +#include +#include + +namespace boost { +namespace beast { +namespace detail { + +// Workaround for ICE on gcc 4.8 +#if BOOST_WORKAROUND(BOOST_GCC, < 40900) +template +using get_executor_type = + typename std::decay::type::executor_type; +#else +template +using get_executor_type = + decltype(std::declval().get_executor()); +#endif + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/impl/read.hpp b/include/boost/beast/core/detail/impl/read.hpp index f9b4d53a..037d45d6 100644 --- a/include/boost/beast/core/detail/impl/read.hpp +++ b/include/boost/beast/core/detail/impl/read.hpp @@ -10,11 +10,16 @@ #ifndef BOOST_BEAST_DETAIL_IMPL_READ_HPP #define BOOST_BEAST_DETAIL_IMPL_READ_HPP +#include #include +#include +#include #include -#include -#include -#include +#include +#include + +// REMOVE +#define BOOST_BEAST_ENABLE_NON_BLOCKING namespace boost { namespace beast { @@ -32,31 +37,31 @@ template< class DynamicBuffer, class Condition, class Handler> -class read_op : public net::coroutine +class read_op + : public net::coroutine + , public async_op_base< + Handler, get_executor_type> { Stream& s_; - net::executor_work_guard().get_executor())> wg_; DynamicBuffer& b_; Condition cond_; - Handler h_; std::size_t total_ = 0; public: read_op(read_op&&) = default; - read_op(read_op const&) = delete; - template + template read_op( Stream& s, DynamicBuffer& b, Condition cond, - DeducedHandler&& h) - : s_(s) - , wg_(s_.get_executor()) + Handler_&& h) + : async_op_base< + Handler, get_executor_type>( + s.get_executor(), std::forward(h)) + , s_(s) , b_(b) , cond_(cond) - , h_(std::forward(h)) { (*this)({}, 0, false); } @@ -65,100 +70,43 @@ public: operator()( error_code ec, std::size_t bytes_transferred, - bool cont = true); - - // - - using allocator_type = - net::associated_allocator_t; - - using executor_type = net::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - allocator_type - get_allocator() const noexcept + bool cont = true) { - return net::get_associated_allocator(h_); - } - - executor_type - get_executor() const noexcept - { - return net::get_associated_executor( - h_, s_.get_executor()); - } - - friend - void* asio_handler_allocate( - std::size_t size, read_op* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, read_op* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, read_op* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template< - class Stream, class DynamicBuffer, - class Condition, class Handler> -void -read_op:: -operator()( - error_code ec, - std::size_t bytes_transferred, - bool cont) -{ - std::size_t max_size; - std::size_t max_prepare; - BOOST_ASIO_CORO_REENTER(*this) - { - max_size = cond_(ec, total_, b_); - max_prepare = std::min( - std::max( - 512, b_.capacity() - b_.size()), - std::min( - max_size, b_.max_size() - b_.size())); - while(max_prepare > 0) + std::size_t max_size; + std::size_t max_prepare; + BOOST_ASIO_CORO_REENTER(*this) { - BOOST_ASIO_CORO_YIELD - s_.async_read_some( - b_.prepare(max_prepare), std::move(*this)); - b_.commit(bytes_transferred); - total_ += bytes_transferred; max_size = cond_(ec, total_, b_); max_prepare = std::min( std::max( 512, b_.capacity() - b_.size()), std::min( max_size, b_.max_size() - b_.size())); + while(max_prepare > 0) + { + BOOST_ASIO_CORO_YIELD + s_.async_read_some( + b_.prepare(max_prepare), std::move(*this)); + b_.commit(bytes_transferred); + total_ += bytes_transferred; + max_size = cond_(ec, total_, b_); + max_prepare = std::min( + std::max( + 512, b_.capacity() - b_.size()), + std::min( + max_size, b_.max_size() - b_.size())); + } + if(! cont) + { + BOOST_ASIO_CORO_YIELD + net::post(s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, total_)); + } + this->invoke(ec, total_); } - if(! cont) - { - BOOST_ASIO_CORO_YIELD - net::post(s_.get_executor(), - beast::bind_front_handler( - std::move(*this), ec, total_)); - } - h_(ec, total_); } -} +}; //------------------------------------------------------------------------------ @@ -170,14 +118,14 @@ template< class DynamicBuffer, class Condition, class Handler> -class read_non_blocking_op : public net::coroutine +class read_non_blocking_op + : public net::coroutine + , public async_op_base>> { net::basic_stream_socket& s_; - net::executor_work_guard wg_; DynamicBuffer& b_; Condition cond_; - Handler h_; std::size_t limit_; std::size_t total_ = 0; @@ -185,143 +133,90 @@ public: read_non_blocking_op(read_non_blocking_op&&) = default; read_non_blocking_op(read_non_blocking_op const&) = delete; - template + template read_non_blocking_op( net::basic_stream_socket& s, DynamicBuffer& b, Condition cond, - DeducedHandler&& h) - : s_(s) - , wg_(s_.get_executor()) + Handler_&& h) + : async_op_base>>( + s.get_executor(), std::forward(h)) + , s_(s) , b_(b) , cond_(cond) - , h_(std::forward(h)) { (*this)({}, false); } void - operator()(error_code ec, bool cont = true); - - // - - using allocator_type = - net::associated_allocator_t; - - using executor_type = net::associated_executor_t< - Handler, decltype(s_.get_executor())>; - - allocator_type - get_allocator() const noexcept + operator()(error_code ec, bool cont = true) { - return net::get_associated_allocator(h_); + std::size_t n; + std::size_t bytes_transferred; + BOOST_ASIO_CORO_REENTER(*this) + { + limit_ = cond_(ec, total_, b_); + for(;;) + { + n = detail::min( + limit_, b_.max_size() - b_.size()); + if(n == 0) + break; + BOOST_ASIO_CORO_YIELD + s_.async_wait( + net::socket_base::wait_read, std::move(*this)); + if(b_.size() <= default_max_stack_buffer) + { + flat_static_buffer< + default_max_stack_buffer> sb; + bytes_transferred = net::buffer_copy( + sb.prepare(b_.size()), b_.data()); + sb.commit(bytes_transferred); + b_.consume(bytes_transferred); + //detail::shrink_to_fit(b_); + n = detail::min( + limit_, + sb.capacity() - sb.size(), + b_.max_size() - sb.size()); + BOOST_ASSERT(n > 0); + bytes_transferred = + s_.read_some(sb.prepare(n), ec); + sb.commit(bytes_transferred); + total_ += bytes_transferred; + limit_ = cond_(ec, total_, sb); + b_.commit(net::buffer_copy( + b_.prepare(sb.size()), sb.data())); + } + else + { + n = detail::min( + limit_, + s_.available(), + b_.max_size() - b_.size(), + std::max( + 512, b_.capacity() - b_.size())); + BOOST_ASSERT(n > 0); + bytes_transferred = s_.read_some( + b_.prepare(n), ec); + b_.commit(bytes_transferred); + total_ += bytes_transferred; + limit_ = cond_(ec, total_, b_); + } + } + if(! cont) + { + BOOST_ASIO_CORO_YIELD + net::post(s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, total_)); + } + this->invoke(ec, total_); + } } - executor_type - get_executor() const noexcept - { - return net::get_associated_executor( - h_, s_.get_executor()); - } - - friend - void* asio_handler_allocate( - std::size_t size, read_non_blocking_op* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate(void* p, - std::size_t size, read_non_blocking_op* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke( - Function&& f, read_non_blocking_op* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } }; -template< - class Protocol, class DynamicBuffer, - class Condition, class Handler> -void -read_non_blocking_op< - Protocol, DynamicBuffer, Condition, Handler>:: -operator()(error_code ec, bool cont) -{ - std::size_t n; - std::size_t bytes_transferred; - BOOST_ASIO_CORO_REENTER(*this) - { - limit_ = cond_(ec, total_, b_); - for(;;) - { - n = detail::min( - limit_, b_.max_size() - b_.size()); - if(n == 0) - break; - BOOST_ASIO_CORO_YIELD - s_.async_wait( - net::socket_base::wait_read, std::move(*this)); - if(b_.size() <= default_max_stack_buffer) - { - flat_static_buffer< - default_max_stack_buffer> sb; - bytes_transferred = net::buffer_copy( - sb.prepare(b_.size()), b_.data()); - sb.commit(bytes_transferred); - b_.consume(bytes_transferred); - //detail::shrink_to_fit(b_); - n = detail::min( - limit_, - sb.capacity() - sb.size(), - b_.max_size() - sb.size()); - BOOST_ASSERT(n > 0); - bytes_transferred = - s_.read_some(sb.prepare(n), ec); - sb.commit(bytes_transferred); - total_ += bytes_transferred; - limit_ = cond_(ec, total_, sb); - b_.commit(net::buffer_copy( - b_.prepare(sb.size()), sb.data())); - } - else - { - n = detail::min( - limit_, - s_.available(), - b_.max_size() - b_.size(), - std::max( - 512, b_.capacity() - b_.size())); - BOOST_ASSERT(n > 0); - bytes_transferred = s_.read_some( - b_.prepare(n), ec); - b_.commit(bytes_transferred); - total_ += bytes_transferred; - limit_ = cond_(ec, total_, b_); - } - } - if(! cont) - { - BOOST_ASIO_CORO_YIELD - net::post(s_.get_executor(), - beast::bind_front_handler( - std::move(*this), ec, total_)); - } - h_(ec, total_); - } -} #endif //------------------------------------------------------------------------------ diff --git a/include/boost/beast/core/detail/operation_base.hpp b/include/boost/beast/core/detail/operation_base.hpp deleted file mode 100644 index d85169ca..00000000 --- a/include/boost/beast/core/detail/operation_base.hpp +++ /dev/null @@ -1,241 +0,0 @@ -// -// 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_DETAIL_OPERATION_BASE_HPP -#define BOOST_BEAST_CORE_DETAIL_OPERATION_BASE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace detail { - -/** Base class which stores a handler and forwards handler associations. - - This mix-in assists bind wrappers, intermediate handlers, composed - operations, and similar types which store a handler, by acting as - a base class which holds the handler. Any networking customizations - on the handler will be propagated to the derived class. Specifically: - - @li Any allocator associated with the handler will be propagated to - this object. Otherwise, the allocator associated with this - object will be a default allocator which the caller may specify - through a template parameter and constructor parameter. - - @li Any executor associated with the handler will be propagated to - this object. Otherwise, the executor associated with this - object will be a default executor which the caller may specify - through a template parameter and constructor parameter. - - @li 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 declaration produces a class which wraps a - handler and inherits all of the networking customizations - associated with that handler: - - @code - template - struct wrapped_handler : operation_base< - Handler, net::associated_executor_t> - { - template - explicit wrapped_handler (Handler_&& handler) - : operation_base>( - std::forward(handler), net::get_associated_executor(handler)) - { - } - - template - void operator()(Args&&... args) - { - this->handler_(std::forward(args)...); - } - }; - @endcode - - @tparam Handler The type of the completion handler to store. - This type must meet the requirements of CompletionHandler. - - @tparam Executor The executor type to use if the handler does not - have an associated executor. - - @tparam Allocator The allocator type to use if the handler does not - have an associated allocator. If this parameter is omitted, then - `std::allocator` will be used. -*/ -template< - class Handler, - class Executor, - class Allocator = std::allocator -> -class operation_base -#if ! BOOST_BEAST_DOXYGEN - : private boost::empty_value< - net::associated_allocator_t< - Handler, Allocator>, 0> - , private boost::empty_value< - net::associated_executor_t< - Handler, Executor>, 1> -#endif -{ -public: - /** The type of allocator associated with this object. - - If a class derived from @ref operation_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 operation_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, Executor>; - - /** Returns the allocator associated with this object. - - If a class derived from @ref operation_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 boost::empty_value< - allocator_type, 0>::get(); - } - - /** Returns the allocator associated with this object. - - If a class derived from @ref operation_base is a completion - handler, then the object returned from this function will be used - as the associated allocator of the derived class. - */ - executor_type - get_executor() const noexcept - { - return boost::empty_value< - executor_type, 1>::get(); - } - -protected: - Handler handler_; - - template< - class DeducedHandler -#if BOOST_BEAST_DOXYGEN - ,class = typename std::enable_if< - ! std::is_same::type, - operation_base - >::value>::type -#endif - > - operation_base( - DeducedHandler&& handler, - executor_type ex = executor_type{}, - allocator_type alloc = allocator_type{}) - : boost::empty_value( - boost::empty_init_t{}, alloc) - , boost::empty_value( - boost::empty_init_t{}, ex) - , handler_(std::forward(handler)) - { - } - -public: -#if ! BOOST_BEAST_DOXYGEN - template< - class Handler_, - class Executor_, - class Allocator_, - class Function> - friend - void asio_handler_invoke( - Function&& f, - operation_base< - Handler_, Executor_, Allocator_>* p); - - friend - void* asio_handler_allocate( - std::size_t size, operation_base* p) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(p->handler_)); - } - - friend - void asio_handler_deallocate( - void* mem, std::size_t size, - operation_base* p) - { - using net::asio_handler_deallocate; - asio_handler_deallocate(mem, size, - std::addressof(p->handler_)); - } - - friend - bool asio_handler_is_continuation( - operation_base* p) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(p->handler_)); - } -#endif -}; - -#if ! BOOST_BEAST_DOXYGEN - -template< - class Handler, - class Executor, - class Allocator, - class Function> -void asio_handler_invoke( - Function&& f, - operation_base< - Handler, Executor, Allocator>* p) -{ - using net::asio_handler_invoke; - asio_handler_invoke( - f, std::addressof(p->handler_)); -} - -#endif - -} // detail -} // beast -} // boost - -#endif diff --git a/include/boost/beast/core/detail/read.hpp b/include/boost/beast/core/detail/read.hpp index eb16221a..fe092e65 100644 --- a/include/boost/beast/core/detail/read.hpp +++ b/include/boost/beast/core/detail/read.hpp @@ -11,7 +11,10 @@ #define BOOST_BEAST_DETAIL_READ_HPP #include -#include +#include +#include +#include +#include namespace boost { namespace beast { diff --git a/include/boost/beast/core/impl/basic_stream_socket.hpp b/include/boost/beast/core/impl/basic_stream_socket.hpp index 2b95369a..4ddf6d85 100644 --- a/include/boost/beast/core/impl/basic_stream_socket.hpp +++ b/include/boost/beast/core/impl/basic_stream_socket.hpp @@ -11,10 +11,9 @@ #define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_SOCKET_HPP #include -#include +#include #include #include -#include #include #include #include @@ -24,6 +23,64 @@ namespace boost { namespace beast { +template +struct basic_stream_socket< + Protocol, Executor>::read_timeout_handler +{ + std::shared_ptr impl; + + void + operator()(error_code ec) + { + // timer canceled + if(ec == net::error::operation_aborted) + return; + + BOOST_ASSERT(! ec); + + if(! impl->read_closed) + { + // timeout + impl->close(); + impl->read_closed = true; + } + else + { + // late completion + impl->read_closed = false; + } + } +}; + +template +struct basic_stream_socket< + Protocol, Executor>::write_timeout_handler +{ + std::shared_ptr impl; + + void + operator()(error_code ec) + { + // timer canceled + if(ec == net::error::operation_aborted) + return; + + BOOST_ASSERT(! ec); + + if(! impl->write_closed) + { + // timeout + impl->close(); + impl->write_closed = true; + } + else + { + // late completion + impl->write_closed = false; + } + } +}; + /* The algorithm for implementing the timeout depends on the executor providing ordered execution guarantee. @@ -35,87 +92,53 @@ namespace beast { template template class basic_stream_socket::read_op - : public detail::operation_base< - Handler, Executor> + : public detail::async_op_base , public boost::asio::coroutine { - basic_stream_socket& s_; - net::executor_work_guard wg0_; - net::executor_work_guard wg1_; + typename basic_stream_socket< + Protocol, Executor>::impl_type& impl_; pending_guard pg_; Buffers b_; - struct timeout_handler - { - std::shared_ptr impl; - - void - operator()(error_code ec) - { - // timer canceled - if(ec == net::error::operation_aborted) - return; - - BOOST_ASSERT(! ec); - - if(! impl->read_closed) - { - // timeout - impl->close(); - impl->read_closed = true; - } - else - { - // late completion - impl->read_closed = false; - } - } - }; - public: template read_op( basic_stream_socket& s, Buffers const& b, Handler_&& h) - : detail::operation_base< - Handler, Executor>( - std::forward(h), - net::get_associated_executor( - h, s.get_executor())) - , s_(s) - , wg0_(s_.get_executor()) - , wg1_(net::get_associated_executor( - this->handler_, s_.get_executor())) - , pg_(s_.impl_->read_pending) + : detail::async_op_base( + s.get_executor(), std::forward(h)) + , impl_(*s.impl_) + , pg_(impl_.read_pending) , b_(b) { - (*this)(); + (*this)({}); } void operator()( - error_code ec = {}, + error_code ec, std::size_t bytes_transferred = 0) { BOOST_ASIO_CORO_REENTER(*this) { // must come first // VFALCO TODO what about the handler's allocator? - s_.impl_->read_timer.async_wait( + impl_.read_timer.async_wait( net::bind_executor( this->get_executor(), - timeout_handler{s_.impl_})); + read_timeout_handler{ + impl_.shared_from_this()})); - s_.impl_->maybe_kick(); + impl_.maybe_kick(); // check if the balance is zero - if(s_.impl_->read_remain == 0) + if(impl_.read_remain == 0) { // wait until the next time slice - ++s_.impl_->waiting; + ++impl_.waiting; BOOST_ASIO_CORO_YIELD - s_.impl_->rate_timer.async_wait(std::move(*this)); + impl_.rate_timer.async_wait(std::move(*this)); if(ec) { @@ -126,46 +149,46 @@ public: } // must call this - s_.impl_->on_timer(); - BOOST_ASSERT(s_.impl_->read_remain > 0); + impl_.on_timer(); + BOOST_ASSERT(impl_.read_remain > 0); } // we always use buffers_prefix, // to reduce template instantiations. - BOOST_ASSERT(s_.impl_->read_remain > 0); + BOOST_ASSERT(impl_.read_remain > 0); BOOST_ASIO_CORO_YIELD - s_.impl_->socket.async_read_some( + impl_.socket.async_read_some( beast::buffers_prefix( - s_.impl_->read_remain, b_), + impl_.read_remain, b_), std::move(*this)); - if(s_.impl_->read_remain != no_limit) + if(impl_.read_remain != no_limit) { // adjust balance BOOST_ASSERT( - bytes_transferred <= s_.impl_->read_remain); - s_.impl_->read_remain -= bytes_transferred; + bytes_transferred <= impl_.read_remain); + impl_.read_remain -= bytes_transferred; } { // try cancelling timer auto const n = - s_.impl_->read_timer.cancel(); + impl_.read_timer.cancel(); - if(s_.impl_->read_closed) + if(impl_.read_closed) { // timeout handler already invoked BOOST_ASSERT(n == 0); ec = beast::error::timeout; - s_.impl_->read_closed = false; + impl_.read_closed = false; } else if(n == 0) { // timeout handler already queued ec = beast::error::timeout; - s_.impl_->close(); - s_.impl_->read_closed = true; + impl_.close(); + impl_.read_closed = true; } else { @@ -176,7 +199,7 @@ public: upcall: pg_.reset(); - this->handler_(ec, bytes_transferred); + this->invoke(ec, bytes_transferred); } } }; @@ -186,59 +209,24 @@ public: template template class basic_stream_socket::write_op - : public detail::operation_base< - Handler, Executor> + : public detail::async_op_base , public boost::asio::coroutine { - basic_stream_socket& s_; - net::executor_work_guard wg0_; - net::executor_work_guard wg1_; + typename basic_stream_socket< + Protocol, Executor>::impl_type& impl_; pending_guard pg_; Buffers b_; - struct timeout_handler - { - std::shared_ptr impl; - - void - operator()(error_code ec) - { - // timer canceled - if(ec == net::error::operation_aborted) - return; - - BOOST_ASSERT(! ec); - - if(! impl->write_closed) - { - // timeout - impl->close(); - impl->write_closed = true; - } - else - { - // late completion - impl->write_closed = false; - } - } - }; - public: template write_op( basic_stream_socket& s, Buffers const& b, Handler_&& h) - : detail::operation_base< - Handler, Executor>( - std::forward(h), - net::get_associated_executor( - h, s.get_executor())) - , s_(s) - , wg0_(s_.get_executor()) - , wg1_(net::get_associated_executor( - this->handler_, s_.get_executor())) - , pg_(s_.impl_->write_pending) + : detail::async_op_base( + s.get_executor(), std::forward(h)) + , impl_(*s.impl_) + , pg_(impl_.write_pending) , b_(b) { (*this)(); @@ -253,20 +241,21 @@ public: { // must come first // VFALCO TODO what about the handler's allocator? - s_.impl_->write_timer.async_wait( + impl_.write_timer.async_wait( net::bind_executor( this->get_executor(), - timeout_handler{s_.impl_})); + write_timeout_handler{ + impl_.shared_from_this()})); - s_.impl_->maybe_kick(); + impl_.maybe_kick(); // check if the balance is zero - if(s_.impl_->write_remain == 0) + if(impl_.write_remain == 0) { // wait until the next time slice - ++s_.impl_->waiting; + ++impl_.waiting; BOOST_ASIO_CORO_YIELD - s_.impl_->rate_timer.async_wait(std::move(*this)); + impl_.rate_timer.async_wait(std::move(*this)); if(ec) { @@ -277,46 +266,46 @@ public: } // must call this - s_.impl_->on_timer(); - BOOST_ASSERT(s_.impl_->write_remain > 0); + impl_.on_timer(); + BOOST_ASSERT(impl_.write_remain > 0); } // we always use buffers_prefix, // to reduce template instantiations. - BOOST_ASSERT(s_.impl_->write_remain > 0); + BOOST_ASSERT(impl_.write_remain > 0); BOOST_ASIO_CORO_YIELD - s_.impl_->socket.async_write_some( + impl_.socket.async_write_some( beast::buffers_prefix( - s_.impl_->write_remain, b_), + impl_.write_remain, b_), std::move(*this)); - if(s_.impl_->write_remain != no_limit) + if(impl_.write_remain != no_limit) { // adjust balance BOOST_ASSERT( - bytes_transferred <= s_.impl_->write_remain); - s_.impl_->write_remain -= bytes_transferred; + bytes_transferred <= impl_.write_remain); + impl_.write_remain -= bytes_transferred; } { // try cancelling timer auto const n = - s_.impl_->write_timer.cancel(); + impl_.write_timer.cancel(); - if(s_.impl_->write_closed) + if(impl_.write_closed) { // timeout handler already invoked BOOST_ASSERT(n == 0); ec = beast::error::timeout; - s_.impl_->write_closed = false; + impl_.write_closed = false; } else if(n == 0) { // timeout handler already queued ec = beast::error::timeout; - s_.impl_->close(); - s_.impl_->write_closed = true; + impl_.close(); + impl_.write_closed = true; } else { @@ -327,7 +316,7 @@ public: upcall: pg_.reset(); - this->handler_(ec, bytes_transferred); + this->invoke(ec, bytes_transferred); } } }; @@ -339,46 +328,18 @@ namespace detail { template< class Protocol, class Executor, class Handler> class stream_socket_connect_op - : public detail::operation_base< - Handler, Executor> + : public detail::async_op_base { using stream_type = beast::basic_stream_socket; - stream_type& s_; - net::executor_work_guard wg0_; - net::executor_work_guard wg1_; + using timeout_handler = + typename stream_type::write_timeout_handler; + + typename basic_stream_socket< + Protocol, Executor>::impl_type& impl_; typename stream_type::pending_guard pg0_; typename stream_type::pending_guard pg1_; - struct timeout_handler - { - std::shared_ptr impl; - - void - operator()(error_code ec) - { - // timer canceled - if(ec == net::error::operation_aborted) - return; - - BOOST_ASSERT(! ec); - - if(! impl->write_closed) - { - // timeout - impl->close(); - impl->write_closed = true; - } - else - { - // late completion - impl->write_closed = false; - } - } - }; - public: template< class Endpoints, class Condition, @@ -388,26 +349,21 @@ public: Endpoints const& eps, Condition cond, Handler_&& h) - : detail::operation_base< - Handler, Executor>( - std::forward(h), - net::get_associated_executor( - h, s.get_executor())) - , s_(s) - , wg0_(s_.get_executor()) - , wg1_(net::get_associated_executor( - this->handler_, s_.get_executor())) - , pg0_(s_.impl_->read_pending) - , pg1_(s_.impl_->write_pending) + : detail::async_op_base( + s.get_executor(), std::forward(h)) + , impl_(*s.impl_) + , pg0_(impl_.read_pending) + , pg1_(impl_.write_pending) { // must come first // VFALCO TODO what about the handler's allocator? - s_.impl_->write_timer.async_wait( + impl_.write_timer.async_wait( net::bind_executor( this->get_executor(), - timeout_handler{s_.impl_})); + timeout_handler{ + impl_.shared_from_this()})); - net::async_connect(s_.impl_->socket, + net::async_connect(impl_.socket, eps, cond, std::move(*this)); // *this is now moved-from } @@ -420,25 +376,20 @@ public: Iterator begin, Iterator end, Condition cond, Handler_&& h) - : detail::operation_base< - Handler, Executor>( - std::forward(h), - net::get_associated_executor( - h, s.get_executor())) - , s_(s) - , wg0_(s_.get_executor()) - , wg1_(net::get_associated_executor( - this->handler_, s_.get_executor())) - , pg0_(s_.impl_->read_pending) - , pg1_(s_.impl_->write_pending) + : detail::async_op_base( + s.get_executor(), std::forward(h)) + , impl_(*s.impl_) + , pg0_(impl_.read_pending) + , pg1_(impl_.write_pending) { // must come first - s_.impl_->write_timer.async_wait( + impl_.write_timer.async_wait( net::bind_executor( this->get_executor(), - timeout_handler{s_.impl_})); + timeout_handler{ + impl_.shared_from_this()})); - net::async_connect(s_.impl_->socket, + net::async_connect(impl_.socket, begin, end, cond, std::move(*this)); // *this is now moved-from } @@ -449,22 +400,22 @@ public: { // try to cancel the timer auto const n = - s_.impl_->write_timer.cancel(); + impl_.write_timer.cancel(); - if(s_.impl_->write_closed) + if(impl_.write_closed) { // timeout handler already invoked BOOST_ASSERT(n == 0); ec = beast::error::timeout; - s_.impl_->write_closed = false; + impl_.write_closed = false; } else if(n == 0) { // timeout handler already queued ec = beast::error::timeout; - s_.impl_->close(); - s_.impl_->write_closed = true; + impl_.close(); + impl_.write_closed = true; } else { @@ -474,7 +425,7 @@ public: pg0_.reset(); pg1_.reset(); - this->handler_(ec, std::forward(arg)); + this->invoke(ec, std::forward(arg)); } }; diff --git a/include/boost/beast/core/impl/buffers_suffix.hpp b/include/boost/beast/core/impl/buffers_suffix.hpp index 72df82e1..af26388a 100644 --- a/include/boost/beast/core/impl/buffers_suffix.hpp +++ b/include/boost/beast/core/impl/buffers_suffix.hpp @@ -69,15 +69,9 @@ public: reference operator*() const { -#if 0 if(it_ == b_->begin_) return value_type{*it_} + b_->skip_; return value_type{*it_}; -#else - return it_ == b_->begin_ - ? value_type{*it_} + b_->skip_ - : *it_; -#endif } pointer diff --git a/include/boost/beast/http/impl/write.ipp b/include/boost/beast/http/impl/write.ipp index a08be699..ac188bd9 100644 --- a/include/boost/beast/http/impl/write.ipp +++ b/include/boost/beast/http/impl/write.ipp @@ -13,16 +13,11 @@ #include #include #include -#include #include #include -#include -#include -#include +#include +#include #include -#include -#include -#include #include #include #include @@ -39,12 +34,11 @@ template< class Stream, class Handler, bool isRequest, class Body, class Fields> class write_some_op + : public beast::detail::async_op_base< + Handler, beast::detail::get_executor_type> { Stream& s_; - net::executor_work_guard().get_executor())> wg_; serializer& sr_; - Handler h_; class lambda { @@ -61,122 +55,73 @@ class write_some_op template void - operator()(error_code& ec, + operator()( + error_code& ec, ConstBufferSequence const& buffers) { invoked = true; - ec.assign(0, ec.category()); - return op_.s_.async_write_some( + ec = {}; + op_.s_.async_write_some( buffers, std::move(op_)); } }; public: - write_some_op(write_some_op&&) = default; - write_some_op(write_some_op const&) = delete; - - template - write_some_op(DeducedHandler&& h, Stream& s, - serializer& sr) - : s_(s) - , wg_(s_.get_executor()) + template + write_some_op( + Handler_&& h, + Stream& s, + serializer& sr) + : beast::detail::async_op_base< + Handler, beast::detail::get_executor_type>( + s.get_executor(), std::forward(h)) + , s_(s) , sr_(sr) - , h_(std::forward(h)) { - } - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (net::get_associated_allocator)(h_); - } - - using executor_type = net::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (net::get_associated_executor)( - h_, s_.get_executor()); + (*this)(); } void - operator()(); + operator()() + { + error_code ec; + if(! sr_.is_done()) + { + lambda f{*this}; + sr_.next(ec, f); + if(ec) + { + BOOST_ASSERT(! f.invoked); + return net::post( + s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + } + if(f.invoked) + { + // *this is now moved-from, + return; + } + // What else could it be? + BOOST_ASSERT(sr_.is_done()); + } + return net::post( + s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + } void operator()( error_code ec, - std::size_t bytes_transferred); - - friend - bool asio_handler_is_continuation(write_some_op* op) + std::size_t bytes_transferred) { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, write_some_op* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); + if(! ec) + sr_.consume(bytes_transferred); + this->invoke(ec, bytes_transferred); } }; -template< - class Stream, class Handler, - bool isRequest, class Body, class Fields> -void -write_some_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()() -{ - error_code ec; - if(! sr_.is_done()) - { - lambda f{*this}; - sr_.next(ec, f); - if(ec) - { - BOOST_ASSERT(! f.invoked); - return net::post( - s_.get_executor(), - beast::bind_front_handler(std::move(*this), ec, 0)); - } - if(f.invoked) - { - // *this has been moved from, - // cannot access members here. - return; - } - // What else could it be? - BOOST_ASSERT(sr_.is_done()); - } - return net::post( - s_.get_executor(), - beast::bind_front_handler(std::move(*this), ec, 0)); -} - -template< - class Stream, class Handler, - bool isRequest, class Body, class Fields> -void -write_some_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()( - error_code ec, std::size_t bytes_transferred) -{ - if(! ec) - sr_.consume(bytes_transferred); - h_(ec, bytes_transferred); -} - //------------------------------------------------------------------------------ struct serializer_is_header_done @@ -208,220 +153,107 @@ struct serializer_is_done template< class Stream, class Handler, class Predicate, bool isRequest, class Body, class Fields> -class write_op : public net::coroutine +class write_op + : public beast::detail::async_op_base< + Handler, beast::detail::get_executor_type> + , public net::coroutine { Stream& s_; - net::executor_work_guard().get_executor())> wg_; serializer& sr_; std::size_t bytes_transferred_ = 0; - Handler h_; - bool cont_; public: - write_op(write_op&&) = default; - write_op(write_op const&) = delete; - - template - write_op(DeducedHandler&& h, Stream& s, - serializer& sr) - : s_(s) - , wg_(s_.get_executor()) + template + write_op( + Handler_&& h, + Stream& s, + serializer& sr) + : beast::detail::async_op_base< + Handler, beast::detail::get_executor_type>( + s.get_executor(), std::forward(h)) + , s_(s) , sr_(sr) - , h_(std::forward(h)) - , cont_([&] - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(h_)); - }()) { - } - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (net::get_associated_allocator)(h_); - } - - using executor_type = net::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (net::get_associated_executor)( - h_, s_.get_executor()); + (*this)(); } void operator()( error_code ec = {}, - std::size_t bytes_transferred = 0); - - friend - bool asio_handler_is_continuation(write_op* op) + std::size_t bytes_transferred = 0) { - return op->cont_; - } - - template - friend - void asio_handler_invoke(Function&& f, write_op* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); + BOOST_ASIO_CORO_REENTER(*this) + { + if(Predicate{}(sr_)) + { + BOOST_ASIO_CORO_YIELD + net::post( + s_.get_executor(), + std::move(*this)); + goto upcall; + } + for(;;) + { + BOOST_ASIO_CORO_YIELD + beast::http::async_write_some( + s_, sr_, std::move(*this)); + bytes_transferred_ += bytes_transferred; + if(ec) + goto upcall; + if(Predicate{}(sr_)) + break; + } + upcall: + this->invoke(ec, bytes_transferred_); + } } }; -template< - class Stream, class Handler, class Predicate, - bool isRequest, class Body, class Fields> -void -write_op:: -operator()( - error_code ec, - std::size_t bytes_transferred) -{ - BOOST_ASIO_CORO_REENTER(*this) - { - if(Predicate{}(sr_)) - { - BOOST_ASIO_CORO_YIELD - net::post( - s_.get_executor(), - std::move(*this)); - goto upcall; - } - for(;;) - { - BOOST_ASIO_CORO_YIELD - beast::http::async_write_some( - s_, sr_, std::move(*this)); - bytes_transferred_ += bytes_transferred; - if(ec) - goto upcall; - if(Predicate{}(sr_)) - break; - cont_ = true; - } - upcall: - h_(ec, bytes_transferred_); - } -} - //------------------------------------------------------------------------------ -template class write_msg_op + : public beast::detail::stable_async_op_base< + Handler, beast::detail::get_executor_type> { - struct data - { - Stream& s; - net::executor_work_guard().get_executor())> wg; - serializer sr; - - data(Handler const&, Stream& s_, message< - isRequest, Body, Fields>& m_) - : s(s_) - , wg(s.get_executor()) - , sr(m_) - { - } - - data(Handler const&, Stream& s_, message< - isRequest, Body, Fields> const& m_) - : s(s_) - , wg(s.get_executor()) - , sr(m_) - { - } - }; - - handler_ptr d_; + Stream& s_; + serializer& sr_; public: - write_msg_op(write_msg_op&&) = default; - write_msg_op(write_msg_op const&) = delete; - - template - write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args) - : d_(std::forward(h), - s, std::forward(args)...) + template< + class Handler_, + class... Args> + write_msg_op( + Stream& s, + Handler_&& h, + Args&&... args) + : beast::detail::stable_async_op_base< + Handler, beast::detail::get_executor_type>( + s.get_executor(), std::forward(h)) + , s_(s) + , sr_(beast::detail::allocate_stable< + serializer>( + *this, std::forward(args)...)) { - } - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (net::get_associated_allocator)(d_.handler()); - } - - using executor_type = net::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (net::get_associated_executor)( - d_.handler(), d_->s.get_executor()); + (*this)(); } void - operator()(); + operator()() + { + async_write(s_, sr_, std::move(*this)); + } void operator()( - error_code ec, std::size_t bytes_transferred); - - friend - bool asio_handler_is_continuation(write_msg_op* op) + error_code ec, std::size_t bytes_transferred) { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->d_.handler())); - } - - template - friend - void asio_handler_invoke(Function&& f, write_msg_op* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->d_.handler())); + this->invoke(ec, bytes_transferred); } }; -template -void -write_msg_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()() -{ - auto& d = *d_; - return async_write(d.s, d.sr, std::move(*this)); -} - -template -void -write_msg_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()(error_code ec, std::size_t bytes_transferred) -{ - auto wg = std::move(d_->wg); - d_.invoke(ec, bytes_transferred); -} - //------------------------------------------------------------------------------ template @@ -516,8 +348,8 @@ async_write_some_impl( AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); + isRequest, Body, Fields>( + std::move(init.completion_handler), stream, sr); return init.result.get(); } @@ -675,7 +507,7 @@ async_write_header( void(error_code, std::size_t)), detail::serializer_is_header_done, isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); + std::move(init.completion_handler), stream, sr}; return init.result.get(); } @@ -751,7 +583,7 @@ async_write( void(error_code, std::size_t)), detail::serializer_is_done, isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); + std::move(init.completion_handler), stream, sr}; return init.result.get(); } @@ -873,8 +705,8 @@ async_write( AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, msg}(); + isRequest, Body, Fields>(stream, + std::move(init.completion_handler), msg); return init.result.get(); } @@ -904,8 +736,8 @@ async_write( AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, msg}(); + isRequest, Body, Fields>(stream, + std::move(init.completion_handler), msg); return init.result.get(); } diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt index 88804079..0bedd480 100644 --- a/test/beast/core/CMakeLists.txt +++ b/test/beast/core/CMakeLists.txt @@ -16,10 +16,10 @@ add_executable (tests-beast-core ${EXTRAS_FILES} ${TEST_MAIN} Jamfile + _detail_async_op_base.cpp _detail_base64.cpp _detail_buffer.cpp _detail_clamp.cpp - _detail_operation_base.cpp _detail_read.cpp _detail_sha1.cpp _detail_tuple.cpp diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile index 60ab672e..89bbca5c 100644 --- a/test/beast/core/Jamfile +++ b/test/beast/core/Jamfile @@ -8,10 +8,10 @@ # local SOURCES = + _detail_async_op_base.cpp _detail_base64.cpp _detail_buffer.cpp _detail_clamp.cpp - _detail_operation_base.cpp _detail_read.cpp _detail_sha1.cpp _detail_tuple.cpp diff --git a/test/beast/core/_detail_async_op_base.cpp b/test/beast/core/_detail_async_op_base.cpp new file mode 100644 index 00000000..d39cae1f --- /dev/null +++ b/test/beast/core/_detail_async_op_base.cpp @@ -0,0 +1,569 @@ +// +// Copyright (c) 2016-2017 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 +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ + +namespace boost { +namespace beast { +namespace detail { + +//namespace { + +struct ex1_type +{ + void* context() { return nullptr; } + void on_work_started() {} + void on_work_finished() {} + template void dispatch(F&&) {} + template void post(F&&) {} + template void defer(F&&) {} +}; + +struct no_alloc +{ +}; + +struct nested_alloc +{ + struct allocator_type + { + }; +}; + +struct intrusive_alloc +{ + struct allocator_type + { + }; +}; + +struct no_ex +{ + using executor_type = net::system_executor; +}; + +struct nested_ex +{ + struct executor_type + { + }; +}; + +struct intrusive_ex +{ + struct executor_type + { + }; +}; + +template +struct handler; + +template<> +struct handler +{ +}; + +template<> +struct handler + : nested_alloc +{ +}; + +template<> +struct handler +{ +}; + +template<> +struct handler + : nested_ex +{ +}; + +template<> +struct handler +{ +}; + +struct legacy_handler +{ + bool invoked = false; + struct executor + { + void* context() { return nullptr; } + void on_work_started() {} + void on_work_finished() {} + template void dispatch(F&&) {} + template void post(F&&) {} + template void defer(F&&) {} + }; + + executor + get_executor() const noexcept + { + return {}; + }; +}; + +template +void +asio_handler_invoke( + Function&& f, + legacy_handler* p) +{ + p->invoked = true; + f(); +} + +void* +asio_handler_allocate( + std::size_t, + legacy_handler* p) +{ + p->invoked = true; + return nullptr; +} + +void +asio_handler_deallocate( + void*, std::size_t, + legacy_handler* p) +{ + p->invoked = true; +} + +bool +asio_handler_is_continuation( + legacy_handler* p) +{ + p->invoked = true; + return false; +} + +//} // (anon) + +} // detail +} // beast +} // boost + +namespace boost { +namespace asio { + +template +struct associated_allocator< + boost::beast::detail::handler< + boost::beast::detail::no_ex, + boost::beast::detail::intrusive_alloc>, + Allocator> +{ + using type = + boost::beast::detail::intrusive_alloc::allocator_type; + + static type get( + boost::beast::detail::handler< + boost::beast::detail::no_ex, + boost::beast::detail::intrusive_alloc> const& h, + Allocator const& a = Allocator()) noexcept + { + return type{}; + } +}; + +template +struct associated_executor< + boost::beast::detail::handler< + boost::beast::detail::intrusive_ex, + boost::beast::detail::no_alloc>, + Executor> +{ + using type = + boost::beast::detail::intrusive_ex::executor_type; + + static type get( + boost::beast::detail::handler< + boost::beast::detail::intrusive_ex, + boost::beast::detail::no_alloc> const& h, + Executor const& a = Executor()) noexcept + { + return type{}; + } +}; + +template +struct associated_allocator< + boost::beast::detail::legacy_handler, Allocator> +{ + using type = std::allocator; + + static type get( + boost::beast::detail::legacy_handler const& h, + Allocator const& a = Allocator()) noexcept + { + return type{}; + } +}; + +template +struct associated_executor< + boost::beast::detail::legacy_handler, Executor> +{ + using type = typename + boost::beast::detail::legacy_handler::executor; + + static type get( + boost::beast::detail::legacy_handler const&, + Executor const& = Executor()) noexcept + { + return type{}; + } +}; + +} // asio +} // boost + +//------------------------------------------------------------------------------ + +namespace boost { +namespace beast { +namespace detail { + +class async_op_base_test : public beast::unit_test::suite +{ +public: + // no associated allocator + + BOOST_STATIC_ASSERT( + std::is_same< + std::allocator, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + std::allocator, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + std::allocator, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type>, + std::allocator // ignored + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + std::allocator, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator>, + std::allocator // ignored + >>::value); + + // nested associated allocator + + BOOST_STATIC_ASSERT( + std::is_same< + nested_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + nested_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator> // ignored + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + nested_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type>, + std::allocator // ignored + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + nested_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator>, // ignored + std::allocator // ignored + >>::value); + + // intrusive associated allocator + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator> // ignored + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type>, + std::allocator // ignored + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_alloc::allocator_type, + net::associated_allocator_t< + async_op_base< + handler, + net::io_context::executor_type, + std::allocator>, // ignored + std::allocator // ignored + >>::value); + + // no associated executor + + BOOST_STATIC_ASSERT( + std::is_same< + ex1_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + ex1_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type>, + net::system_executor // ignored + >>::value); + + // nested associated executor + + BOOST_STATIC_ASSERT( + std::is_same< + nested_ex::executor_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + nested_ex::executor_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type>, + net::system_executor // ignored + >>::value); + + // intrusive associated executor + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_ex::executor_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type> + >>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + intrusive_ex::executor_type, + net::associated_executor_t< + async_op_base< + handler, + ex1_type>, + net::system_executor // ignored + >>::value); + + struct test_op : async_op_base< + legacy_handler, ex1_type> + { + test_op() + : async_op_base< + legacy_handler, + ex1_type>( + ex1_type{}, legacy_handler{}) + { + + } + + bool invoked() const noexcept + { + return this->handler().invoked; + } + }; + + void + testLegacyHooks() + { + // asio_handler_invoke + { + test_op h; + BEAST_EXPECT(! h.invoked()); + bool invoked = false; + using net::asio_handler_invoke; + asio_handler_invoke( + [&invoked] + { + invoked =true; + }, &h); + BEAST_EXPECT(invoked); + BEAST_EXPECT(h.invoked()); + } + + // asio_handler_allocate + { + test_op h; + BEAST_EXPECT(! h.invoked()); + using net::asio_handler_allocate; + asio_handler_allocate(0, &h); + BEAST_EXPECT(h.invoked()); + } + + // asio_handler_deallocate + { + test_op h; + BEAST_EXPECT(! h.invoked()); + using net::asio_handler_deallocate; + asio_handler_deallocate(nullptr, 0, &h); + BEAST_EXPECT(h.invoked()); + } + + // asio_handler_deallocate + { + test_op h; + BEAST_EXPECT(! h.invoked()); + using net::asio_handler_is_continuation; + asio_handler_is_continuation(&h); + BEAST_EXPECT(h.invoked()); + } + } + + void + testSpecialMembers() + { + test_op h1; + test_op h2(std::move(h1)); + } + + //-------------------------------------------------------------------------- + +#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) + { + net::async_completion init{handler}; + + using base = async_op_base< + BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t)), + get_executor_type>; + + struct read_op : base + { + AsyncReadStream& stream_; + buffers_suffix buffers_; + + void operator()(error_code ec, std::size_t bytes_transferred) + { + + } + }; + + read_op(stream, buffers, std::forward(handler)); + return init.result.get(); + } +#endif + + void + testJavadocs() + { + } + + //-------------------------------------------------------------------------- + + void + run() override + { + testLegacyHooks(); + testSpecialMembers(); + testJavadocs(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,core,async_op_base); + +} // detail +} // beast +} // boost diff --git a/test/beast/core/_detail_operation_base.cpp b/test/beast/core/_detail_operation_base.cpp deleted file mode 100644 index e134852a..00000000 --- a/test/beast/core/_detail_operation_base.cpp +++ /dev/null @@ -1,500 +0,0 @@ -// -// Copyright (c) 2016-2017 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 -// - -// Test that header file is self-contained. -#include - -#include -#include - -//------------------------------------------------------------------------------ - -namespace boost { -namespace beast { -namespace detail { - -namespace { - -struct specialized_handler -{ - bool invoked = false; - struct executor - { - void* context() { return nullptr; } - void on_work_started() {} - void on_work_finished() {} - template void dispatch(F&&) {} - template void post(F&&) {} - template void defer(F&&) {} - }; -}; - -template -static -void -asio_handler_invoke( - Function&& f, - specialized_handler* p) -{ - p->invoked = true; - f(); -} - -static -void* -asio_handler_allocate( - std::size_t, - specialized_handler* p) -{ - p->invoked = true; - return nullptr; -} - -static -void -asio_handler_deallocate( - void*, std::size_t, - specialized_handler* p) -{ - p->invoked = true; -} - -static bool -asio_handler_is_continuation( - specialized_handler* p) -{ - p->invoked = true; - return false; -} - -} // - -} // detail -} // beast -} // boost - -//------------------------------------------------------------------------------ - -namespace boost { -namespace asio { - -template -struct associated_allocator< - boost::beast::detail::specialized_handler, Allocator> -{ - using type = std::allocator; - - static type get( - boost::beast::detail::specialized_handler const& h, - Allocator const& a = Allocator()) noexcept - { - return type{}; - } -}; - -template -struct associated_executor< - boost::beast::detail::specialized_handler, Executor> -{ - using type = typename - boost::beast::detail::specialized_handler::executor; - - static type get( - boost::beast::detail::specialized_handler const& h, - Executor const& ex = Executor()) noexcept - { - return type{}; - } -}; - -} // asio -} // boost - -//------------------------------------------------------------------------------ - -namespace boost { -namespace beast { -namespace detail { - -class operation_base_test : public beast::unit_test::suite -{ -public: - using default_alloc = std::allocator; - using default_exec = net::system_executor; - - struct U {}; - struct V {}; - - template - struct executor - { - void* context() { return nullptr; } - void on_work_started() {} - void on_work_finished() {} - template void dispatch(F&&) {} - template void post(F&&) {} - template void defer(F&&) {} - }; - - struct none - { - void operator()() const - { - } - }; - - struct with_alloc - { - using allocator_type = std::allocator; - }; - - struct with_exec - { - using executor_type = executor; - }; - - struct move_only - { - move_only() = default; - move_only(move_only&&) = default; - move_only(move_only const&) = delete; - void operator()() const{}; - }; - - template< - class H, - class E = default_exec, - class A = default_alloc> - using tested_base = - operation_base; - - struct movable_handler : tested_base - { - movable_handler() - : tested_base(move_only{}) - { - } - }; - - struct test_handler : - tested_base - { - test_handler() - : tested_base< - specialized_handler>( - specialized_handler{}) - { - } - - bool invoked() const noexcept - { - return this->handler_.invoked; - } - }; - - - // no nested allocator type - - BOOST_STATIC_ASSERT( - std::is_same< - default_alloc, - net::associated_allocator_t< - tested_base - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - default_alloc, - decltype(net::get_associated_allocator( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - default_alloc, - decltype(net::get_associated_allocator( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - decltype(net::get_associated_allocator(std::declval< - tested_base>>() - ))>::value); - - // shouldn't work due to net.ts limitations - BOOST_STATIC_ASSERT( - ! std::is_same< - std::allocator, - decltype(net::get_associated_allocator( - std::declval>(), - std::declval>() - ))>::value); - BOOST_STATIC_ASSERT( - std::is_same< - default_alloc, - decltype(net::get_associated_allocator( - std::declval>(), - std::declval>() - ))>::value); - - // nested allocator_type - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - net::associated_allocator_t< - tested_base - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - decltype(net::get_associated_allocator( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - decltype(net::get_associated_allocator( - std::declval>(), - std::declval>() - ))>::value); - - // specialization of associated_allocator - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - net::associated_allocator_t< - tested_base< - specialized_handler> - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - decltype(net::get_associated_allocator( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - std::allocator, - decltype(net::get_associated_allocator( - std::declval>(), - std::declval>() - ))>::value); - - // no nested executor type - - BOOST_STATIC_ASSERT( - std::is_same< - default_exec, - net::associated_executor_t< - tested_base - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - executor, - net::associated_executor_t< - tested_base> - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - default_exec, - decltype(net::get_associated_executor( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - executor, - decltype(net::get_associated_executor(std::declval< - tested_base>>() - ))>::value); - - // shouldn't work due to net.ts limitations - BOOST_STATIC_ASSERT( - ! std::is_same< - executor, - decltype(net::get_associated_executor( - std::declval>(), - std::declval>() - ))>::value); - BOOST_STATIC_ASSERT( - std::is_same< - default_exec, - decltype(net::get_associated_executor( - std::declval>(), - std::declval>() - ))>::value); - - // nested executor_type - - BOOST_STATIC_ASSERT( - std::is_same< - executor, - net::associated_executor_t< - tested_base - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - executor, - decltype(net::get_associated_executor( - std::declval>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - executor, - decltype(net::get_associated_executor( - std::declval>(), - std::declval>() - ))>::value); - - // specialization of associated_executor - - BOOST_STATIC_ASSERT( - std::is_same< - specialized_handler::executor, - net::associated_executor_t< - tested_base< - specialized_handler> - >>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - specialized_handler::executor, - decltype(net::get_associated_executor(std::declval< - tested_base< - specialized_handler>>() - ))>::value); - - BOOST_STATIC_ASSERT( - std::is_same< - specialized_handler::executor, - decltype(net::get_associated_executor(std::declval< - tested_base< - specialized_handler>>(), - std::declval>() - ))>::value); - - //-------------------------------------------------------------------------- - - template - struct wrapped_handler : operation_base< - Handler, net::associated_executor_t> - { - template - explicit wrapped_handler (Handler_&& handler) - : operation_base>( - std::forward(handler), net::get_associated_executor(handler)) - { - } - - template - void operator()(Args&&... args) - { - this->handler_(std::forward(args)...); - } - }; - - void - testJavadocs() - { - wrapped_handler{none{}}(); - } - - //-------------------------------------------------------------------------- - - void - testLegacyHooks() - { - // asio_handler_invoke - { - test_handler h; - BEAST_EXPECT(! h.invoked()); - bool invoked = false; - using net::asio_handler_invoke; - asio_handler_invoke( - [&invoked] - { - invoked =true; - }, &h); - BEAST_EXPECT(invoked); - BEAST_EXPECT(h.invoked()); - } - - // asio_handler_allocate - { - test_handler h; - BEAST_EXPECT(! h.invoked()); - using net::asio_handler_allocate; - asio_handler_allocate(0, &h); - BEAST_EXPECT(h.invoked()); - } - - // asio_handler_deallocate - { - test_handler h; - BEAST_EXPECT(! h.invoked()); - using net::asio_handler_deallocate; - asio_handler_deallocate(nullptr, 0, &h); - BEAST_EXPECT(h.invoked()); - } - - // asio_handler_deallocate - { - test_handler h; - BEAST_EXPECT(! h.invoked()); - using net::asio_handler_is_continuation; - asio_handler_is_continuation(&h); - BEAST_EXPECT(h.invoked()); - } - } - - void - testSpecialMembers() - { - { - test_handler h1; - test_handler h2(std::move(h1)); - test_handler h3(h2); - boost::ignore_unused(h3); - } - - { - movable_handler h1; - movable_handler h2(std::move(h1)); - boost::ignore_unused(h2); - } - } - - void - run() override - { - testJavadocs(); - testLegacyHooks(); - testSpecialMembers(); - } -}; - -BEAST_DEFINE_TESTSUITE(beast,core,operation_base); - -} // detail -} // beast -} // boost