Reformated code acording to format rules in .clang-format.
This commit is contained in:
parent
246cf1fd78
commit
bf174644de
@ -87,4 +87,3 @@ Standard: "C++11"
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ memgraph
|
||||
*.session.yaml
|
||||
tags
|
||||
.gdb_history
|
||||
Testing/
|
||||
|
@ -5,37 +5,46 @@
|
||||
|
||||
using std::pair;
|
||||
|
||||
template <typename K, typename T> class ConcurrentMap {
|
||||
template <typename K, typename T>
|
||||
class ConcurrentMap
|
||||
{
|
||||
|
||||
class Item : public TotalOrdering<Item>,
|
||||
public TotalOrdering<K, Item>,
|
||||
public TotalOrdering<Item, K>,
|
||||
public pair<const K, T> {
|
||||
public:
|
||||
public pair<const K, T>
|
||||
{
|
||||
public:
|
||||
using pair<const K, T>::pair;
|
||||
|
||||
friend constexpr bool operator<(const Item &lhs, const Item &rhs) {
|
||||
friend constexpr bool operator<(const Item &lhs, const Item &rhs)
|
||||
{
|
||||
std::pair<const K, T> *a;
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const Item &lhs, const Item &rhs) {
|
||||
friend constexpr bool operator==(const Item &lhs, const Item &rhs)
|
||||
{
|
||||
return lhs.first == rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(const K &lhs, const Item &rhs) {
|
||||
friend constexpr bool operator<(const K &lhs, const Item &rhs)
|
||||
{
|
||||
return lhs < rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const K &lhs, const Item &rhs) {
|
||||
friend constexpr bool operator==(const K &lhs, const Item &rhs)
|
||||
{
|
||||
return lhs == rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(const Item &lhs, const K &rhs) {
|
||||
friend constexpr bool operator<(const Item &lhs, const K &rhs)
|
||||
{
|
||||
return lhs.first < rhs;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const Item &lhs, const K &rhs) {
|
||||
friend constexpr bool operator==(const Item &lhs, const K &rhs)
|
||||
{
|
||||
return lhs.first == rhs;
|
||||
}
|
||||
};
|
||||
@ -44,16 +53,17 @@ template <typename K, typename T> class ConcurrentMap {
|
||||
typedef typename SkipList<Item>::Iterator list_it;
|
||||
typedef typename SkipList<Item>::ConstIterator list_it_con;
|
||||
|
||||
public:
|
||||
public:
|
||||
ConcurrentMap() {}
|
||||
|
||||
friend class Accessor;
|
||||
class Accessor {
|
||||
class Accessor
|
||||
{
|
||||
friend class ConcurrentMap;
|
||||
|
||||
Accessor(list *skiplist) : accessor(skiplist->access()) {}
|
||||
|
||||
public:
|
||||
public:
|
||||
Accessor(const Accessor &) = delete;
|
||||
|
||||
Accessor(Accessor &&other) : accessor(std::move(other.accessor)) {}
|
||||
@ -72,15 +82,18 @@ public:
|
||||
|
||||
list_it_con cend() const { return accessor.cend(); }
|
||||
|
||||
std::pair<list_it, bool> insert(const K &key, const T &data) {
|
||||
std::pair<list_it, bool> insert(const K &key, const T &data)
|
||||
{
|
||||
return accessor.insert(Item(key, data));
|
||||
}
|
||||
|
||||
std::pair<list_it, bool> insert(const K &key, T &&data) {
|
||||
std::pair<list_it, bool> insert(const K &key, T &&data)
|
||||
{
|
||||
return accessor.insert(Item(key, std::forward<T>(data)));
|
||||
}
|
||||
|
||||
std::pair<list_it, bool> insert(K &&key, T &&data) {
|
||||
std::pair<list_it, bool> insert(K &&key, T &&data)
|
||||
{
|
||||
return accessor.insert(Item(std::forward<K>(key), std::forward<T>(data)));
|
||||
}
|
||||
|
||||
@ -94,7 +107,7 @@ public:
|
||||
|
||||
size_t size() const { return accessor.size(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
typename list::Accessor accessor;
|
||||
};
|
||||
|
||||
@ -102,6 +115,6 @@ public:
|
||||
|
||||
const Accessor access() const { return Accessor(&skiplist); }
|
||||
|
||||
private:
|
||||
private:
|
||||
list skiplist;
|
||||
};
|
||||
|
@ -94,8 +94,9 @@
|
||||
* and deletion of nodes.
|
||||
*/
|
||||
template <class T, size_t H = 32, class lock_t = SpinLock>
|
||||
class SkipList : private Lockable<lock_t> {
|
||||
public:
|
||||
class SkipList : private Lockable<lock_t>
|
||||
{
|
||||
public:
|
||||
// computes the height for the new node from the interval [1...H]
|
||||
// with p(k) = (1/2)^k for all k from the interval
|
||||
static thread_local FastBinomial<H> rnd;
|
||||
@ -106,8 +107,10 @@ public:
|
||||
* FULLY_LINKED is used to mark the node as fully inserted, i.e. linked
|
||||
* at all layers in the skiplist up to the node height
|
||||
*/
|
||||
struct Flags {
|
||||
enum node_flags : uint8_t {
|
||||
struct Flags
|
||||
{
|
||||
enum node_flags : uint8_t
|
||||
{
|
||||
MARKED = 0x01,
|
||||
FULLY_LINKED = 0x10,
|
||||
};
|
||||
@ -120,12 +123,13 @@ public:
|
||||
|
||||
void set_fully_linked() { flags.fetch_or(FULLY_LINKED); }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::atomic<uint8_t> flags{0};
|
||||
};
|
||||
|
||||
class Node : Lockable<lock_t> {
|
||||
public:
|
||||
class Node : Lockable<lock_t>
|
||||
{
|
||||
public:
|
||||
friend class SkipList;
|
||||
|
||||
const uint8_t height;
|
||||
@ -135,17 +139,20 @@ public:
|
||||
|
||||
const T &value() const { return data.get(); }
|
||||
|
||||
static Node *sentinel(uint8_t height) {
|
||||
static Node *sentinel(uint8_t height)
|
||||
{
|
||||
// we have raw memory and we need to construct an object
|
||||
// of type Node on it
|
||||
return new (allocate(height)) Node(height);
|
||||
}
|
||||
|
||||
static Node *create(const T &item, uint8_t height) {
|
||||
static Node *create(const T &item, uint8_t height)
|
||||
{
|
||||
return create(item, height);
|
||||
}
|
||||
|
||||
static Node *create(T &&item, uint8_t height) {
|
||||
static Node *create(T &&item, uint8_t height)
|
||||
{
|
||||
auto node = allocate(height);
|
||||
|
||||
// we have raw memory and we need to construct an object
|
||||
@ -153,7 +160,8 @@ public:
|
||||
return new (node) Node(std::forward<T>(item), height);
|
||||
}
|
||||
|
||||
static void destroy(Node *node) {
|
||||
static void destroy(Node *node)
|
||||
{
|
||||
node->~Node();
|
||||
std::free(node);
|
||||
}
|
||||
@ -162,8 +170,9 @@ public:
|
||||
|
||||
void forward(size_t level, Node *next) { tower[level].store(next); }
|
||||
|
||||
private:
|
||||
Node(uint8_t height) : height(height) {
|
||||
private:
|
||||
Node(uint8_t height) : height(height)
|
||||
{
|
||||
// here we assume, that the memory for N towers (N = height) has
|
||||
// been allocated right after the Node structure so we need to
|
||||
// initialize that memory
|
||||
@ -171,16 +180,19 @@ public:
|
||||
new (&tower[i]) std::atomic<Node *>{nullptr};
|
||||
}
|
||||
|
||||
Node(T &&data, uint8_t height) : Node(height) {
|
||||
Node(T &&data, uint8_t height) : Node(height)
|
||||
{
|
||||
this->data.set(std::forward<T>(data));
|
||||
}
|
||||
|
||||
~Node() {
|
||||
~Node()
|
||||
{
|
||||
for (auto i = 0; i < height; ++i)
|
||||
tower[i].~atomic();
|
||||
}
|
||||
|
||||
static Node *allocate(uint8_t height) {
|
||||
static Node *allocate(uint8_t height)
|
||||
{
|
||||
// [ Node ][Node*][Node*][Node*]...[Node*]
|
||||
// | | | | |
|
||||
// | 0 1 2 height-1
|
||||
@ -205,33 +217,39 @@ public:
|
||||
std::atomic<Node *> tower[0];
|
||||
};
|
||||
|
||||
public:
|
||||
template <class It> class IteratorBase : public Crtp<It> {
|
||||
protected:
|
||||
public:
|
||||
template <class It>
|
||||
class IteratorBase : public Crtp<It>
|
||||
{
|
||||
protected:
|
||||
IteratorBase(Node *node) : node(node) {}
|
||||
|
||||
Node *node{nullptr};
|
||||
|
||||
public:
|
||||
public:
|
||||
IteratorBase() = default;
|
||||
IteratorBase(const IteratorBase &) = default;
|
||||
|
||||
T &operator*() {
|
||||
T &operator*()
|
||||
{
|
||||
assert(node != nullptr);
|
||||
return node->value();
|
||||
}
|
||||
|
||||
T *operator->() {
|
||||
T *operator->()
|
||||
{
|
||||
assert(node != nullptr);
|
||||
return &node->value();
|
||||
}
|
||||
|
||||
operator T &() {
|
||||
operator T &()
|
||||
{
|
||||
assert(node != nullptr);
|
||||
return node->value();
|
||||
}
|
||||
|
||||
It &operator++() {
|
||||
It &operator++()
|
||||
{
|
||||
assert(node != nullptr);
|
||||
node = node->forward(0);
|
||||
return this->derived();
|
||||
@ -239,18 +257,20 @@ public:
|
||||
|
||||
It &operator++(int) { return operator++(); }
|
||||
|
||||
friend bool operator==(const It &a, const It &b) {
|
||||
friend bool operator==(const It &a, const It &b)
|
||||
{
|
||||
return a.node == b.node;
|
||||
}
|
||||
|
||||
friend bool operator!=(const It &a, const It &b) { return !(a == b); }
|
||||
};
|
||||
|
||||
class ConstIterator : public IteratorBase<ConstIterator> {
|
||||
class ConstIterator : public IteratorBase<ConstIterator>
|
||||
{
|
||||
friend class SkipList;
|
||||
ConstIterator(Node *node) : IteratorBase<ConstIterator>(node) {}
|
||||
|
||||
public:
|
||||
public:
|
||||
ConstIterator() = default;
|
||||
ConstIterator(const ConstIterator &) = default;
|
||||
|
||||
@ -261,18 +281,20 @@ public:
|
||||
operator const T &() { return IteratorBase<ConstIterator>::operator T &(); }
|
||||
};
|
||||
|
||||
class Iterator : public IteratorBase<Iterator> {
|
||||
class Iterator : public IteratorBase<Iterator>
|
||||
{
|
||||
friend class SkipList;
|
||||
Iterator(Node *node) : IteratorBase<Iterator>(node) {}
|
||||
|
||||
public:
|
||||
public:
|
||||
Iterator() = default;
|
||||
Iterator(const Iterator &) = default;
|
||||
};
|
||||
|
||||
SkipList() : header(Node::sentinel(H)) {}
|
||||
|
||||
~SkipList() {
|
||||
~SkipList()
|
||||
{
|
||||
// Someone could be using this map through an Accessor.
|
||||
Node *now = header;
|
||||
header = nullptr;
|
||||
@ -286,25 +308,28 @@ public:
|
||||
|
||||
friend class Accessor;
|
||||
|
||||
class Accessor {
|
||||
class Accessor
|
||||
{
|
||||
friend class SkipList;
|
||||
|
||||
Accessor(SkipList *skiplist) : skiplist(skiplist) {
|
||||
Accessor(SkipList *skiplist) : skiplist(skiplist)
|
||||
{
|
||||
assert(skiplist != nullptr);
|
||||
|
||||
skiplist->gc.add_ref();
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
Accessor(const Accessor &) = delete;
|
||||
|
||||
Accessor(Accessor &&other) : skiplist(other.skiplist) {
|
||||
Accessor(Accessor &&other) : skiplist(other.skiplist)
|
||||
{
|
||||
other.skiplist = nullptr;
|
||||
}
|
||||
|
||||
~Accessor() {
|
||||
if (skiplist == nullptr)
|
||||
return;
|
||||
~Accessor()
|
||||
{
|
||||
if (skiplist == nullptr) return;
|
||||
|
||||
skiplist->gc.release_ref();
|
||||
}
|
||||
@ -321,33 +346,43 @@ public:
|
||||
|
||||
ConstIterator cend() const { return skiplist->cend(); }
|
||||
|
||||
std::pair<Iterator, bool> insert(const T &item) {
|
||||
std::pair<Iterator, bool> insert(const T &item)
|
||||
{
|
||||
return skiplist->insert(item, preds, succs);
|
||||
}
|
||||
|
||||
std::pair<Iterator, bool> insert(T &&item) {
|
||||
std::pair<Iterator, bool> insert(T &&item)
|
||||
{
|
||||
return skiplist->insert(std::forward<T>(item), preds, succs);
|
||||
}
|
||||
|
||||
template <class K> ConstIterator find(const K &item) const {
|
||||
template <class K>
|
||||
ConstIterator find(const K &item) const
|
||||
{
|
||||
return static_cast<const SkipList &>(*skiplist).find(item);
|
||||
}
|
||||
|
||||
template <class K> Iterator find(const K &item) {
|
||||
template <class K>
|
||||
Iterator find(const K &item)
|
||||
{
|
||||
return skiplist->find(item);
|
||||
}
|
||||
|
||||
template <class K> bool contains(const K &item) const {
|
||||
template <class K>
|
||||
bool contains(const K &item) const
|
||||
{
|
||||
return this->find(item) != this->end();
|
||||
}
|
||||
|
||||
template <class K> bool remove(const K &item) {
|
||||
template <class K>
|
||||
bool remove(const K &item)
|
||||
{
|
||||
return skiplist->remove(item, preds, succs);
|
||||
}
|
||||
|
||||
size_t size() const { return skiplist->size(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
SkipList *skiplist;
|
||||
Node *preds[H], *succs[H];
|
||||
};
|
||||
@ -356,7 +391,7 @@ public:
|
||||
|
||||
const Accessor access() const { return Accessor(this); }
|
||||
|
||||
private:
|
||||
private:
|
||||
using guard_t = std::unique_lock<lock_t>;
|
||||
|
||||
Iterator begin() { return Iterator(header->forward(0)); }
|
||||
@ -373,23 +408,33 @@ private:
|
||||
|
||||
size_t size() const { return count.load(); }
|
||||
|
||||
template <class K> bool greater(const K &item, const Node *const node) {
|
||||
template <class K>
|
||||
bool greater(const K &item, const Node *const node)
|
||||
{
|
||||
return node && item > node->value();
|
||||
}
|
||||
|
||||
template <class K> bool less(const K &item, const Node *const node) {
|
||||
template <class K>
|
||||
bool less(const K &item, const Node *const node)
|
||||
{
|
||||
return (node == nullptr) || item < node->value();
|
||||
}
|
||||
|
||||
template <class K> ConstIterator find(const K &item) const {
|
||||
template <class K>
|
||||
ConstIterator find(const K &item) const
|
||||
{
|
||||
return const_cast<SkipList *>(this)->find_node<ConstIterator, K>(item);
|
||||
}
|
||||
|
||||
template <class K> Iterator find(const K &item) {
|
||||
template <class K>
|
||||
Iterator find(const K &item)
|
||||
{
|
||||
return find_node<Iterator, K>(item);
|
||||
}
|
||||
|
||||
template <class It, class K> It find_node(const K &item) {
|
||||
template <class It, class K>
|
||||
It find_node(const K &item)
|
||||
{
|
||||
Node *node, *pred = header;
|
||||
int h = static_cast<int>(pred->height) - 1;
|
||||
|
||||
@ -399,8 +444,7 @@ private:
|
||||
}
|
||||
|
||||
// if we overshoot at every layer, item doesn't exist
|
||||
if (h < 0)
|
||||
return It();
|
||||
if (h < 0) return It();
|
||||
|
||||
// the item is farther to the right, continue going right as long
|
||||
// as the key is greater than the current node's key
|
||||
@ -408,14 +452,14 @@ private:
|
||||
pred = node, node = node->forward(h);
|
||||
|
||||
// check if we have a hit. if not, we need to descend down again
|
||||
if (!less(item, node) && !node->flags.is_marked())
|
||||
return It(node);
|
||||
if (!less(item, node) && !node->flags.is_marked()) return It(node);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K>
|
||||
int find_path(Node *from, int start, const K &item, Node *preds[],
|
||||
Node *succs[]) {
|
||||
Node *succs[])
|
||||
{
|
||||
int level_found = -1;
|
||||
Node *pred = from;
|
||||
|
||||
@ -425,8 +469,7 @@ private:
|
||||
while (greater(item, node))
|
||||
pred = node, node = pred->forward(level);
|
||||
|
||||
if (level_found == -1 && !less(item, node))
|
||||
level_found = level;
|
||||
if (level_found == -1 && !less(item, node)) level_found = level;
|
||||
|
||||
preds[level] = pred;
|
||||
succs[level] = node;
|
||||
@ -437,7 +480,8 @@ private:
|
||||
|
||||
template <bool ADDING>
|
||||
bool lock_nodes(uint8_t height, guard_t guards[], Node *preds[],
|
||||
Node *succs[]) {
|
||||
Node *succs[])
|
||||
{
|
||||
Node *prepred, *pred, *succ = nullptr;
|
||||
bool valid = true;
|
||||
|
||||
@ -456,7 +500,8 @@ private:
|
||||
return valid;
|
||||
}
|
||||
|
||||
std::pair<Iterator, bool> insert(T &&data, Node *preds[], Node *succs[]) {
|
||||
std::pair<Iterator, bool> insert(T &&data, Node *preds[], Node *succs[])
|
||||
{
|
||||
while (true) {
|
||||
// TODO: before here was data.first
|
||||
auto level = find_path(header, H - 1, data, preds, succs);
|
||||
@ -464,8 +509,7 @@ private:
|
||||
if (level != -1) {
|
||||
auto found = succs[level];
|
||||
|
||||
if (found->flags.is_marked())
|
||||
continue;
|
||||
if (found->flags.is_marked()) continue;
|
||||
|
||||
while (!found->flags.is_fully_linked())
|
||||
usleep(250);
|
||||
@ -479,8 +523,7 @@ private:
|
||||
// try to acquire the locks for predecessors up to the height of
|
||||
// the new node. release the locks and try again if someone else
|
||||
// has the locks
|
||||
if (!lock_nodes<true>(height, guards, preds, succs))
|
||||
continue;
|
||||
if (!lock_nodes<true>(height, guards, preds, succs)) continue;
|
||||
|
||||
// you have the locks, create a new node
|
||||
auto new_node = Node::create(std::forward<T>(data), height);
|
||||
@ -503,12 +546,15 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool ok_delete(Node *node, int level) {
|
||||
bool ok_delete(Node *node, int level)
|
||||
{
|
||||
return node->flags.is_fully_linked() && node->height - 1 == level &&
|
||||
!node->flags.is_marked();
|
||||
}
|
||||
|
||||
template <class K> bool remove(const K &item, Node *preds[], Node *succs[]) {
|
||||
template <class K>
|
||||
bool remove(const K &item, Node *preds[], Node *succs[])
|
||||
{
|
||||
Node *node = nullptr;
|
||||
guard_t node_guard;
|
||||
bool marked = false;
|
||||
@ -525,8 +571,7 @@ private:
|
||||
height = node->height;
|
||||
node_guard = node->acquire_unique();
|
||||
|
||||
if (node->flags.is_marked())
|
||||
return false;
|
||||
if (node->flags.is_marked()) return false;
|
||||
|
||||
node->flags.set_marked();
|
||||
marked = true;
|
||||
@ -534,8 +579,7 @@ private:
|
||||
|
||||
guard_t guards[H];
|
||||
|
||||
if (!lock_nodes<false>(height, guards, preds, succs))
|
||||
continue;
|
||||
if (!lock_nodes<false>(height, guards, preds, succs)) continue;
|
||||
|
||||
for (int level = height - 1; level >= 0; --level)
|
||||
preds[level]->forward(level, node->forward(level));
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T, class lock_t = SpinLock>
|
||||
class SkiplistGC : public LazyGC<SkiplistGC<T, lock_t>, lock_t> {
|
||||
public:
|
||||
class SkiplistGC : public LazyGC<SkiplistGC<T, lock_t>, lock_t>
|
||||
{
|
||||
public:
|
||||
// release_ref method should be called by a thread
|
||||
// when the thread finish it job over object
|
||||
// which has to be lazy cleaned
|
||||
@ -17,7 +18,8 @@ public:
|
||||
// are going to be deleted
|
||||
// the only problem with this approach is that
|
||||
// GC may never be called, but for now we can deal with that
|
||||
void release_ref() {
|
||||
void release_ref()
|
||||
{
|
||||
std::vector<T *> local_freelist;
|
||||
|
||||
// take freelist if there is no more threads
|
||||
@ -47,6 +49,6 @@ public:
|
||||
|
||||
void collect(T *node) { freelist.add(node); }
|
||||
|
||||
private:
|
||||
private:
|
||||
FreeList<T> freelist;
|
||||
};
|
||||
|
@ -8,17 +8,19 @@
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
template <class Derived, class lock_t = SpinLock>
|
||||
class LazyGC : public Crtp<Derived>, public Lockable<lock_t> {
|
||||
public:
|
||||
class LazyGC : public Crtp<Derived>, public Lockable<lock_t>
|
||||
{
|
||||
public:
|
||||
// add_ref method should be called by a thread
|
||||
// when the thread has to do something over
|
||||
// object which has to be lazy cleaned when
|
||||
// the thread finish it job
|
||||
void add_ref() {
|
||||
void add_ref()
|
||||
{
|
||||
auto lock = this->acquire_unique();
|
||||
++count;
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
size_t count{0};
|
||||
};
|
||||
|
@ -4,25 +4,26 @@
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "edge_accessor.hpp"
|
||||
|
||||
class Edges {
|
||||
public:
|
||||
Edge::Accessor find(tx::Transaction &t, const Id &id) {
|
||||
class Edges
|
||||
{
|
||||
public:
|
||||
Edge::Accessor find(tx::Transaction &t, const Id &id)
|
||||
{
|
||||
auto edges_accessor = edges.access();
|
||||
auto edges_iterator = edges_accessor.find(id);
|
||||
|
||||
if (edges_iterator == edges_accessor.end())
|
||||
return Edge::Accessor();
|
||||
if (edges_iterator == edges_accessor.end()) return Edge::Accessor();
|
||||
|
||||
// find edge
|
||||
auto edge = edges_iterator->second.find(t);
|
||||
|
||||
if (edge == nullptr)
|
||||
return Edge::Accessor();
|
||||
if (edge == nullptr) return Edge::Accessor();
|
||||
|
||||
return Edge::Accessor(edge, &edges_iterator->second, this);
|
||||
}
|
||||
|
||||
Edge::Accessor insert(tx::Transaction &t) {
|
||||
Edge::Accessor insert(tx::Transaction &t)
|
||||
{
|
||||
// get next vertex id
|
||||
auto next = counter.next(std::memory_order_acquire);
|
||||
|
||||
@ -40,7 +41,7 @@ public:
|
||||
return Edge::Accessor(edge, &inserted_edge_record->second, this);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
ConcurrentMap<uint64_t, EdgeRecord> edges;
|
||||
AtomicCounter<uint64_t> counter;
|
||||
};
|
||||
|
@ -7,13 +7,16 @@
|
||||
#include "storage/indexes/index_record_collection.hpp"
|
||||
#include "storage/label/label.hpp"
|
||||
|
||||
template <class Key, class Item> class Index {
|
||||
public:
|
||||
template <class Key, class Item>
|
||||
class Index
|
||||
{
|
||||
public:
|
||||
using container_t = ConcurrentMap<Key, Item>;
|
||||
|
||||
Index() : index(std::make_unique<container_t>()) {}
|
||||
|
||||
auto update(const Label &label, VertexIndexRecord &&index_record) {
|
||||
auto update(const Label &label, VertexIndexRecord &&index_record)
|
||||
{
|
||||
auto accessor = index->access();
|
||||
auto label_ref = label_ref_t(label);
|
||||
|
||||
@ -27,7 +30,8 @@ public:
|
||||
record_collection.add(std::forward<VertexIndexRecord>(index_record));
|
||||
}
|
||||
|
||||
VertexIndexRecordCollection &find(const Label &label) {
|
||||
VertexIndexRecordCollection &find(const Label &label)
|
||||
{
|
||||
// TODO: accessor should be outside?
|
||||
// bacause otherwise GC could delete record that has just be returned
|
||||
auto label_ref = label_ref_t(label);
|
||||
@ -35,6 +39,6 @@ public:
|
||||
return (*accessor.find(label_ref)).second;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::unique_ptr<container_t> index;
|
||||
};
|
||||
|
@ -1,22 +1,22 @@
|
||||
#include "storage/vertices.hpp"
|
||||
|
||||
const Vertex::Accessor Vertices::find(tx::Transaction &t, const Id &id) {
|
||||
const Vertex::Accessor Vertices::find(tx::Transaction &t, const Id &id)
|
||||
{
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto vertices_iterator = vertices_accessor.find(id);
|
||||
|
||||
if (vertices_iterator == vertices_accessor.end())
|
||||
return Vertex::Accessor();
|
||||
if (vertices_iterator == vertices_accessor.end()) return Vertex::Accessor();
|
||||
|
||||
// find vertex
|
||||
auto vertex = vertices_iterator->second.find(t);
|
||||
|
||||
if (vertex == nullptr)
|
||||
return Vertex::Accessor();
|
||||
if (vertex == nullptr) return Vertex::Accessor();
|
||||
|
||||
return Vertex::Accessor(vertex, &vertices_iterator->second, this);
|
||||
}
|
||||
|
||||
Vertex::Accessor Vertices::insert(tx::Transaction &t) {
|
||||
Vertex::Accessor Vertices::insert(tx::Transaction &t)
|
||||
{
|
||||
// get next vertex id
|
||||
auto next = counter.next();
|
||||
|
||||
@ -36,10 +36,12 @@ Vertex::Accessor Vertices::insert(tx::Transaction &t) {
|
||||
}
|
||||
|
||||
void Vertices::update_label_index(const Label &label,
|
||||
VertexIndexRecord &&index_record) {
|
||||
VertexIndexRecord &&index_record)
|
||||
{
|
||||
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
|
||||
}
|
||||
|
||||
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label) {
|
||||
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label)
|
||||
{
|
||||
return label_index.find(label);
|
||||
}
|
||||
|
@ -1,19 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
template <class Derived, class Other = Derived> struct TotalOrdering {
|
||||
friend constexpr bool operator!=(const Derived &a, const Other &b) {
|
||||
template <class Derived, class Other = Derived>
|
||||
struct TotalOrdering
|
||||
{
|
||||
friend constexpr bool operator!=(const Derived &a, const Other &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
friend constexpr bool operator<=(const Derived &a, const Other &b) {
|
||||
friend constexpr bool operator<=(const Derived &a, const Other &b)
|
||||
{
|
||||
return a < b || a == b;
|
||||
}
|
||||
|
||||
friend constexpr bool operator>(const Derived &a, const Other &b) {
|
||||
friend constexpr bool operator>(const Derived &a, const Other &b)
|
||||
{
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
friend constexpr bool operator>=(const Derived &a, const Other &b) {
|
||||
friend constexpr bool operator>=(const Derived &a, const Other &b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
};
|
||||
|
@ -19,26 +19,30 @@ using skiplist_t = ConcurrentMap<int, int>;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// Returns uniform random size_t generator from range [0,n>
|
||||
auto rand_gen(size_t n) {
|
||||
auto rand_gen(size_t n)
|
||||
{
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<size_t> distribution(0, n - 1);
|
||||
return std::bind(distribution, generator);
|
||||
}
|
||||
|
||||
// Returns random bool generator with distribution of 1 true for n false.
|
||||
auto rand_gen_bool(size_t n = 1) {
|
||||
auto rand_gen_bool(size_t n = 1)
|
||||
{
|
||||
auto gen = rand_gen(n + 1);
|
||||
return [=]() mutable { return gen() == 0; };
|
||||
}
|
||||
|
||||
// Checks for all owned.second keys if there data is owned.first.
|
||||
void check_present_same(skiplist_t::Accessor &acc,
|
||||
std::pair<size_t, std::vector<size_t>> &owned) {
|
||||
std::pair<size_t, std::vector<size_t>> &owned)
|
||||
{
|
||||
check_present_same(acc, owned.first, owned.second);
|
||||
}
|
||||
// Checks for all owned keys if there data is data.
|
||||
void check_present_same(skiplist_t::Accessor &acc, size_t data,
|
||||
std::vector<size_t> &owned) {
|
||||
std::vector<size_t> &owned)
|
||||
{
|
||||
for (auto num : owned) {
|
||||
permanent_assert(acc.find(num)->second == data,
|
||||
"My data is present and my");
|
||||
@ -46,7 +50,8 @@ void check_present_same(skiplist_t::Accessor &acc, size_t data,
|
||||
}
|
||||
|
||||
// Checks if reported size and traversed size are equal to given size.
|
||||
void check_size(const skiplist_t::Accessor &acc, long long size) {
|
||||
void check_size(const skiplist_t::Accessor &acc, long long size)
|
||||
{
|
||||
// check size
|
||||
|
||||
permanent_assert(acc.size() == size,
|
||||
@ -70,7 +75,8 @@ void check_size(const skiplist_t::Accessor &acc, long long size) {
|
||||
template <class R>
|
||||
std::vector<std::future<std::pair<size_t, R>>>
|
||||
run(size_t threads_no, skiplist_t &skiplist,
|
||||
std::function<R(skiplist_t::Accessor, size_t)> f) {
|
||||
std::function<R(skiplist_t::Accessor, size_t)> f)
|
||||
{
|
||||
std::vector<std::future<std::pair<size_t, R>>> futures;
|
||||
|
||||
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
|
||||
@ -84,7 +90,9 @@ run(size_t threads_no, skiplist_t &skiplist,
|
||||
}
|
||||
|
||||
// Collects all data from futures.
|
||||
template <class R> auto collect(std::vector<std::future<R>> &collect) {
|
||||
template <class R>
|
||||
auto collect(std::vector<std::future<R>> &collect)
|
||||
{
|
||||
std::vector<R> collection;
|
||||
for (auto &fut : collect) {
|
||||
collection.push_back(fut.get());
|
||||
@ -96,7 +104,8 @@ template <class R> auto collect(std::vector<std::future<R>> &collect) {
|
||||
// downcounts.
|
||||
template <class K, class D>
|
||||
auto insert_try(skiplist_t::Accessor &acc, size_t &downcount,
|
||||
std::vector<K> &owned) {
|
||||
std::vector<K> &owned)
|
||||
{
|
||||
return [&](K key, D data) mutable {
|
||||
if (acc.insert(key, data).second) {
|
||||
downcount--;
|
||||
@ -106,7 +115,8 @@ auto insert_try(skiplist_t::Accessor &acc, size_t &downcount,
|
||||
}
|
||||
|
||||
// Helper function.
|
||||
int parseLine(char *line) {
|
||||
int parseLine(char *line)
|
||||
{
|
||||
// This assumes that a digit will be found and the line ends in " Kb".
|
||||
int i = strlen(line);
|
||||
const char *p = line;
|
||||
@ -118,7 +128,8 @@ int parseLine(char *line) {
|
||||
}
|
||||
|
||||
// Returns currentlz used memory in kB.
|
||||
int currently_used_memory() { // Note: this value is in KB!
|
||||
int currently_used_memory()
|
||||
{ // Note: this value is in KB!
|
||||
FILE *file = fopen("/proc/self/status", "r");
|
||||
int result = -1;
|
||||
char line[128];
|
||||
@ -137,7 +148,8 @@ int currently_used_memory() { // Note: this value is in KB!
|
||||
// function
|
||||
// is aproximately equal to memory usage after function. Memory usage is thread
|
||||
// senstive so no_threads spawned in function is necessary.
|
||||
void memory_check(size_t no_threads, std::function<void()> f) {
|
||||
void memory_check(size_t no_threads, std::function<void()> f)
|
||||
{
|
||||
long long start = currently_used_memory();
|
||||
f();
|
||||
long long leaked =
|
||||
|
@ -3,7 +3,8 @@
|
||||
#define THREADS_NO 1
|
||||
constexpr size_t elems_per_thread = 16e5;
|
||||
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [&] {
|
||||
ds::static_array<std::thread, THREADS_NO> threads;
|
||||
skiplist_t skiplist;
|
||||
|
@ -7,7 +7,8 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
|
||||
|
||||
// This test checks insert_unique method under pressure.
|
||||
// Test checks for missing data and changed/overwriten data.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -8,7 +8,8 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
|
||||
// Threads will try to insert keys in the same order.
|
||||
// This will force threads to compete intensly with each other.
|
||||
// Test checks for missing data and changed/overwriten data.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -5,7 +5,8 @@
|
||||
constexpr size_t elements = 2e6;
|
||||
|
||||
// Test for simple memory leaks
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -10,7 +10,8 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// Threads will try to insert and remove keys aproximetly in the same order.
|
||||
// This will force threads to compete intensly with each other.
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -8,7 +8,8 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// This test checks remove method under pressure.
|
||||
// Each thread removes it's own data. So removes are disjoint.
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -10,7 +10,8 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// This test checks remove method under pressure.
|
||||
// Each thread removes random data. So removes are joint.
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
@ -12,7 +12,8 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// Each thread makes a series of finds interleaved with method which change.
|
||||
// Exact ratio of finds per change and insert per delete can be regulated with
|
||||
// no_find_per_change and no_insert_for_one_delete.
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user