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