From 96b2944f700c8f5f828490867182f109540b366f Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 11 Feb 2019 21:10:29 -0800 Subject: [PATCH] Add detail::bind_continuation --- CHANGELOG.md | 3 +- .../boost/beast/core/bind_continuation.hpp | 55 ++++++ .../beast/core/detail/remap_post_to_defer.hpp | 109 ++++++++++ test/beast/core/CMakeLists.txt | 1 + test/beast/core/Jamfile | 1 + test/beast/core/bind_continuation.cpp | 186 ++++++++++++++++++ test/beast/core/test_executor.hpp | 122 ++++++++++++ 7 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 include/boost/beast/core/bind_continuation.hpp create mode 100644 include/boost/beast/core/detail/remap_post_to_defer.hpp create mode 100644 test/beast/core/bind_continuation.cpp create mode 100644 test/beast/core/test_executor.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e35f4b..3e254460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 214: -* handler binders use the associated allocator +* Handler binders use the associated allocator +* Add detail::bind_continuation -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/bind_continuation.hpp b/include/boost/beast/core/bind_continuation.hpp new file mode 100644 index 00000000..ad91107e --- /dev/null +++ b/include/boost/beast/core/bind_continuation.hpp @@ -0,0 +1,55 @@ +// +// 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_BIND_CONTINUATION_HPP +#define BOOST_BEAST_BIND_CONTINUATION_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { + +/** Mark a completion handler as a continuation. + + This function wraps a completion handler to associate it with an + executor whose `post` operation is remapped to the `defer` operation. + It is used by composed asynchronous operation implementations to + indicate that a completion handler submitted to an initiating + function represents a continuation of the current asynchronous + flow of control. + + @see + + @li [N4242] Executors and Asynchronous Operations, Revision 1 +*/ +template +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +net::executor_binder::type, + detail::remap_post_to_defer> +#endif +bind_continuation( + Executor const& ex, CompletionHandler&& handler) +{ + return net::bind_executor( + detail::remap_post_to_defer(ex), + std::forward(handler)); +} + +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/remap_post_to_defer.hpp b/include/boost/beast/core/detail/remap_post_to_defer.hpp new file mode 100644 index 00000000..6accef84 --- /dev/null +++ b/include/boost/beast/core/detail/remap_post_to_defer.hpp @@ -0,0 +1,109 @@ +// +// Copyright (c) 2019 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_REMAP_POST_TO_DEFER_HPP +#define BOOST_BEAST_DETAIL_REMAP_POST_TO_DEFER_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace detail { + +template +class remap_post_to_defer + : private boost::empty_value +{ + BOOST_STATIC_ASSERT( + net::is_executor::value); + + Executor const& + ex() const noexcept + { + return this->get(); + } + +public: + remap_post_to_defer( + remap_post_to_defer&&) = default; + + remap_post_to_defer( + remap_post_to_defer const&) = default; + + explicit + remap_post_to_defer( + Executor const& ex) + : boost::empty_value( + boost::empty_init_t{}, ex) + { + } + + bool + operator==( + remap_post_to_defer const& other) const noexcept + { + return ex() == other.ex(); + } + + bool + operator!=( + remap_post_to_defer const& other) const noexcept + { + return ex() != other.ex(); + } + + decltype(std::declval().context()) + context() const noexcept + { + return ex().context(); + } + + void + on_work_started() const noexcept + { + ex().on_work_started(); + } + + void + on_work_finished() const noexcept + { + ex().on_work_finished(); + } + + template + void + dispatch(F&& f, A const& a) const + { + ex().dispatch(std::forward(f), a); + } + + template + void + post(F&& f, A const& a) const + { + ex().defer(std::forward(f), a); + } + + template + void + defer(F&& f, A const& a) const + { + ex().defer(std::forward(f), a); + } +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt index b22e7cde..b7223c12 100644 --- a/test/beast/core/CMakeLists.txt +++ b/test/beast/core/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable (tests-beast-core _detail_varint.cpp async_op_base.cpp basic_stream.cpp + bind_continuation.cpp bind_handler.cpp buffer_size.cpp buffer_traits.cpp diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile index 109328bb..33e7a201 100644 --- a/test/beast/core/Jamfile +++ b/test/beast/core/Jamfile @@ -19,6 +19,7 @@ local SOURCES = _detail_varint.cpp async_op_base.cpp basic_stream.cpp + bind_continuation.cpp bind_handler.cpp buffer_size.cpp buffer_traits.cpp diff --git a/test/beast/core/bind_continuation.cpp b/test/beast/core/bind_continuation.cpp new file mode 100644 index 00000000..f5ef983f --- /dev/null +++ b/test/beast/core/bind_continuation.cpp @@ -0,0 +1,186 @@ +// +// 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 +// + +// Test that header file is self-contained. +#include + +#include "test_executor.hpp" +#include "test_handler.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { + +class bind_continuation_test + : public beast::unit_test::suite +{ +public: + class handler + { + bool pass_ = false; + + public: + handler() = default; + + ~handler() + { + BEAST_EXPECT(pass_); + } + + handler(handler&& other) + : pass_(boost::exchange(other.pass_, true)) + { + } + + void operator()() + { + pass_ = true; + } + }; + + void + testBinder() + { + net::io_context ioc; + + // free functions + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + net::dispatch( + bind_continuation(ex, handler{})); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->dispatch == 1); + } + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + net::post( + bind_continuation(ex, handler{})); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->defer == 1); + } + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + net::defer( + bind_continuation(ex, handler{})); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->defer == 1); + } + + // members + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + ex.dispatch( + bind_continuation(ex, handler{}), + std::allocator{}); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->dispatch == 1); + } + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + ex.post( + bind_continuation(ex, handler{}), + std::allocator{}); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->post == 1); + } + + { + test_executor<> ex(ioc.get_executor()); + BEAST_EXPECT(ex->total == 0); + ex.defer( + bind_continuation(ex, handler{}), + std::allocator{}); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(ex->defer == 1); + } + + // relational + + { + auto h1 = bind_continuation( + ioc.get_executor(), handler{}); + auto h2 = bind_continuation( + ioc.get_executor(), handler{}); + BEAST_EXPECT( + net::get_associated_executor(h1) == + net::get_associated_executor(h2)); + BEAST_EXPECT( + std::addressof( + net::get_associated_executor(h1).context()) == + std::addressof( + net::get_associated_executor(h2).context())); + h1(); + h2(); + } + + { + net::io_context ioc1; + net::io_context ioc2; + auto h1 = bind_continuation( + ioc1.get_executor(), handler{}); + auto h2 = bind_continuation( + ioc2.get_executor(), handler{}); + BEAST_EXPECT( + net::get_associated_executor(h1) != + net::get_associated_executor(h2)); + BEAST_EXPECT( + std::addressof( + net::get_associated_executor(h1).context()) != + std::addressof( + net::get_associated_executor(h2).context())); + h1(); + h2(); + } + } + + //-------------------------------------------------------------------------- + + void + testJavadoc() + { + } + + //-------------------------------------------------------------------------- + + void + run() override + { + testBinder(); + testJavadoc(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,core,bind_continuation); + +} // beast +} // boost diff --git a/test/beast/core/test_executor.hpp b/test/beast/core/test_executor.hpp new file mode 100644 index 00000000..6c65c77f --- /dev/null +++ b/test/beast/core/test_executor.hpp @@ -0,0 +1,122 @@ +// +// 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_TEST_TEST_EXECUTOR_HPP +#define BOOST_BEAST_TEST_TEST_EXECUTOR_HPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { + +template +class test_executor +{ +public: + // VFALCO These need to be atomic or something + struct info + { + int dispatch = 0; + int post = 0; + int defer = 0; + int work = 0; + int total = 0; + }; + +private: + struct state + { + Executor ex; + info info_; + + state(Executor const& ex_) + : ex(ex_) + { + } + }; + + std::shared_ptr sp_; + +public: + test_executor(test_executor const&) = default; + test_executor& operator=(test_executor const&) = default; + + explicit + test_executor(Executor const& ex) + : sp_(std::make_shared(ex)) + { + } + + decltype(sp_->ex.context()) + context() const noexcept + { + return sp_->ex.context(); + } + + info& + operator*() noexcept + { + return sp_->info_; + } + + info* + operator->() noexcept + { + return &sp_->info_; + } + + void + on_work_started() const noexcept + { + ++sp_->info_.work; + } + + void + on_work_finished() const noexcept + { + } + + template + void + dispatch(F&& f, A const& a) const + { + ++sp_->info_.dispatch; + ++sp_->info_.total; + sp_->ex.dispatch( + std::forward(f), a); + } + + template + void + post(F&& f, A const& a) const + { + ++sp_->info_.post; + ++sp_->info_.total; + sp_->ex.post( + std::forward(f), a); + } + + template + void + defer(F&& f, A const& a) const + { + ++sp_->info_.defer; + ++sp_->info_.total; + sp_->ex.defer( + std::forward(f), a); + } +}; + +} // beast +} // boost + +#endif