More work on porting the async traversal

This commit is contained in:
Denis Blank 2018-02-10 01:13:01 +01:00
parent 16dd2b0cb9
commit 04111c0bc3
3 changed files with 134 additions and 62 deletions

View File

@ -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

View File

@ -133,9 +133,7 @@ public:
}
/// We require a virtual base
~async_traversal_frame() override {
assert(finished_.load());
}
virtual ~async_traversal_frame() override = default;
template <typename MapperArg>
explicit async_traversal_frame(async_traverse_in_place_tag<Visitor>,
@ -161,13 +159,14 @@ public:
/// when it's called later.
template <typename T, typename Hierarchy>
void async_continue(T&& value, Hierarchy&& hierarchy) {
// Create a self reference
boost::intrusive_ptr<async_traversal_frame> self(this);
// Cast the frame up
auto frame = std::static_pointer_cast<async_traversal_frame>(
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>(hierarchy));
std::move(frame), std::forward<Hierarchy>(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<Begin>(*target_)) {
constexpr decltype(auto) operator*() const noexcept {
return std::get<Begin>(*target_);
}
template <std::size_t Position>
constexpr static_async_range<Target, Position, End> relocate() const
noexcept {
constexpr auto relocate() const noexcept {
return static_async_range<Target, Position, End>{target_};
}
constexpr static_async_range<Target, Begin + 1, End> next() const noexcept {
constexpr auto next() const noexcept {
return static_async_range<Target, Begin + 1, End>{target_};
}
@ -243,12 +240,12 @@ struct static_async_range<Target, Begin, Begin> {
};
/// Returns a static range for the given type
template <typename T, typename Range = static_async_range<
typename std::decay<T>::type, 0U,
std::tuple_size<typename std::decay<T>::type>::value>>
Range make_static_range(T&& element) {
auto pointer = std::addressof(element);
return Range{pointer};
template <typename T>
auto make_static_range(T&& element) {
using range_t = static_async_range<std::decay_t<T>, 0U,
std::tuple_size<std::decay_t<T>>::value>;
return range_t{std::addressof(element)};
}
template <typename Begin, typename Sentinel>
@ -282,9 +279,10 @@ using dynamic_async_range_of_t = dynamic_async_range<
typename std::decay<decltype(std::end(std::declval<T>()))>::type>;
/// Returns a dynamic range for the given type
template <typename T, typename Range = dynamic_async_range_of_t<T>>
Range make_dynamic_async_range(T&& element) {
return Range{std::begin(element), std::end(element)};
template <typename T>
auto make_dynamic_async_range(T&& element) {
using range_t = dynamic_async_range_of_t<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 <typename Parent>
auto push(Parent&& parent)
-> async_traversal_point<Frame, typename std::decay<Parent>::type,
Hierarchy...> {
-> async_traversal_point<Frame, std::decay_t<Parent>, 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<Sequence>()),
0)...};
(void)dummy;
(void)current;
}
/// Traverse a static range
@ -514,14 +512,11 @@ template <typename Visitor, typename... Args>
struct async_traversal_types {
/// Deduces to the async traversal frame type of the given
/// traversal arguments and mapper
using frame_type = async_traversal_frame<typename std::decay<Visitor>::type,
typename std::decay<Args>::type...>;
/// The type of the frame pointer
using frame_pointer_type = boost::intrusive_ptr<frame_type>;
using frame_t = async_traversal_frame<typename std::decay<Visitor>::type,
typename std::decay<Args>::type...>;
/// The type of the demoted visitor type
using visitor_pointer_type = boost::intrusive_ptr<Visitor>;
using visitor_t = Visitor;
};
template <typename Visitor, typename VisitorArg, typename... Args>
@ -530,30 +525,40 @@ struct async_traversal_types<async_traverse_in_place_tag<Visitor>, VisitorArg,
: async_traversal_types<Visitor, Args...> {};
/// Traverses the given pack with the given mapper
template <typename Visitor, typename... Args,
typename types = async_traversal_types<Visitor, Args...>>
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>(visitor),
std::forward<Args>(args)...);
template <typename Visitor, typename... Args>
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<Visitor, Args...>;
// Check whether the visitor inherits enable_shared_from_this
static_assert(
std::is_base_of<std::enable_shared_from_this<typename types::visitor_t>,
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<typename types::visitor_t>::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<types::frame_t>(std::forward<Visitor>(visitor),
std::forward<Args>(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<types::visitor_t>(std::move(frame));
}
} // namespace traversal
} // namespace detail

View File

@ -1,3 +1,70 @@
/*
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
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 <array>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <continuable/continuable-traverse-async.hpp>
#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<my_visitor> {
virtual ~my_visitor() = default;
bool operator()(async_traverse_visit_tag, std::size_t i) {
(void)i;
return false;
}
template <typename N>
void operator()(async_traverse_detach_tag, std::size_t i, N&& next) {
(void)i;
next();
}
template <typename T>
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<ArgCount>>{},
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<ArgCount>>{},
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<std::size_t>& 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<async_unique_visitor<ArgCount>>
bool operator()(async_traverse_visit_tag,
std::unique_ptr<std::size_t>& 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<async_unique_visitor<ArgCount>>
{
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<invalidate_visitor>
bool operator()(async_traverse_visit_tag, std::shared_ptr<int>& 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<invalidate_visitor>{},
42, value);
HPX_TEST_EQ(value.use_count(), 1U);
EXPECT_EQ(value.use_count(), 1U);
}
int main(int, char**)