Const map/set iteration.

Summary: In the current state, it was not possible to iterate, or even access a const map, or const set structure because of an incorrect implementation of "ConstAccessors".

Reviewers: mislav.bradac, teon.banek, buda

Reviewed By: teon.banek, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D902
This commit is contained in:
Dominik Gleich 2017-10-12 16:02:57 +02:00
parent fcecb14545
commit ebc7b2b1b9
11 changed files with 84 additions and 62 deletions

View File

@ -42,9 +42,11 @@ class Item : public TotalOrdering<Item<K, T>>,
// Common base for accessor of all derived containers(ConcurrentMap, // Common base for accessor of all derived containers(ConcurrentMap,
// ConcurrentSet, ...) from SkipList. // ConcurrentSet, ...) from SkipList.
template <typename T> template <typename T, bool IsConst>
class AccessorBase { class AccessorBase {
typedef SkipList<T> list; typedef
typename std::conditional<IsConst, const SkipList<T>, SkipList<T>>::type
list;
typedef typename SkipList<T>::Iterator list_it; typedef typename SkipList<T>::Iterator list_it;
typedef typename SkipList<T>::ConstIterator list_it_con; typedef typename SkipList<T>::ConstIterator list_it_con;
@ -60,20 +62,20 @@ class AccessorBase {
size_t size() { return accessor.size(); }; size_t size() { return accessor.size(); };
list_it begin() { return accessor.begin(); } auto begin() { return accessor.begin(); }
list_it_con begin() const { return accessor.cbegin(); } auto begin() const { return accessor.cbegin(); }
list_it_con cbegin() const { return accessor.cbegin(); } list_it_con cbegin() const { return accessor.cbegin(); }
list_it end() { return accessor.end(); } auto end() { return accessor.end(); }
list_it_con end() const { return accessor.cend(); } auto end() const { return accessor.cend(); }
list_it_con cend() const { return accessor.cend(); } list_it_con cend() const { return accessor.cend(); }
size_t size() const { return accessor.size(); } size_t size() const { return accessor.size(); }
protected: protected:
typename list::template Accessor<SkipList<T>> accessor; decltype(std::declval<list>().access()) accessor;
}; };

View File

@ -21,13 +21,14 @@ class ConcurrentMap {
public: public:
ConcurrentMap() {} ConcurrentMap() {}
class Accessor : public AccessorBase<item_t> { template <bool IsConst = false>
class Accessor : public AccessorBase<item_t, IsConst> {
friend class ConcurrentMap; friend class ConcurrentMap;
using AccessorBase<item_t>::AccessorBase; using AccessorBase<item_t, IsConst>::AccessorBase;
private: private:
using AccessorBase<item_t>::accessor; using AccessorBase<item_t, IsConst>::accessor;
public: public:
std::pair<list_it, bool> insert(const K &key, const T &data) { std::pair<list_it, bool> insert(const K &key, const T &data) {
@ -71,10 +72,8 @@ class ConcurrentMap {
bool remove(const K &key) { return accessor.remove(key); } bool remove(const K &key) { return accessor.remove(key); }
}; };
Accessor access() { return Accessor(&skiplist); } auto access() { return Accessor<false>(&skiplist); }
auto access() const { return Accessor<true>(&skiplist); }
// TODO:
// const Accessor access() const { return Accessor(&skiplist); }
private: private:
list skiplist; list skiplist;

View File

@ -14,13 +14,14 @@ class ConcurrentSet {
public: public:
ConcurrentSet() {} ConcurrentSet() {}
class Accessor : public AccessorBase<T> { template <bool IsConst = false>
class Accessor : public AccessorBase<T, IsConst> {
friend class ConcurrentSet; friend class ConcurrentSet;
using AccessorBase<T>::AccessorBase; using AccessorBase<T, IsConst>::AccessorBase;
private: private:
using AccessorBase<T>::accessor; using AccessorBase<T, IsConst>::accessor;
public: public:
std::pair<list_it, bool> insert(const T &item) { std::pair<list_it, bool> insert(const T &item) {
@ -60,9 +61,8 @@ class ConcurrentSet {
bool remove(const T &item) { return accessor.remove(item); } bool remove(const T &item) { return accessor.remove(item); }
}; };
Accessor access() { return Accessor(&skiplist); } auto access() { return Accessor<false>(&skiplist); }
auto access() const { return Accessor<true>(&skiplist); }
const Accessor access() const { return Accessor(&skiplist); }
private: private:
list skiplist; list skiplist;

View File

@ -475,37 +475,11 @@ class SkipList : private Lockable<lock_t> {
status_.alive_ = false; status_.alive_ = false;
} }
// TODO(dgleich): Remove after C++17 compile flag. auto begin() const { return skiplist->begin(); }
template <class B>
struct negation : std::integral_constant<bool, !bool(B::value)> {};
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(); }
template <typename TIsConst = TSkipList> auto end() const { return skiplist->end(); }
Iterator end(typename std::enable_if<
negation<std::is_const<TIsConst>>::value>::type * = 0) {
return skiplist->end();
}
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(); }
@ -700,7 +674,7 @@ class SkipList : private Lockable<lock_t> {
Accessor<SkipList> access() { return Accessor<SkipList>(this); } Accessor<SkipList> access() { return Accessor<SkipList>(this); }
Accessor<const SkipList> caccess() const { Accessor<const SkipList> access() const {
return Accessor<const SkipList>(this); return Accessor<const SkipList>(this);
} }

View File

@ -18,7 +18,7 @@ namespace {
// returned. // returned.
std::experimental::optional<tx::transaction_id_t> FindOldestTxInLockCycle( std::experimental::optional<tx::transaction_id_t> FindOldestTxInLockCycle(
tx::transaction_id_t start, tx::transaction_id_t start,
ConcurrentMap<tx::transaction_id_t, tx::transaction_id_t>::Accessor ConcurrentMap<tx::transaction_id_t, tx::transaction_id_t>::Accessor<>
&graph_accessor) { &graph_accessor) {
std::vector<tx::transaction_id_t> path; std::vector<tx::transaction_id_t> path;
std::unordered_set<tx::transaction_id_t> visited; std::unordered_set<tx::transaction_id_t> visited;

View File

@ -40,7 +40,7 @@ auto rand_gen_bool(size_t n = 1) {
// Checks for all owned keys if there data is data. // Checks for all owned keys if there data is data.
template <typename S> template <typename S>
void check_present_same(typename S::Accessor &acc, size_t data, void check_present_same(typename S::template Accessor<> &acc, size_t data,
std::vector<size_t> &owned) { std::vector<size_t> &owned) {
for (auto num : owned) { for (auto num : owned) {
CHECK(acc.find(num)->second == data) << "My data is present and my"; CHECK(acc.find(num)->second == data) << "My data is present and my";
@ -49,7 +49,7 @@ void check_present_same(typename S::Accessor &acc, size_t data,
// Checks for all owned.second keys if there data is owned.first. // Checks for all owned.second keys if there data is owned.first.
template <typename S> template <typename S>
void check_present_same(typename S::Accessor &acc, void check_present_same(typename S::template Accessor<> &acc,
std::pair<size_t, std::vector<size_t>> &owned) { std::pair<size_t, std::vector<size_t>> &owned) {
check_present_same<S>(acc, owned.first, owned.second); check_present_same<S>(acc, owned.first, owned.second);
} }
@ -74,7 +74,7 @@ void check_size_list(S &acc, long long size) {
<< iterator_counter; << iterator_counter;
} }
template <typename S> template <typename S>
void check_size(typename S::Accessor &acc, long long size) { void check_size(typename S::template Accessor<> &acc, long long size) {
// check size // check size
CHECK(acc.size() == size) CHECK(acc.size() == size)
@ -94,7 +94,7 @@ void check_size(typename S::Accessor &acc, long long size) {
// Checks if order in list is maintened. It expects map // Checks if order in list is maintened. It expects map
template <typename S> template <typename S>
void check_order(typename S::Accessor &acc) { void check_order(typename S::template Accessor<> &acc) {
if (acc.begin() != acc.end()) { if (acc.begin() != acc.end()) {
auto last = acc.begin()->first; auto last = acc.begin()->first;
for (auto elem : acc) { for (auto elem : acc) {
@ -122,10 +122,9 @@ void check_set(DynamicBitset<> &db, std::vector<bool> &set) {
// Runs given function in threads_no threads and returns vector of futures for // Runs given function in threads_no threads and returns vector of futures for
// there // there
// results. // results.
template <class R, typename S> template <class R, typename S, class FunT>
std::vector<std::future<std::pair<size_t, R>>> run( std::vector<std::future<std::pair<size_t, R>>> run(size_t threads_no,
size_t threads_no, S &skiplist, S &skiplist, FunT f) {
std::function<R(typename S::Accessor, size_t)> f) {
std::vector<std::future<std::pair<size_t, R>>> futures; std::vector<std::future<std::pair<size_t, R>>> futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) { for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
@ -181,7 +180,7 @@ std::vector<bool> collect_set(
// Returns object which tracs in owned which (key,data) where added and // Returns object which tracs in owned which (key,data) where added and
// downcounts. // downcounts.
template <class K, class D, class S> template <class K, class D, class S>
auto insert_try(typename S::Accessor &acc, long long &downcount, auto insert_try(typename S::template Accessor<> &acc, long long &downcount,
std::vector<K> &owned) { std::vector<K> &owned) {
return [&](K key, D data) mutable { return [&](K key, D data) mutable {
if (acc.insert(key, data).second) { if (acc.insert(key, data).second) {

View File

@ -16,6 +16,7 @@ int main(int argc, char **argv) {
auto futures = auto futures =
run<std::vector<long>>(THREADS_NO, skiplist, [](auto acc, auto index) { run<std::vector<long>>(THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range); auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete); auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread; long long downcount = op_per_thread;

View File

@ -8,7 +8,7 @@
using concurrent_map_t = ConcurrentMap<int, int>; using concurrent_map_t = ConcurrentMap<int, int>;
void print_skiplist(const concurrent_map_t::Accessor &map) { void print_skiplist(const concurrent_map_t::Accessor<false> &map) {
DLOG(INFO) << "Map now has: "; DLOG(INFO) << "Map now has: ";
for (auto &kv : map) for (auto &kv : map)
DLOG(INFO) << fmt::format(" ({}, {})", kv.first, kv.second); DLOG(INFO) << fmt::format(" ({}, {})", kv.first, kv.second);

View File

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

View File

@ -6,7 +6,7 @@
#include "data_structures/concurrent/concurrent_set.hpp" #include "data_structures/concurrent/concurrent_set.hpp"
#include "utils/assert.hpp" #include "utils/assert.hpp"
void print_skiplist(const ConcurrentSet<int>::Accessor &skiplist) { void print_skiplist(const ConcurrentSet<int>::Accessor<false> &skiplist) {
DLOG(INFO) << "Skiplist set now has:"; DLOG(INFO) << "Skiplist set now has:";
for (auto &item : skiplist) DLOG(INFO) << item; for (auto &item : skiplist) DLOG(INFO) << item;
} }

View File

@ -32,7 +32,7 @@ TEST(SkipList, ConstAccess) {
} }
const SkipList<int> &skiplist = input; const SkipList<int> &skiplist = input;
auto accessor = skiplist.caccess(); auto accessor = skiplist.access();
std::vector<int> results; std::vector<int> results;
for (auto it = accessor.begin(); it != accessor.end(); ++it) for (auto it = accessor.begin(); it != accessor.end(); ++it)