Implemented ConncurentMap and updated it's users.
Added tests for ConcurrentMap. Implemented ~SkipList.
This commit is contained in:
parent
6970170f69
commit
ac7f6f507f
1
Testing/Temporary/CTestCostData.txt
Normal file
1
Testing/Temporary/CTestCostData.txt
Normal file
@ -0,0 +1 @@
|
||||
---
|
3
Testing/Temporary/LastTest.log
Normal file
3
Testing/Temporary/LastTest.log
Normal file
@ -0,0 +1,3 @@
|
||||
Start testing: Jul 28 19:05 BST
|
||||
----------------------------------------------------------
|
||||
End testing: Jul 28 19:05 BST
|
90
format.clang-format
Normal file
90
format.clang-format
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: "C++11"
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
@ -1,9 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "data_structures/skiplist/skiplist_map.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "utils/total_ordering.hpp"
|
||||
|
||||
template<class K, class T>
|
||||
class ConcurrentMap : public SkipListMap<K, T>
|
||||
{
|
||||
// TODO implement with SkipList
|
||||
using std::pair;
|
||||
|
||||
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:
|
||||
using pair<const K, T>::pair;
|
||||
|
||||
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) {
|
||||
return lhs.first == rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(const K &lhs, const Item &rhs) {
|
||||
return lhs < rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const K &lhs, const Item &rhs) {
|
||||
return lhs == rhs.first;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(const Item &lhs, const K &rhs) {
|
||||
return lhs.first < rhs;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const Item &lhs, const K &rhs) {
|
||||
return lhs.first == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SkipList<Item> list;
|
||||
typedef typename SkipList<Item>::Iterator list_it;
|
||||
typedef typename SkipList<Item>::ConstIterator list_it_con;
|
||||
|
||||
public:
|
||||
ConcurrentMap() {}
|
||||
|
||||
friend class Accessor;
|
||||
class Accessor {
|
||||
friend class ConcurrentMap;
|
||||
|
||||
Accessor(list *skiplist) : accessor(skiplist->access()) {}
|
||||
|
||||
public:
|
||||
Accessor(const Accessor &) = delete;
|
||||
|
||||
Accessor(Accessor &&other) : accessor(std::move(other.accessor)) {}
|
||||
|
||||
~Accessor() {}
|
||||
|
||||
list_it begin() { return accessor.begin(); }
|
||||
|
||||
list_it_con begin() const { return accessor.cbegin(); }
|
||||
|
||||
list_it_con cbegin() const { return accessor.cbegin(); }
|
||||
|
||||
list_it end() { return accessor.end(); }
|
||||
|
||||
list_it_con end() const { return accessor.cend(); }
|
||||
|
||||
list_it_con cend() const { return accessor.cend(); }
|
||||
|
||||
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) {
|
||||
return accessor.insert(Item(key, std::forward<T>(data)));
|
||||
}
|
||||
|
||||
std::pair<list_it, bool> insert(K &&key, T &&data) {
|
||||
return accessor.insert(Item(std::forward<K>(key), std::forward<T>(data)));
|
||||
}
|
||||
|
||||
list_it_con find(const K &key) const { return accessor.find(key); }
|
||||
|
||||
list_it find(const K &key) { return accessor.find(key); }
|
||||
|
||||
bool contains(const K &key) const { return this->find(key) != this->end(); }
|
||||
|
||||
bool remove(const K &key) { return accessor.remove(key); }
|
||||
|
||||
size_t size() const { return accessor.size(); }
|
||||
|
||||
private:
|
||||
typename list::Accessor accessor;
|
||||
};
|
||||
|
||||
Accessor access() { return Accessor(&skiplist); }
|
||||
|
||||
const Accessor access() const { return Accessor(&skiplist); }
|
||||
|
||||
private:
|
||||
list skiplist;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,48 +8,45 @@
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
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:
|
||||
// release_ref method should be called by a thread
|
||||
// when the thread finish it job over object
|
||||
// which has to be lazy cleaned
|
||||
// if thread counter becames zero, all objects in the local_freelist
|
||||
// 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()
|
||||
// release_ref method should be called by a thread
|
||||
// when the thread finish it job over object
|
||||
// which has to be lazy cleaned
|
||||
// if thread counter becames zero, all objects in the local_freelist
|
||||
// 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() {
|
||||
std::vector<T *> local_freelist;
|
||||
|
||||
// take freelist if there is no more threads
|
||||
{
|
||||
std::vector<T *> local_freelist;
|
||||
|
||||
// take freelist if there is no more threads
|
||||
{
|
||||
auto lock = this->acquire_unique();
|
||||
--this->count;
|
||||
if (this->count == 0) {
|
||||
freelist.swap(local_freelist);
|
||||
}
|
||||
}
|
||||
|
||||
if (local_freelist.size() > 0) {
|
||||
std::cout << "GC started" << std::endl;
|
||||
std::cout << "Local skiplist size: " <<
|
||||
local_freelist.size() << std::endl;
|
||||
long long counter = 0;
|
||||
// destroy all elements from local_freelist
|
||||
for (auto element : local_freelist) {
|
||||
counter++;
|
||||
if (element->flags.is_marked()) T::destroy(element);
|
||||
}
|
||||
std::cout << "Number of destroyed elements " << counter << std::endl;
|
||||
}
|
||||
auto lock = this->acquire_unique();
|
||||
--this->count;
|
||||
if (this->count == 0) {
|
||||
freelist.swap(local_freelist);
|
||||
}
|
||||
}
|
||||
|
||||
void collect(T *node)
|
||||
{
|
||||
freelist.add(node);
|
||||
if (local_freelist.size() > 0) {
|
||||
std::cout << "GC started" << std::endl;
|
||||
std::cout << "Local list size: " << local_freelist.size() << std::endl;
|
||||
long long counter = 0;
|
||||
// destroy all elements from local_freelist
|
||||
for (auto element : local_freelist) {
|
||||
|
||||
if (element->flags.is_marked()) {
|
||||
T::destroy(element);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
std::cout << "Number of destroyed elements " << counter << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void collect(T *node) { freelist.add(node); }
|
||||
|
||||
private:
|
||||
FreeList<T> freelist;
|
||||
FreeList<T> freelist;
|
||||
};
|
||||
|
@ -1,24 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: remove from here and from the project
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
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:
|
||||
// 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()
|
||||
{
|
||||
auto lock = this->acquire_unique();
|
||||
++count;
|
||||
}
|
||||
// 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() {
|
||||
auto lock = this->acquire_unique();
|
||||
++count;
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t count{0};
|
||||
size_t count{0};
|
||||
};
|
||||
|
@ -1,52 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "edge_accessor.hpp"
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "edge_accessor.hpp"
|
||||
|
||||
class Edges
|
||||
{
|
||||
class Edges {
|
||||
public:
|
||||
Edge::Accessor find(tx::Transaction& t, const Id& id)
|
||||
{
|
||||
auto edges_accessor = edges.access();
|
||||
auto edges_iterator = edges_accessor.find(id);
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
return Edge::Accessor(edge, &edges_iterator->second, this);
|
||||
}
|
||||
|
||||
Edge::Accessor insert(tx::Transaction& t)
|
||||
{
|
||||
// get next vertex id
|
||||
auto next = counter.next(std::memory_order_acquire);
|
||||
Edge::Accessor insert(tx::Transaction &t) {
|
||||
// get next vertex id
|
||||
auto next = counter.next(std::memory_order_acquire);
|
||||
|
||||
// create new vertex record
|
||||
EdgeRecord edge_record(next);
|
||||
// create new vertex record
|
||||
EdgeRecord edge_record(next);
|
||||
|
||||
// insert the new vertex record into the vertex store
|
||||
auto edges_accessor = edges.access();
|
||||
auto result = edges_accessor.insert_unique(
|
||||
next,
|
||||
std::move(edge_record)
|
||||
);
|
||||
// insert the new vertex record into the vertex store
|
||||
auto edges_accessor = edges.access();
|
||||
auto result = edges_accessor.insert(next, std::move(edge_record));
|
||||
|
||||
// create new vertex
|
||||
auto inserted_edge_record = result.first;
|
||||
auto edge = inserted_edge_record->second.insert(t);
|
||||
// create new vertex
|
||||
auto inserted_edge_record = result.first;
|
||||
auto edge = inserted_edge_record->second.insert(t);
|
||||
|
||||
return Edge::Accessor(edge, &inserted_edge_record->second, this);
|
||||
}
|
||||
return Edge::Accessor(edge, &inserted_edge_record->second, this);
|
||||
}
|
||||
|
||||
private:
|
||||
ConcurrentMap<uint64_t, EdgeRecord> edges;
|
||||
AtomicCounter<uint64_t> counter;
|
||||
ConcurrentMap<uint64_t, EdgeRecord> edges;
|
||||
AtomicCounter<uint64_t> counter;
|
||||
};
|
||||
|
@ -7,39 +7,34 @@
|
||||
#include "storage/indexes/index_record_collection.hpp"
|
||||
#include "storage/label/label.hpp"
|
||||
|
||||
template <class Key, class Item>
|
||||
class Index
|
||||
{
|
||||
template <class Key, class Item> 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 accessor = index->access();
|
||||
auto label_ref = label_ref_t(label);
|
||||
auto update(const Label &label, VertexIndexRecord &&index_record) {
|
||||
auto accessor = index->access();
|
||||
auto label_ref = label_ref_t(label);
|
||||
|
||||
// create Index Record Collection if it doesn't exist
|
||||
if (!accessor.contains(label_ref)) {
|
||||
accessor.insert_unique(label_ref,
|
||||
std::move(VertexIndexRecordCollection()));
|
||||
}
|
||||
|
||||
// add Vertex Index Record to the Record Collection
|
||||
auto &record_collection = (*accessor.find(label_ref)).second;
|
||||
record_collection.add(std::forward<VertexIndexRecord>(index_record));
|
||||
// create Index Record Collection if it doesn't exist
|
||||
if (!accessor.contains(label_ref)) {
|
||||
accessor.insert(label_ref, std::move(VertexIndexRecordCollection()));
|
||||
}
|
||||
|
||||
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);
|
||||
auto accessor = index->access();
|
||||
return (*accessor.find(label_ref)).second;
|
||||
}
|
||||
// add Vertex Index Record to the Record Collection
|
||||
auto &record_collection = (*accessor.find(label_ref)).second;
|
||||
record_collection.add(std::forward<VertexIndexRecord>(index_record));
|
||||
}
|
||||
|
||||
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);
|
||||
auto accessor = index->access();
|
||||
return (*accessor.find(label_ref)).second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<container_t> index;
|
||||
std::unique_ptr<container_t> index;
|
||||
};
|
||||
|
@ -1,50 +1,45 @@
|
||||
#include "storage/vertices.hpp"
|
||||
|
||||
const Vertex::Accessor Vertices::find(tx::Transaction &t, const Id &id)
|
||||
{
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto vertices_iterator = vertices_accessor.find(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);
|
||||
// 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);
|
||||
return Vertex::Accessor(vertex, &vertices_iterator->second, this);
|
||||
}
|
||||
|
||||
Vertex::Accessor Vertices::insert(tx::Transaction &t)
|
||||
{
|
||||
// get next vertex id
|
||||
auto next = counter.next();
|
||||
Vertex::Accessor Vertices::insert(tx::Transaction &t) {
|
||||
// get next vertex id
|
||||
auto next = counter.next();
|
||||
|
||||
// create new vertex record
|
||||
VertexRecord vertex_record(next);
|
||||
// vertex_record.id(next);
|
||||
// create new vertex record
|
||||
VertexRecord vertex_record(next);
|
||||
// vertex_record.id(next);
|
||||
|
||||
// insert the new vertex record into the vertex store
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto result =
|
||||
vertices_accessor.insert_unique(next, std::move(vertex_record));
|
||||
// insert the new vertex record into the vertex store
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto result = vertices_accessor.insert(next, std::move(vertex_record));
|
||||
|
||||
// create new vertex
|
||||
auto inserted_vertex_record = result.first;
|
||||
auto vertex = inserted_vertex_record->second.insert(t);
|
||||
// create new vertex
|
||||
auto inserted_vertex_record = result.first;
|
||||
auto vertex = inserted_vertex_record->second.insert(t);
|
||||
|
||||
return Vertex::Accessor(vertex, &inserted_vertex_record->second, this);
|
||||
return Vertex::Accessor(vertex, &inserted_vertex_record->second, this);
|
||||
}
|
||||
|
||||
void Vertices::update_label_index(const Label &label,
|
||||
VertexIndexRecord &&index_record)
|
||||
{
|
||||
label_index.update(label,
|
||||
std::forward<VertexIndexRecord>(index_record));
|
||||
VertexIndexRecord &&index_record) {
|
||||
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
|
||||
}
|
||||
|
||||
VertexIndexRecordCollection& Vertices::find_label_index(const Label& label)
|
||||
{
|
||||
return label_index.find(label);
|
||||
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label) {
|
||||
return label_index.find(label);
|
||||
}
|
||||
|
@ -1,25 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
template <class Derived>
|
||||
struct TotalOrdering
|
||||
{
|
||||
friend constexpr bool operator!=(const Derived& a, const Derived& b)
|
||||
{
|
||||
return !(a == 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 Derived& b)
|
||||
{
|
||||
return a < b || a == b;
|
||||
}
|
||||
friend constexpr bool operator<=(const Derived &a, const Other &b) {
|
||||
return a < b || a == b;
|
||||
}
|
||||
|
||||
friend constexpr bool operator>(const Derived& a, const Derived& b)
|
||||
{
|
||||
return !(a <= b);
|
||||
}
|
||||
friend constexpr bool operator>(const Derived &a, const Other &b) {
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
friend constexpr bool operator>=(const Derived& a, const Derived& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
friend constexpr bool operator>=(const Derived &a, const Other &b) {
|
||||
return !(a < b);
|
||||
}
|
||||
};
|
||||
|
135
tests/concurrent/common.h
Normal file
135
tests/concurrent/common.h
Normal file
@ -0,0 +1,135 @@
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "data_structures/static_array.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using skiplist_t = ConcurrentMap<int, int>;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
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 gen = rand_gen(n + 1);
|
||||
return [=]() mutable { return gen() == 0; };
|
||||
}
|
||||
|
||||
void check_present_same(skiplist_t::Accessor &acc,
|
||||
std::pair<size_t, std::vector<size_t>> &owned) {
|
||||
for (auto num : owned.second) {
|
||||
permanent_assert(acc.find(num)->second == owned.first,
|
||||
"My data is present and my");
|
||||
}
|
||||
}
|
||||
void check_present_same(skiplist_t::Accessor &acc, size_t owner,
|
||||
std::vector<size_t> &owned) {
|
||||
for (auto num : owned) {
|
||||
permanent_assert(acc.find(num)->second == owner,
|
||||
"My data is present and my");
|
||||
}
|
||||
}
|
||||
|
||||
void check_size(const skiplist_t::Accessor &acc, long long size) {
|
||||
// check size
|
||||
|
||||
permanent_assert(acc.size() == size,
|
||||
"Size should be " << size << ", but size is " << acc.size());
|
||||
|
||||
// check count
|
||||
|
||||
size_t iterator_counter = 0;
|
||||
|
||||
for (auto elem : acc) {
|
||||
++iterator_counter;
|
||||
}
|
||||
permanent_assert(iterator_counter == size, "Iterator count should be "
|
||||
<< size << ", but size is "
|
||||
<< acc.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::vector<std::future<std::pair<size_t, R>>> futures;
|
||||
|
||||
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
|
||||
std::packaged_task<std::pair<size_t, R>()> task([&skiplist, f, thread_i]() {
|
||||
return std::pair<size_t, R>(thread_i, f(skiplist.access(), thread_i));
|
||||
}); // wrap the function
|
||||
futures.push_back(task.get_future()); // get a future
|
||||
std::thread(std::move(task)).detach();
|
||||
}
|
||||
return futures;
|
||||
}
|
||||
|
||||
template <class R> auto collect(std::vector<std::future<R>> &collect) {
|
||||
std::vector<R> collection;
|
||||
for (auto &fut : collect) {
|
||||
collection.push_back(fut.get());
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
template <class K, class D>
|
||||
auto insert_try(skiplist_t::Accessor &acc, size_t &downcount,
|
||||
std::vector<K> &owned) {
|
||||
return [&](K key, D data) mutable {
|
||||
if (acc.insert(key, data).second) {
|
||||
downcount--;
|
||||
owned.push_back(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
while (*p < '0' || *p > '9')
|
||||
p++;
|
||||
line[i - 3] = '\0';
|
||||
i = atoi(p);
|
||||
return i;
|
||||
}
|
||||
|
||||
int currently_used_memory() { // Note: this value is in KB!
|
||||
FILE *file = fopen("/proc/self/status", "r");
|
||||
int result = -1;
|
||||
char line[128];
|
||||
|
||||
while (fgets(line, 128, file) != NULL) {
|
||||
if (strncmp(line, "VmSize:", 7) == 0) {
|
||||
result = parseLine(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
void memory_check(size_t no_threads, std::function<void()> f) {
|
||||
long long start = currently_used_memory();
|
||||
f();
|
||||
long long leaked =
|
||||
currently_used_memory() - start -
|
||||
no_threads * 73732; // OS sensitive, 73732 size allocated for thread
|
||||
std::cout << "leaked: " << leaked << "\n";
|
||||
permanent_assert(leaked <= 0, "Memory leak check");
|
||||
}
|
@ -1,85 +1,69 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "data_structures/static_array.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using skiplist_t = ConcurrentMap<int, int>;
|
||||
using namespace std::chrono_literals;
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 1
|
||||
constexpr size_t elems_per_thread = 1000;
|
||||
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;
|
||||
|
||||
|
||||
// put THREADS_NO * elems_per_thread items to the skiplist
|
||||
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
|
||||
threads[thread_i] = std::thread(
|
||||
[&skiplist](size_t start, size_t end) {
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = start; elem_i < end; ++elem_i) {
|
||||
accessor.insert_unique(elem_i, elem_i);
|
||||
}
|
||||
},
|
||||
thread_i * elems_per_thread,
|
||||
thread_i * elems_per_thread + elems_per_thread);
|
||||
threads[thread_i] = std::thread(
|
||||
[&skiplist](size_t start, size_t end) {
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = 0; elem_i < elems_per_thread; ++elem_i) {
|
||||
accessor.insert(elem_i, elem_i);
|
||||
}
|
||||
},
|
||||
thread_i * elems_per_thread,
|
||||
thread_i * elems_per_thread + elems_per_thread);
|
||||
}
|
||||
// wait all threads
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
// get skiplist size
|
||||
{
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == THREADS_NO * elems_per_thread,
|
||||
"all elements in skiplist");
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == THREADS_NO * elems_per_thread,
|
||||
"all elements in skiplist");
|
||||
}
|
||||
|
||||
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
|
||||
threads[thread_i] = std::thread(
|
||||
[&skiplist](size_t start, size_t end) {
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = start; elem_i < end; ++elem_i) {
|
||||
permanent_assert(accessor.remove(elem_i) == true, "");
|
||||
}
|
||||
},
|
||||
thread_i * elems_per_thread,
|
||||
thread_i * elems_per_thread + elems_per_thread);
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = 0; elem_i < elems_per_thread; ++elem_i) {
|
||||
permanent_assert(accessor.remove(elem_i) == true, "");
|
||||
}
|
||||
},
|
||||
thread_i * elems_per_thread,
|
||||
thread_i * elems_per_thread + elems_per_thread);
|
||||
}
|
||||
// wait all threads
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
// check size
|
||||
{
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == 0, "Size should be 0, but size is " << accessor.size());
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == 0, "Size should be 0, but size is "
|
||||
<< accessor.size());
|
||||
}
|
||||
|
||||
// check count
|
||||
{
|
||||
size_t iterator_counter = 0;
|
||||
auto accessor = skiplist.access();
|
||||
for (auto elem : accessor) {
|
||||
++iterator_counter;
|
||||
cout << elem.first << " ";
|
||||
}
|
||||
permanent_assert(iterator_counter == 0, "deleted elements");
|
||||
size_t iterator_counter = 0;
|
||||
auto accessor = skiplist.access();
|
||||
for (auto elem : accessor) {
|
||||
++iterator_counter;
|
||||
cout << elem.first << " ";
|
||||
}
|
||||
permanent_assert(iterator_counter == 0, "deleted elements");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
||||
// TODO: test GC and memory
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
36
tests/concurrent/sl_insert.cpp
Normal file
36
tests/concurrent/sl_insert.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
|
||||
constexpr size_t elems_per_thread = 100000;
|
||||
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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::vector<size_t>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand = rand_gen(key_range);
|
||||
size_t downcount = elems_per_thread;
|
||||
std::vector<size_t> owned;
|
||||
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
|
||||
|
||||
do {
|
||||
inserter(rand(), index);
|
||||
} while (downcount > 0);
|
||||
|
||||
check_present_same(acc, index, owned);
|
||||
return owned;
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
for (auto &owned : collect(futures)) {
|
||||
check_present_same(accessor, owned);
|
||||
}
|
||||
|
||||
check_size(accessor, THREADS_NO * elems_per_thread);
|
||||
});
|
||||
}
|
37
tests/concurrent/sl_insert_competetive.cpp
Normal file
37
tests/concurrent/sl_insert_competetive.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
constexpr size_t elems_per_thread = 100000;
|
||||
constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
|
||||
|
||||
// This test checks insert_unique method under pressure.
|
||||
// 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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::vector<size_t>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand = rand_gen(key_range);
|
||||
size_t downcount = elems_per_thread;
|
||||
std::vector<size_t> owned;
|
||||
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
|
||||
|
||||
for (int i = 0; downcount > 0; i++) {
|
||||
inserter(i, index);
|
||||
}
|
||||
|
||||
check_present_same(acc, index, owned);
|
||||
return owned;
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
for (auto &owned : collect(futures)) {
|
||||
check_present_same(accessor, owned);
|
||||
}
|
||||
|
||||
check_size(accessor, THREADS_NO * elems_per_thread);
|
||||
});
|
||||
}
|
21
tests/concurrent/sl_memory.cpp
Normal file
21
tests/concurrent/sl_memory.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
|
||||
constexpr size_t elements = 2e6;
|
||||
|
||||
// Test for simple memory leaks
|
||||
int main() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<size_t>(THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
for (size_t i = 0; i < elements; i++) {
|
||||
acc.insert(i, index);
|
||||
}
|
||||
return index;
|
||||
});
|
||||
collect(futures);
|
||||
check_size(skiplist.access(), elements);
|
||||
});
|
||||
}
|
62
tests/concurrent/sl_remove_competetive.cpp
Normal file
62
tests/concurrent/sl_remove_competetive.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
// Depending on value there is a possiblity of numerical overflow
|
||||
constexpr size_t max_number = 10;
|
||||
constexpr size_t no_insert_for_one_delete = 2;
|
||||
|
||||
// This test checks remove method under pressure.
|
||||
// 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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::pair<long long, long long>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
|
||||
size_t downcount = op_per_thread;
|
||||
long long sum = 0;
|
||||
long long count = 0;
|
||||
|
||||
for (int i = 0; downcount > 0; i++) {
|
||||
auto data = i % max_number;
|
||||
if (rand_op()) {
|
||||
auto t = i;
|
||||
while (t > 0) {
|
||||
if (acc.remove(t)) {
|
||||
sum -= t % max_number;
|
||||
downcount--;
|
||||
count--;
|
||||
break;
|
||||
}
|
||||
t--;
|
||||
}
|
||||
} else {
|
||||
if (acc.insert(i, data).second) {
|
||||
sum += data;
|
||||
count++;
|
||||
downcount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::pair<long long, long long>(sum, count);
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
long long sums = 0;
|
||||
long long counters = 0;
|
||||
for (auto &data : collect(futures)) {
|
||||
sums += data.second.first;
|
||||
counters += data.second.second;
|
||||
}
|
||||
|
||||
for (auto &e : accessor) {
|
||||
sums -= e.second;
|
||||
}
|
||||
permanent_assert(sums == 0, "Aproximetly Same values are present");
|
||||
check_size(accessor, counters);
|
||||
});
|
||||
}
|
46
tests/concurrent/sl_remove_disjoint.cpp
Normal file
46
tests/concurrent/sl_remove_disjoint.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
constexpr size_t key_range = 1e5;
|
||||
constexpr size_t op_per_thread = 1e6;
|
||||
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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::vector<size_t>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand = rand_gen(key_range);
|
||||
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
|
||||
size_t downcount = op_per_thread;
|
||||
std::vector<size_t> owned;
|
||||
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
|
||||
|
||||
do {
|
||||
if (owned.size() != 0 && rand_op()) {
|
||||
auto rem = rand() % owned.size();
|
||||
permanent_assert(acc.remove(owned[rem]), "Owned data removed");
|
||||
owned.erase(owned.begin() + rem);
|
||||
downcount--;
|
||||
} else {
|
||||
inserter(rand(), index);
|
||||
}
|
||||
} while (downcount > 0);
|
||||
|
||||
check_present_same(acc, index, owned);
|
||||
return owned;
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
size_t count = 0;
|
||||
for (auto &owned : collect(futures)) {
|
||||
check_present_same(accessor, owned);
|
||||
count += owned.second.size();
|
||||
}
|
||||
check_size(accessor, count);
|
||||
});
|
||||
}
|
60
tests/concurrent/sl_remove_joint.cpp
Normal file
60
tests/concurrent/sl_remove_joint.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
constexpr size_t key_range = 1e5;
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
// Depending on value there is a possiblity of numerical overflow
|
||||
constexpr size_t max_number = 10;
|
||||
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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::pair<long long, long long>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand = rand_gen(key_range);
|
||||
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
|
||||
size_t downcount = op_per_thread;
|
||||
long long sum = 0;
|
||||
long long count = 0;
|
||||
|
||||
do {
|
||||
auto num = rand();
|
||||
auto data = num % max_number;
|
||||
if (rand_op()) {
|
||||
if (acc.remove(num)) {
|
||||
sum -= data;
|
||||
downcount--;
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
if (acc.insert(num, data).second) {
|
||||
sum += data;
|
||||
downcount--;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} while (downcount > 0);
|
||||
|
||||
return std::pair<long long, long long>(sum, count);
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
long long sums = 0;
|
||||
long long counters = 0;
|
||||
for (auto &data : collect(futures)) {
|
||||
sums += data.second.first;
|
||||
counters += data.second.second;
|
||||
}
|
||||
|
||||
for (auto &e : accessor) {
|
||||
sums -= e.second;
|
||||
}
|
||||
permanent_assert(sums == 0, "Aproximetly Same values are present");
|
||||
check_size(accessor, counters);
|
||||
});
|
||||
}
|
66
tests/concurrent/sl_simulation.cpp
Normal file
66
tests/concurrent/sl_simulation.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "common.h"
|
||||
|
||||
#define THREADS_NO 8
|
||||
constexpr size_t key_range = 1e5;
|
||||
constexpr size_t op_per_thread = 1e6;
|
||||
// Depending on value there is a possiblity of numerical overflow
|
||||
constexpr size_t max_number = 10;
|
||||
constexpr size_t no_find_per_change = 5;
|
||||
constexpr size_t no_insert_for_one_delete = 1;
|
||||
|
||||
// This test simulates behavior of transactions.
|
||||
// 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() {
|
||||
memory_check(THREADS_NO, [] {
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto futures = run<std::pair<long long, long long>>(
|
||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
auto rand = rand_gen(key_range);
|
||||
auto rand_change = rand_gen_bool(no_find_per_change);
|
||||
auto rand_delete = rand_gen_bool(no_insert_for_one_delete);
|
||||
long long sum = 0;
|
||||
long long count = 0;
|
||||
|
||||
for (int i = 0; i < op_per_thread; i++) {
|
||||
auto num = rand();
|
||||
auto data = num % max_number;
|
||||
if (rand_change()) {
|
||||
if (rand_delete()) {
|
||||
if (acc.remove(num)) {
|
||||
sum -= data;
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
if (acc.insert(num, data).second) {
|
||||
sum += data;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto value = acc.find(num);
|
||||
permanent_assert(value == acc.end() || value->second == data,
|
||||
"Data is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair<long long, long long>(sum, count);
|
||||
});
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
long long sums = 0;
|
||||
long long counters = 0;
|
||||
for (auto &data : collect(futures)) {
|
||||
sums += data.second.first;
|
||||
counters += data.second.second;
|
||||
}
|
||||
|
||||
for (auto &e : accessor) {
|
||||
sums -= e.second;
|
||||
}
|
||||
permanent_assert(sums == 0, "Same values aren't present");
|
||||
check_size(accessor, counters);
|
||||
});
|
||||
}
|
@ -8,62 +8,59 @@ using std::endl;
|
||||
|
||||
using skiplist_t = ConcurrentMap<int, int>;
|
||||
|
||||
void print_skiplist(const skiplist_t::Accessor &skiplist)
|
||||
{
|
||||
cout << "---- skiplist now has: ";
|
||||
void print_skiplist(const skiplist_t::Accessor &skiplist) {
|
||||
cout << "---- skiplist now has: ";
|
||||
|
||||
for (auto &kv : skiplist)
|
||||
cout << "(" << kv.first << ", " << kv.second << ") ";
|
||||
for (auto &kv : skiplist)
|
||||
cout << "(" << kv.first << ", " << kv.second << ") ";
|
||||
|
||||
cout << "----" << endl;
|
||||
cout << "----" << endl;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
skiplist_t skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
int main(void) {
|
||||
skiplist_t skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
// insert 10
|
||||
permanent_assert(accessor.insert_unique(1, 10).second == true,
|
||||
"add first element");
|
||||
// insert 10
|
||||
permanent_assert(accessor.insert(1, 10).second == true, "add first element");
|
||||
|
||||
// try insert 10 again (should fail)
|
||||
permanent_assert(accessor.insert_unique(1, 10).second == false,
|
||||
"add the same element, should fail");
|
||||
// try insert 10 again (should fail)
|
||||
permanent_assert(accessor.insert(1, 10).second == false,
|
||||
"add the same element, should fail");
|
||||
|
||||
// insert 20
|
||||
permanent_assert(accessor.insert_unique(2, 20).second == true,
|
||||
"insert new unique element");
|
||||
// insert 20
|
||||
permanent_assert(accessor.insert(2, 20).second == true,
|
||||
"insert new unique element");
|
||||
|
||||
print_skiplist(accessor);
|
||||
print_skiplist(accessor);
|
||||
|
||||
// value at key 3 shouldn't exist
|
||||
permanent_assert((accessor.find(3) == accessor.end()) == true,
|
||||
"try to find element which doesn't exist");
|
||||
// value at key 3 shouldn't exist
|
||||
permanent_assert((accessor.find(3) == accessor.end()) == true,
|
||||
"try to find element which doesn't exist");
|
||||
|
||||
// value at key 2 should exist
|
||||
permanent_assert((accessor.find(2) != accessor.end()) == true,
|
||||
"find iterator");
|
||||
// value at key 2 should exist
|
||||
permanent_assert((accessor.find(2) != accessor.end()) == true,
|
||||
"find iterator");
|
||||
|
||||
// at key 2 is 20 (true)
|
||||
permanent_assert(accessor.find(2)->second == 20, "find element");
|
||||
// at key 2 is 20 (true)
|
||||
permanent_assert(accessor.find(2)->second == 20, "find element");
|
||||
|
||||
// removed existing (1)
|
||||
permanent_assert(accessor.remove(1) == true, "try to remove element");
|
||||
// removed existing (1)
|
||||
permanent_assert(accessor.remove(1) == true, "try to remove element");
|
||||
|
||||
// removed non-existing (3)
|
||||
permanent_assert(accessor.remove(3) == false,
|
||||
"try to remove element which doesn't exist");
|
||||
// removed non-existing (3)
|
||||
permanent_assert(accessor.remove(3) == false,
|
||||
"try to remove element which doesn't exist");
|
||||
|
||||
// insert (1, 10)
|
||||
permanent_assert(accessor.insert_unique(1, 10).second == true,
|
||||
"insert unique element");
|
||||
// insert (1, 10)
|
||||
permanent_assert(accessor.insert(1, 10).second == true,
|
||||
"insert unique element");
|
||||
|
||||
// insert (4, 40)
|
||||
permanent_assert(accessor.insert_unique(4, 40).second == true,
|
||||
"insert unique element");
|
||||
// insert (4, 40)
|
||||
permanent_assert(accessor.insert(4, 40).second == true,
|
||||
"insert unique element");
|
||||
|
||||
print_skiplist(accessor);
|
||||
print_skiplist(accessor);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user