Const iterator works.

Reviewers: florijan, mislav.bradac

Reviewed By: mislav.bradac

Subscribers: buda, pullbot

Differential Revision: https://phabricator.memgraph.io/D886
This commit is contained in:
Dominik Gleich 2017-10-10 16:06:46 +02:00
parent f10380a861
commit cce6db3442
6 changed files with 125 additions and 48 deletions

View File

@ -75,5 +75,5 @@ class AccessorBase {
size_t size() const { return accessor.size(); } size_t size() const { return accessor.size(); }
protected: protected:
typename list::Accessor accessor; typename list::template Accessor<SkipList<T>> accessor;
}; };

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <memory> #include <memory>
#include <type_traits>
#include "utils/assert.hpp" #include "utils/assert.hpp"
#include "utils/crtp.hpp" #include "utils/crtp.hpp"
@ -451,10 +452,11 @@ class SkipList : private Lockable<lock_t> {
friend class Accessor; friend class Accessor;
template <typename TSkipList>
class Accessor { class Accessor {
friend class SkipList; friend class SkipList;
Accessor(SkipList *skiplist) Accessor(TSkipList *skiplist)
: skiplist(skiplist), status_(skiplist->gc.CreateNewAccessor()) { : skiplist(skiplist), status_(skiplist->gc.CreateNewAccessor()) {
debug_assert(skiplist != nullptr, "Skiplist is nullptr."); debug_assert(skiplist != nullptr, "Skiplist is nullptr.");
} }
@ -473,15 +475,37 @@ class SkipList : private Lockable<lock_t> {
status_.alive_ = false; status_.alive_ = false;
} }
Iterator begin() { return skiplist->begin(); } // TODO(dgleich): Remove after C++17 compile flag.
template <class B>
struct negation : std::integral_constant<bool, !bool(B::value)> {};
ConstIterator begin() const { return skiplist->cbegin(); } template <typename TIsConst = TSkipList>
Iterator begin(typename std::enable_if<
negation<std::is_const<TIsConst>>::value>::type * = 0) {
return skiplist->begin();
}
template <typename TIsConst = TSkipList>
ConstIterator begin(
typename std::enable_if<std::is_const<TIsConst>::value>::type * =
0) const {
return skiplist->cbegin();
}
ConstIterator cbegin() const { return skiplist->cbegin(); } ConstIterator cbegin() const { return skiplist->cbegin(); }
Iterator end() { return skiplist->end(); } template <typename TIsConst = TSkipList>
Iterator end(typename std::enable_if<
negation<std::is_const<TIsConst>>::value>::type * = 0) {
return skiplist->end();
}
ConstIterator end() const { return skiplist->cend(); } template <typename TIsConst = TSkipList>
ConstIterator end(
typename std::enable_if<std::is_const<TIsConst>::value>::type * =
0) const {
return skiplist->cend();
}
ConstIterator cend() const { return skiplist->cend(); } ConstIterator cend() const { return skiplist->cend(); }
@ -526,7 +550,7 @@ class SkipList : private Lockable<lock_t> {
*/ */
template <class TItem> template <class TItem>
Iterator find_or_larger(const TItem &item) { Iterator find_or_larger(const TItem &item) {
return skiplist->find_or_larger<Iterator, TItem>(item); return skiplist->template find_or_larger<Iterator, TItem>(item);
} }
/** /**
@ -669,14 +693,16 @@ class SkipList : private Lockable<lock_t> {
} }
private: private:
SkipList *skiplist; TSkipList *skiplist;
Node *preds[H], *succs[H]; Node *preds[H], *succs[H];
typename SkipListGC<Node>::AccessorStatus &status_; typename SkipListGC<Node>::AccessorStatus &status_;
}; };
Accessor access() { return Accessor(this); } Accessor<SkipList> access() { return Accessor<SkipList>(this); }
const Accessor access() const { return Accessor(this); } Accessor<const SkipList> caccess() const {
return Accessor<const SkipList>(this);
}
private: private:
using guard_t = std::unique_lock<lock_t>; using guard_t = std::unique_lock<lock_t>;
@ -1022,5 +1048,5 @@ class SkipList : private Lockable<lock_t> {
*/ */
std::atomic<size_t> count{0}; std::atomic<size_t> count{0};
Node *header; Node *header;
SkipListGC<Node> gc; mutable SkipListGC<Node> gc;
}; };

View File

@ -17,7 +17,7 @@ namespace IndexUtils {
* iterating, i.e. it allows us to iterate over the suffix of some skiplist * iterating, i.e. it allows us to iterate over the suffix of some skiplist
* hence the name SkipListSuffix. * hence the name SkipListSuffix.
*/ */
template <class TIterator, class TValue> template <class TIterator, class TValue, typename TAccessor>
class SkipListSuffix { class SkipListSuffix {
public: public:
class Iterator { class Iterator {
@ -39,15 +39,16 @@ class SkipListSuffix {
TIterator current_; TIterator current_;
}; };
SkipListSuffix(const TIterator begin, SkipListSuffix(
typename SkipList<TValue>::Accessor &&accessor) const TIterator begin,
typename SkipList<TValue>::template Accessor<TAccessor> &&accessor)
: begin_(begin), accessor_(std::move(accessor)) {} : begin_(begin), accessor_(std::move(accessor)) {}
Iterator begin() const { return Iterator(begin_); } Iterator begin() const { return Iterator(begin_); }
Iterator end() { return Iterator(accessor_.end()); } Iterator end() { return Iterator(accessor_.end()); }
TIterator begin_; TIterator begin_;
typename SkipList<TValue>::Accessor accessor_; typename SkipList<TValue>::template Accessor<TAccessor> accessor_;
}; };
/** /**
@ -67,20 +68,23 @@ class SkipListSuffix {
* ignored). * ignored).
* @Tparam TIndexEntry - index entry inside skiplist * @Tparam TIndexEntry - index entry inside skiplist
* @Tparam TRecord - type of record under index (edge/vertex usually.) * @Tparam TRecord - type of record under index (edge/vertex usually.)
* @Tparam TAccessor - type of accessor to use (const skiplist/non const
* skiplist).
* @return iterable collection of distinct vlist records<TRecord> for which * @return iterable collection of distinct vlist records<TRecord> for which
* exists function evaluates as true * exists function evaluates as true
*/ */
template <class TIterator, class TIndexEntry, class TRecord> template <class TIterator, class TIndexEntry, class TRecord, typename TAccessor>
static auto GetVlists( static auto GetVlists(
typename SkipList<TIndexEntry>::Accessor &&skiplist_accessor, typename SkipList<TIndexEntry>::template Accessor<TAccessor>
&&skiplist_accessor,
TIterator begin, TIterator begin,
const std::function<bool(const TIndexEntry &entry)> predicate, const std::function<bool(const TIndexEntry &entry)> predicate,
const tx::Transaction &t, const tx::Transaction &t,
const std::function<bool(const TIndexEntry &, const TRecord *)> exists, const std::function<bool(const TIndexEntry &, const TRecord *)> exists,
bool current_state = false) { bool current_state = false) {
TIndexEntry *prev = nullptr; TIndexEntry *prev = nullptr;
auto range = auto range = iter::takewhile(
iter::takewhile(predicate, SkipListSuffix<TIterator, TIndexEntry>( predicate, SkipListSuffix<TIterator, TIndexEntry, TAccessor>(
begin, std::move(skiplist_accessor))); begin, std::move(skiplist_accessor)));
auto filtered = iter::filter( auto filtered = iter::filter(
[&t, exists, prev, current_state](TIndexEntry &entry) mutable { [&t, exists, prev, current_state](TIndexEntry &entry) mutable {
@ -176,4 +180,4 @@ static void Refresh(
} }
} }
} }
}; // IndexUtils }; // namespace IndexUtils

View File

@ -172,7 +172,7 @@ class LabelPropertyIndex {
auto access = GetKeyStorage(key)->access(); auto access = GetKeyStorage(key)->access();
auto begin = access.begin(); auto begin = access.begin();
return IndexUtils::GetVlists<typename SkipList<IndexEntry>::Iterator, return IndexUtils::GetVlists<typename SkipList<IndexEntry>::Iterator,
IndexEntry, Vertex>( IndexEntry, Vertex, SkipList<IndexEntry>>(
std::move(access), begin, [](const IndexEntry &) { return true; }, t, std::move(access), begin, [](const IndexEntry &) { return true; }, t,
[key](const IndexEntry &entry, const Vertex *const vertex) { [key](const IndexEntry &entry, const Vertex *const vertex) {
return LabelPropertyIndex::Exists(key, entry.value_, vertex); return LabelPropertyIndex::Exists(key, entry.value_, vertex);
@ -269,8 +269,7 @@ class LabelPropertyIndex {
auto access = GetKeyStorage(key)->access(); auto access = GetKeyStorage(key)->access();
// create the iterator startpoint based on the lower bound // create the iterator startpoint based on the lower bound
auto start_iter = lower auto start_iter = lower ? access.find_or_larger(make_index_bound(
? access.find_or_larger(make_index_bound(
lower, lower.value().IsInclusive())) lower, lower.value().IsInclusive()))
: access.begin(); : access.begin();
@ -394,8 +393,7 @@ class LabelPropertyIndex {
*/ */
std::vector<Key> Keys() { std::vector<Key> Keys() {
std::vector<Key> keys; std::vector<Key> keys;
for (auto &kv : indices_.access()) for (auto &kv : indices_.access()) keys.push_back(kv.first);
keys.push_back(kv.first);
return keys; return keys;
} }

View File

@ -0,0 +1,47 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <vector>
#include "data_structures/concurrent/skiplist.hpp"
TEST(SkipList, Access) {
SkipList<int> input;
{
auto accessor = input.access();
accessor.insert(1);
accessor.insert(2);
accessor.insert(3);
}
auto accessor = input.access();
std::vector<int> results;
for (auto it = accessor.begin(); it != accessor.end(); ++it)
results.push_back(*it);
EXPECT_THAT(results, testing::ElementsAre(1, 2, 3));
}
TEST(SkipList, ConstAccess) {
SkipList<int> input;
{
auto accessor = input.access();
accessor.insert(1);
accessor.insert(2);
accessor.insert(3);
}
const SkipList<int> &skiplist = input;
auto accessor = skiplist.caccess();
std::vector<int> results;
for (auto it = accessor.begin(); it != accessor.end(); ++it)
results.push_back(*it);
EXPECT_THAT(results, testing::ElementsAre(1, 2, 3));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -17,8 +17,9 @@ int Count(TIterable &collection) {
TEST(SkipListSuffix, EmptyRange) { TEST(SkipListSuffix, EmptyRange) {
SkipList<int> V; SkipList<int> V;
auto access = V.access(); auto access = V.access();
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int>( auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int,
access.begin(), std::move(access)); SkipList<int>>(access.begin(),
std::move(access));
EXPECT_EQ(Count(r1), 0); EXPECT_EQ(Count(r1), 0);
} }
@ -28,8 +29,9 @@ TEST(SkipListSuffix, NonEmptyRange) {
access.insert(1); access.insert(1);
access.insert(5); access.insert(5);
access.insert(3); access.insert(3);
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int>( auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int,
access.begin(), std::move(access)); SkipList<int>>(access.begin(),
std::move(access));
EXPECT_EQ(Count(r1), 3); EXPECT_EQ(Count(r1), 3);
auto iter = r1.begin(); auto iter = r1.begin();
EXPECT_EQ(*iter, 1); EXPECT_EQ(*iter, 1);