From 04111c0bc3d652299d7098c5212e3510774d7677 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Sat, 10 Feb 2018 01:13:01 +0100 Subject: [PATCH] More work on porting the async traversal --- .../continuable-traverse-async.hpp | 2 +- include/continuable/detail/traverse-async.hpp | 99 ++++++++++--------- .../test-continuable-traverse-async.cpp | 95 +++++++++++++++--- 3 files changed, 134 insertions(+), 62 deletions(-) diff --git a/include/continuable/continuable-traverse-async.hpp b/include/continuable/continuable-traverse-async.hpp index 08364d5..550748f 100644 --- a/include/continuable/continuable-traverse-async.hpp +++ b/include/continuable/continuable-traverse-async.hpp @@ -105,7 +105,7 @@ using detail::traversal::async_traverse_in_place_tag; /// asynchronously. Nested objects inside containers and /// tuple like types are traversed recursively. /// -/// \returns A boost::intrusive_ptr that references an instance of +/// \returns A std::shared_ptr that references an instance of /// the given visitor object. /// /// \since 3.0.0 diff --git a/include/continuable/detail/traverse-async.hpp b/include/continuable/detail/traverse-async.hpp index 987e3c6..88f0e40 100644 --- a/include/continuable/detail/traverse-async.hpp +++ b/include/continuable/detail/traverse-async.hpp @@ -133,9 +133,7 @@ public: } /// We require a virtual base - ~async_traversal_frame() override { - assert(finished_.load()); - } + virtual ~async_traversal_frame() override = default; template explicit async_traversal_frame(async_traverse_in_place_tag, @@ -161,13 +159,14 @@ public: /// when it's called later. template void async_continue(T&& value, Hierarchy&& hierarchy) { - // Create a self reference - boost::intrusive_ptr self(this); + // Cast the frame up + auto frame = std::static_pointer_cast( + this->shared_from_this()); // Create a callable object which resumes the current // traversal when it's called. auto resumable = make_resume_traversal_callable( - std::move(self), std::forward(hierarchy)); + std::move(frame), std::forward(hierarchy)); // Invoke the visitor with the current value and the // callable object to resume the control flow. @@ -197,12 +196,12 @@ struct static_async_range { } static_async_range(static_async_range const& rhs) = default; - static_async_range(static_async_range&& rhs) : target_(rhs.target_) { + static_async_range(static_async_range&& rhs) noexcept : target_(rhs.target_) { rhs.target_ = nullptr; } static_async_range& operator=(static_async_range const& rhs) = default; - static_async_range& operator=(static_async_range&& rhs) { + static_async_range& operator=(static_async_range&& rhs) noexcept { if (&rhs != this) { target_ = rhs.target_; rhs.target_ = nullptr; @@ -210,18 +209,16 @@ struct static_async_range { return *this; } - constexpr auto operator*() const noexcept - -> decltype(std::get(*target_)) { + constexpr decltype(auto) operator*() const noexcept { return std::get(*target_); } template - constexpr static_async_range relocate() const - noexcept { + constexpr auto relocate() const noexcept { return static_async_range{target_}; } - constexpr static_async_range next() const noexcept { + constexpr auto next() const noexcept { return static_async_range{target_}; } @@ -243,12 +240,12 @@ struct static_async_range { }; /// Returns a static range for the given type -template ::type, 0U, - std::tuple_size::type>::value>> -Range make_static_range(T&& element) { - auto pointer = std::addressof(element); - return Range{pointer}; +template +auto make_static_range(T&& element) { + using range_t = static_async_range, 0U, + std::tuple_size>::value>; + + return range_t{std::addressof(element)}; } template @@ -282,9 +279,10 @@ using dynamic_async_range_of_t = dynamic_async_range< typename std::decay()))>::type>; /// Returns a dynamic range for the given type -template > -Range make_dynamic_async_range(T&& element) { - return Range{std::begin(element), std::end(element)}; +template +auto make_dynamic_async_range(T&& element) { + using range_t = dynamic_async_range_of_t; + return range_t{std::begin(element), std::end(element)}; } /// Represents a particular point in a asynchronous traversal hierarchy @@ -316,8 +314,7 @@ public: /// Creates a new traversal point which template auto push(Parent&& parent) - -> async_traversal_point::type, - Hierarchy...> { + -> async_traversal_point, Hierarchy...> { // Create a new hierarchy which contains the // the parent (the last traversed element). auto hierarchy = std::tuple_cat( @@ -413,6 +410,7 @@ public: current.template relocate()), 0)...}; (void)dummy; + (void)current; } /// Traverse a static range @@ -514,14 +512,11 @@ template struct async_traversal_types { /// Deduces to the async traversal frame type of the given /// traversal arguments and mapper - using frame_type = async_traversal_frame::type, - typename std::decay::type...>; - - /// The type of the frame pointer - using frame_pointer_type = boost::intrusive_ptr; + using frame_t = async_traversal_frame::type, + typename std::decay::type...>; /// The type of the demoted visitor type - using visitor_pointer_type = boost::intrusive_ptr; + using visitor_t = Visitor; }; template @@ -530,30 +525,40 @@ struct async_traversal_types, VisitorArg, : async_traversal_types {}; /// Traverses the given pack with the given mapper -template > -auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) -> - typename types::visitor_pointer_type { - // Create the frame on the heap which stores the arguments - // to traverse asynchronous. - auto frame = [&] { - auto ptr = new typename types::frame_type(std::forward(visitor), - std::forward(args)...); +template +auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) { - // Create an intrusive_ptr from the heap object, don't increase - // reference count (it's already 'one'). - return typename types::frame_pointer_type(ptr, false); - }(); + // Provide the frame and visitor type + using types = async_traversal_types; + + // Check whether the visitor inherits enable_shared_from_this + static_assert( + std::is_base_of, + typename types::visitor_t>::value, + "The visitor must inherit std::enable_shared_from_this!"); + + // Check whether the visitor is virtual destructible + static_assert(std::has_virtual_destructor::value, + "The visitor must have a virtual destructor!"); + + // Create the frame on the heap which stores the arguments + // to traverse asynchronous. It persists until the + // traversal frame isn't referenced anymore. + auto frame = std::make_shared(std::forward(visitor), + std::forward(args)...); // Create a static range for the top level tuple - auto range = make_static_range(frame->head()); + auto range = std::make_tuple(make_static_range(frame->head())); - auto resumer = - make_resume_traversal_callable(frame, std::make_tuple(std::move(range))); + // Create a resumer to start the asynchronous traversal + auto resumer = make_resume_traversal_callable(frame, std::move(range)); // Start the asynchronous traversal resumer(); - return frame; + + // Cast the shared_ptr down to the given visitor type + // for implementation invisibility + return std::static_pointer_cast(std::move(frame)); } } // namespace traversal } // namespace detail diff --git a/test/unit-test/test-continuable-traverse-async.cpp b/test/unit-test/test-continuable-traverse-async.cpp index e47a9ba..d321e9a 100644 --- a/test/unit-test/test-continuable-traverse-async.cpp +++ b/test/unit-test/test-continuable-traverse-async.cpp @@ -1,3 +1,70 @@ + +/* + Copyright(c) 2015 - 2018 Denis Blank + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "test-continuable.hpp" + +using std::get; +using std::make_tuple; +using std::tuple; + +using cti::async_traverse_complete_tag; +using cti::async_traverse_detach_tag; +using cti::async_traverse_visit_tag; +using cti::traverse_pack_async; + +struct my_visitor : std::enable_shared_from_this { + virtual ~my_visitor() = default; + + bool operator()(async_traverse_visit_tag, std::size_t i) { + (void)i; + return false; + } + + template + void operator()(async_traverse_detach_tag, std::size_t i, N&& next) { + (void)i; + next(); + } + + template + void operator()(async_traverse_complete_tag, T&& pack) { + (void)pack; + } +}; + +TEST(misc_as_test, my_test) { + cti::traverse_pack_async(my_visitor{}, 0, 1, 2); +} + // Copyright (c) 2017 Denis Blank // Copyright Andrey Semashev 2007 - 2013. // @@ -142,7 +209,7 @@ struct async_increasing_int_sync_visitor bool operator()(async_traverse_visit_tag, std::size_t i) { - HPX_TEST_EQ(i, this->counter()); + EXPECT_EQ(i, this->counter()); ++this->counter(); return true; } @@ -162,7 +229,7 @@ struct async_increasing_int_sync_visitor { HPX_UNUSED(pack); - HPX_TEST_EQ(this->counter(), ArgCount); + EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; @@ -175,7 +242,7 @@ struct async_increasing_int_visitor bool operator()(async_traverse_visit_tag, std::size_t i) const { - HPX_TEST_EQ(i, this->counter()); + EXPECT_EQ(i, this->counter()); return false; } @@ -193,7 +260,7 @@ struct async_increasing_int_visitor { HPX_UNUSED(pack); - HPX_TEST_EQ(this->counter(), ArgCount); + EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; @@ -208,7 +275,7 @@ void test_async_traversal_base(Args&&... args) hpx::util::async_traverse_in_place_tag< async_increasing_int_sync_visitor>{}, 42, args...); - HPX_TEST_EQ(result->counter(), ArgCount + 1U); + EXPECT_EQ(result->counter(), ArgCount + 1U); } // Test that every element is traversed in the correct order @@ -218,7 +285,7 @@ void test_async_traversal_base(Args&&... args) hpx::util::async_traverse_in_place_tag< async_increasing_int_visitor>{}, 42, args...); - HPX_TEST_EQ(result->counter(), ArgCount + 1U); + EXPECT_EQ(result->counter(), ArgCount + 1U); } } @@ -337,7 +404,7 @@ struct async_unique_sync_visitor bool operator()(async_traverse_visit_tag, std::unique_ptr& i) { - HPX_TEST_EQ(*i, this->counter()); + EXPECT_EQ(*i, this->counter()); ++this->counter(); return true; } @@ -359,7 +426,7 @@ struct async_unique_sync_visitor { HPX_UNUSED(pack); - HPX_TEST_EQ(this->counter(), ArgCount); + EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; @@ -372,7 +439,7 @@ struct async_unique_visitor : async_counter_base> bool operator()(async_traverse_visit_tag, std::unique_ptr& i) const { - HPX_TEST_EQ(*i, this->counter()); + EXPECT_EQ(*i, this->counter()); return false; } @@ -392,7 +459,7 @@ struct async_unique_visitor : async_counter_base> { HPX_UNUSED(pack); - HPX_TEST_EQ(this->counter(), ArgCount); + EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; @@ -408,7 +475,7 @@ static void test_async_move_only_traversal() hpx::util::async_traverse_in_place_tag< async_unique_sync_visitor<4>>{}, 42, of(0), of(1), of(2), of(3)); - HPX_TEST_EQ(result->counter(), 5U); + EXPECT_EQ(result->counter(), 5U); } { @@ -416,7 +483,7 @@ static void test_async_move_only_traversal() hpx::util::async_traverse_in_place_tag< async_unique_visitor<4>>{}, 42, of(0), of(1), of(2), of(3)); - HPX_TEST_EQ(result->counter(), 5U); + EXPECT_EQ(result->counter(), 5U); } } @@ -426,7 +493,7 @@ struct invalidate_visitor : async_counter_base bool operator()(async_traverse_visit_tag, std::shared_ptr& i) const { - HPX_TEST_EQ(*i, 22); + EXPECT_EQ(*i, 22); return false; } @@ -460,7 +527,7 @@ static void test_async_complete_invalidation() hpx::util::async_traverse_in_place_tag{}, 42, value); - HPX_TEST_EQ(value.use_count(), 1U); + EXPECT_EQ(value.use_count(), 1U); } int main(int, char**)