worked on mvcc, compiler and some utils, unfinished

This commit is contained in:
Dominik Tomičević 2015-09-13 11:34:17 +02:00
parent 4aa4f84ada
commit e28d15c272
45 changed files with 764 additions and 136 deletions

View File

@ -9,5 +9,10 @@
#include "relationship.hpp"
#include "node.hpp"
#include "return.hpp"
#include "pattern.hpp"
#include "return.hpp"
#include "match.hpp"
#include "queries.hpp"
#include "start.hpp"
#endif

View File

@ -34,18 +34,27 @@ struct Star;
struct Slash;
struct Rem;
struct RelationshipSpecs;
struct RelationshipList;
struct Relationship;
struct Node;
struct LabelList;
struct Pattern;
struct ReturnList;
struct Match;
struct Where;
struct ReadQuery;
struct Start;
struct AstVisitor : Visitor<Accessor, Boolean, Float, Identifier, Integer,
String, Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus, Minus, Star,
Slash, Rem, PropertyList, RelationshipList, Relationship, Node,
LabelList, ReturnList> {};
RelationshipSpecs, LabelList, ReturnList, Pattern, Match, ReadQuery,
Start, Where> {};
}

22
cypher/ast/match.hpp Normal file
View File

@ -0,0 +1,22 @@
#ifndef MEMGRAPH_CYPHER_AST_MATCH_HPP
#define MEMGRAPH_CYPHER_AST_MATCH_HPP
#include "ast_node.hpp"
#include "pattern.hpp"
#include "where.hpp"
namespace ast
{
struct Match : public AstNode<Match>
{
Match(Pattern* pattern, Where* where)
: pattern(pattern), where(where) {}
Pattern* pattern;
Where* where;
};
}
#endif

View File

@ -2,20 +2,18 @@
#define MEMGRAPH_CYPHER_AST_PATTERN_HPP
#include "ast_node.hpp"
#include "relationship.hpp"
namespace ast
{
template <class Derived>
struct Direction : public AstNode<Derived> {};
struct DirectionLeft : public Direction<DirectionLeft> {};
struct DirectionRight : public Direction<DirectionLeft> {};
struct Unidirectional : public Direction<DirectionLeft> {};
struct Pattern : public AstNode<Pattern>
{
Pattern(Node* node, Relationship* relationship, Pattern* next)
: node(node), relationship(relationship), next(next) {}
Node* node;
Relationship* relationship;
Pattern* next;
};

22
cypher/ast/queries.hpp Normal file
View File

@ -0,0 +1,22 @@
#ifndef MEMGRAPH_CYPHER_AST_QUERIES_HPP
#define MEMGRAPH_CYPHER_AST_QUERIES_HPP
#include "ast_node.hpp"
#include "match.hpp"
#include "return.hpp"
namespace ast
{
struct ReadQuery : public AstNode<ReadQuery>
{
ReadQuery(Match* match, ReturnList* return_list)
: match(match), return_list(return_list) {}
Match* match;
ReturnList* return_list;
};
}
#endif

View File

@ -24,7 +24,13 @@ struct RelationshipSpecs : public AstNode<RelationshipSpecs>
struct Relationship : public AstNode<Relationship>
{
Relationship()
enum Direction { Left, Right, Both };
Relationship(RelationshipSpecs* specs, Direction direction)
: specs(specs), direction(direction) {}
RelationshipSpecs* specs;
Direction direction;
};
}

20
cypher/ast/start.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef MEMGRAPH_CYPHER_AST_START_HPP
#define MEMGRAPH_CYPHER_AST_START_HPP
#include "ast_node.hpp"
#include "queries.hpp"
namespace ast
{
struct Start : public AstNode<Start>
{
Start(ReadQuery* read_query)
: read_query(read_query) {}
ReadQuery* read_query;
};
};
#endif

View File

@ -13,7 +13,7 @@ class Ast
public:
Ast() {}
AstVisitable::uptr root;
AstVisitable* root;
void traverse(AstVisitor& visitor)
{
@ -32,7 +32,7 @@ private:
// basically a gc vector that destroys all ast nodes once this object is
// destroyed. parser generator is written in c and works only with raw
// pointers so this is what makes it leak free after the parser finishes
// parsing
// parsing without using shared pointers
std::vector<AstVisitable::uptr> items;
};

20
cypher/ast/where.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef MEMGRAPH_CYPHER_AST_WHERE_HPP
#define MEMGRAPH_CYPHER_AST_WHERE_HPP
#include "ast_node.hpp"
#include "expr.hpp"
namespace ast
{
struct Where : public AstNode<Where>
{
Where(Expr* expr)
: expr(expr) {}
Expr* expr;
};
}
#endif

View File

@ -13,13 +13,12 @@
%syntax_error
{
std::cout << "syntax error near '" << TOKEN->value << "'." << std::endl;
throw "";
throw SyntaxError(TOKEN->value);
}
%stack_overflow
{
std::cout << "parser stack overflow" << std::endl;
throw ParserError("Parser stack overflow");
}
%name cypher_parser
@ -31,6 +30,7 @@
#include <cstdlib>
#include "token.hpp"
#include "errors.hpp"
#include "ast/ast.hpp"
#include "ast/tree.hpp"
@ -46,33 +46,45 @@
%left PLUS MINUS.
%left STAR SLASH REM.
start ::= read_query.
start ::= read_query(RQ). {
ast->root = ast->create<ast::Start>(RQ);
}
read_query ::= match_clause return_clause.
%type read_query {ast::ReadQuery*}
match_clause ::= MATCH pattern where_clause.
read_query(RQ) ::= match_clause(M) return_clause(R). {
RQ = ast->create<ast::ReadQuery>(M, R);
}
%type match_clause {ast::Match*}
match_clause(M) ::= MATCH pattern(P) where_clause(W). {
M = ast->create<ast::Match>(P, W);
}
%type pattern {ast::Pattern*}
// pattern specification
pattern ::= node rel pattern. {
pattern(P) ::= node(N) rel(R) pattern(NEXT). {
P = ast->create<ast::Pattern>(N, R, NEXT);
}
pattern ::= node. {
pattern(P) ::= node(N). {
P = ast->create<ast::Pattern>(N, nullptr, nullptr);
}
rel ::= MINUS rel_spec MINUS. { // unidirectional
%type rel {ast::Relationship*}
rel(R) ::= MINUS rel_spec(S) MINUS. { // unidirectional
R = ast->create<ast::Relationship>(S, ast::Relationship::Both);
}
rel ::= LT MINUS rel_spec MINUS { // left
rel(R) ::= LT MINUS rel_spec(S) MINUS. { // left
R = ast->create<ast::Relationship>(S, ast::Relationship::Left);
}
rel(R) ::= MINUS rel_spec(S) MINUS GT { // right
R = ast->create<ast::Relationship>(
rel(R) ::= MINUS rel_spec(S) MINUS GT. { // right
R = ast->create<ast::Relationship>(S, ast::Relationship::Right);
}
%type rel_spec {ast::RelationshipSpecs*}
@ -148,11 +160,11 @@ label_idn(L) ::= . {
L = nullptr;
}
%type where_clause {ast::Expr*}
%type where_clause {ast::Where*}
// where clause
where_clause(W) ::= WHERE expr(E). {
W = E;
W = ast->create<ast::Where>(E);
}
where_clause(W) ::= . {

26
cypher/errors.hpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef MEMGRAPH_CYPHER_ERRORS_HPP
#define MEMGRAPH_CYPHER_ERRORS_HPP
#include <stdexcept>
#include "token.hpp"
class SyntaxError : public std::runtime_error
{
public:
SyntaxError(const std::string& near)
: std::runtime_error("Syntax error near '" + near + "'.") {}
};
class LexicalError : public std::runtime_error
{
public:
LexicalError(const Token& token)
: std::runtime_error("Unrecognized token '" + token.value + "'.") {}
};
class ParserError : public std::runtime_error
{
using runtime_error::runtime_error;
};
#endif

View File

@ -3,16 +3,21 @@
#include <cstdint>
// unfortunatelly, lexertl uses some stuff deprecated in c++11 so we get some
// warnings during compile time, mainly for the auto_ptr
// auto_ptr<lexertl::detail::basic_re_token<char, char> > is deprecated
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include "lexertl/generator.hpp"
#include "lexertl/lookup.hpp"
#pragma GCC diagnostic pop
#include "lexical_error.hpp"
#include "errors.hpp"
#include "token.hpp"
class Lexer
{
public:
class Tokenizer
{
public:

View File

@ -1,15 +0,0 @@
#ifndef MEMGRAPH_CYPHER_LEXER_LEXICAL_ERROR_HPP
#define MEMGRAPH_CYPHER_LEXER_LEXICAL_ERROR_HPP
#include <stdexcept>
#include "token.hpp"
class LexicalError : public std::runtime_error
{
public:
LexicalError(const Token& token)
: std::runtime_error("Unrecognized token '" + token.value + "'.") {}
};
#endif

View File

@ -0,0 +1,108 @@
#ifndef MEMGRAPH_DATA_STRUCTURES_LIST_LOCKFREE_LIST_HPP
#define MEMGRAPH_DATA_STRUCTURES_LIST_LOCKFREE_LIST_HPP
#include <atomic>
#include <memory>
#include <unistd.h>
namespace lockfree
{
template <class T, size_t sleep_time = 250>
class List
{
struct Node
{
T item;
std::shared_ptr<Node> next;
};
public:
List() = default;
// the default destructor is recursive so it could blow the stack if the
// list is long enough. the head node is destructed first via a shared_ptr
// and then it automatically destructs the second node and the second
// destructs the third and so on. keep that in mind
~List() = default;
List(List&) = delete;
void operator=(List&) = delete;
class iterator
{
public:
iterator(std::shared_ptr<Node> ptr) : ptr(ptr) {}
T& operator*() { return ptr->item; }
T* operator->() { return &ptr->item; }
iterator& operator++()
{
if(ptr)
ptr = ptr->next;
return *this;
}
iterator& operator++(int)
{
operator++();
return *this;
}
private:
std::shared_ptr<Node> ptr;
};
iterator begin() { return iterator(std::move(head.load())); }
iterator end() { return iterator(nullptr); }
auto find(T item)
{
auto p = head.load();
while(p && p->item != item)
p = p->next;
return iterator(std::move(p));
}
iterator push_front(T item)
{
auto p = std::make_shared<Node>();
p->item = item;
p->next = std::move(head.load());
while(!head.compare_exchange_weak(p->next, p))
usleep(sleep_time);
return iterator(p);
}
iterator pop_front()
{
auto p = head.load();
auto q = p;
while(p && !head.compare_exchange_weak(p, p->next))
{
q = p;
usleep(sleep_time);
}
return iterator(q);
}
// TODO think about how to make this lock free and safe from ABA
// this can easily be thread safe if there is ONLY ONE concurrent
// remove operation
//bool remove(T item);
private:
std::atomic<std::shared_ptr<Node>> head { nullptr };
};
};
#endif

View File

@ -0,0 +1,71 @@
#ifndef MEMGRAPH_DATA_STRUCTURES_LOCKFREE_LIST2_HPP
#define MEMGRAPH_DATA_STRUCTURES_LOCKFREE_LIST2_HPP
#include <atomic>
#include "threading/sync/spinlock.hpp"
#include "threading/sync/lockable.hpp"
namespace lockfree
{
template <class T>
class List2 : Lockable<SpinLock>
{
struct Node { T value; std::atomic<Node*> next; };
class iterator
{
iterator(Node* node) : node(node) {}
T& operator*() const { return *node; }
T* operator->() const { return node; }
bool end() const
{
return node == nullptr;
}
iterator& operator++()
{
node = node->next.load(std::memory_order_relaxed);
return *this;
}
iterator& operator++(int)
{
return operator++();
}
protected:
Node* node;
};
public:
~List2()
{
Node* next, node = head.load(std::memory_order_relaxed);
for(; node != nullptr; node = next)
{
next = node->next;
delete node;
}
}
void insert(T value)
{
auto guard = acquire();
head.store(new Node { value, head });
}
iterator begin() { return iterator(head.load()); }
private:
std::atomic<Node*> head;
};
}
#endif

View File

@ -84,11 +84,9 @@ public:
lfound = level;
preds[level] = pred;
succs[level] = node; // TODO what's FB doing here?
succs[level] = node;
}
//std::cout << "lfound = " << lfound << std::endl;
return lfound;
}

View File

@ -5,7 +5,7 @@
#include <atomic>
#include <mutex>
#include "sync/spinlock.hpp"
#include "threading/sync/spinlock.hpp"
// concurrent skiplist node based on the implementation described in
// "A Provably Correct Scalable Concurrent Skip List"

View File

@ -3,30 +3,29 @@
#include <stack>
#include "utils/sync/spinlock.hpp"
#include "threading/sync/spinlock.hpp"
#include "threading/sync/lockable.hpp"
template <class T>
class SpinLockStack
class SpinLockStack : Lockable<SpinLock>
{
public:
T pop()
{
lock.acquire();
auto guard = acquire();
T elem = stack.top();
stack.pop();
lock.release();
return elem;
}
void push(const T& elem)
{
lock.acquire();
auto guard = acquire();
stack.push(elem);
lock.release();
}
private:

View File

@ -1,7 +1,7 @@
#include <iostream>
#include <thread>
#include "sync/spinlock.hpp"
#include "threading/sync/spinlock.hpp"
#include "transaction/transaction_engine.hpp"
#include "memory/memory_engine.hpp"

View File

@ -1,10 +1,9 @@
#ifndef MEMGRAPH_MEMORY_MEMORY_ENGINE_HPP
#define MEMGRAPH_MEMORY_MEMORY_ENGINE_HPP
#ifndef MEMGRAPH_MEMORY_MEMORY_HPP
#define MEMGRAPH_MEMORY_MEMORY_HPP
#include <atomic>
#include <mutex>
#include "transaction/transaction.hpp"
#include "storage/model/record.hpp"
#include "storage/model/vertex.hpp"
#include "storage/model/edge.hpp"

57
mvcc/atom.hpp Normal file
View File

@ -0,0 +1,57 @@
#ifndef MEMGRAPH_MVCC_ATOM_HPP
#define MEMGRAPH_MVCC_ATOM_HPP
#include "threading/sync/lockable.hpp"
#include "transaction.hpp"
#include "version.hpp"
namespace mvcc
{
template <class T>
class Atom : public Version<T>,
public Lockable<>
{
public:
Atom(uint64_t id, T* first)
: Version<T>(first), id(id)
{
// it's illegal that the first version is nullptr. there should be at
// least one version of a record
assert(first != nullptr);
}
T* first()
{
return this->newer();
}
// inspects the record change history and returns the record version visible
// to the current transaction if it exists, otherwise it returns nullptr
T* latest_visible(const Transaction& t)
{
return first()->latest_visible(t);
}
// inspects the record change history and returns the newest available
// version from all transactions
T& newest_available()
{
T* record = this->newer(), newer = this->newer();
while(newer != nullptr)
record = newer, newer = record->newer();
return record;
}
// every record has a unique id. 2^64 = 1.8 x 10^19. that should be enough
// for a looong time :) but keep in mind that some vacuuming would be nice
// to reuse indices for deleted nodes.
uint64_t id;
};
}
#endif

View File

@ -5,8 +5,8 @@
#include <boost/dynamic_bitset/dynamic_bitset.hpp>
#include "sync/spinlock.hpp"
#include "sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
#include "threading/sync/lockable.hpp"
// optimize allocation performance by preallocating chunks in like 2MB or so
// optimize memory by purging old transactions after vacuuming or something

View File

@ -1,8 +1,11 @@
#ifndef MEMGRAPH_STORAGE_MODEL_UTILS_MINMAX_HPP
#define MEMGRAPH_STORAGE_MODEL_UTILS_MINMAX_HPP
#ifndef MEMGRAPH_MVCC_MINMAX_HPP
#define MEMGRAPH_MVCC_MINMAX_HPP
#include <atomic>
namespace mvcc
{
template <class T>
class MinMax
{
@ -35,4 +38,6 @@ private:
std::atomic<T> min_, max_;
};
}
#endif

View File

@ -3,15 +3,16 @@
#include <atomic>
#include "transaction/transaction.hpp"
#include "storage/model/utils/minmax.hpp"
#include "storage/model/utils/version.hpp"
#include "transaction/commit_log.hpp"
#include "transaction.hpp"
#include "minmax.hpp"
#include "version.hpp"
// the mvcc implementation used here is very much like postgresql's
// more info: https://momjian.us/main/writings/pgsql/mvcc.pdf
namespace mvcc
{
template <class T>
class Mvcc : public Version<T>
{
@ -62,7 +63,7 @@ public:
// inspects the record change history and returns the record version visible
// to the current transaction if it exists, otherwise it returns nullptr
T* latest(const Transaction& t)
T* latest_visible(const Transaction& t)
{
T* record = this, newer = this->newer();
@ -75,6 +76,18 @@ public:
return record;
}
void mark_created(const Transaction& t)
{
tx.min(t.id);
cmd.min(t.cid);
}
void mark_deleted(const Transaction& t)
{
tx.max(t.id);
cmd.max(t.cid);
}
protected:
// known committed and known aborted for both xmax and xmin
// this hints are used to quickly check the commit/abort status of the
@ -84,4 +97,6 @@ protected:
std::atomic<uint8_t> hints;
};
}
#endif

85
mvcc/store.hpp Normal file
View File

@ -0,0 +1,85 @@
#ifndef MEMGRAPH_STORAGE_MVCC_STORE_HPP
#define MEMGRAPH_STORAGE_MVCC_STORE_HPP
#include <stdexcept>
#include "mvcc/transaction.hpp"
#include "mvcc/atom.hpp"
#include "data_structures/list/lockfree_list.hpp"
#include "utils/counters/atomic_counter.hpp"
// some interesting concepts described there, keep in mind for the future
// Serializable Isolation for Snapshot Databases, J. Cahill, et al.
namespace mvcc
{
class MvccError : public std::runtime_error
{
public:
using runtime_error::runtime_error;
};
template <class T>
class MvccStore
{
using list_t = lockfree::List<Atom<T>>;
public:
using iterator = typename list_t::iterator;
MvccStore() : counter(0) {}
iterator insert(const Transaction& t)
{
auto record = new T();
record->mark_created(t);
return data.push_front(Atom<T>(counter.next(), record));
}
T* update(Atom<T>& atom, T& record, const Transaction& t)
{
auto guard = atom.acquire();
// if xmax is not zero, that means there is a newer version of this
// record or it has been deleted. we cannot do anything here
if(record.tx.max())
throw MvccError("can't serialize due to concurrent operation(s)");
// make a new version
auto updated = new T();
*updated = *record;
// mark the new version as created
updated->mark_created(t);
// mark the current version as deleted
record.mark_deleted(t);
record.newer(updated);
return updated;
}
void remove(Atom<T>& atom, T& record, const Transaction& t)
{
auto guard = atom.acquire();
// if xmax is not zero, that means there is a newer version of this
// record or it has been deleted. we cannot do anything here
if(record.tx.max())
throw MvccError("can't serialize due to concurrent operation(s)");
record.mark_deleted(t);
}
private:
AtomicCounter<uint64_t> counter;
lockfree::List<Atom<T>> data;
};
}
#endif

View File

@ -1,11 +1,11 @@
#ifndef MEMGRAPH_TRANSACTION_TRANSACTION_HPP
#define MEMGRAPH_TRANSACTION_TRANSACTION_HPP
#ifndef MEMGRAPH_MVCC_TRANSACTION_HPP
#define MEMGRAPH_MVCC_TRANSACTION_HPP
#include <cstdlib>
#include <cstdint>
#include <vector>
#include "transaction/commit_log.hpp"
#include "commit_log.hpp"
struct Transaction
{

View File

@ -1,5 +1,5 @@
#ifndef MEMGRAPH_TRANSACTION_TRANSACTIONENGINE_HPP
#define MEMGRAPH_TRANSACTION_TRANSACTIONENGINE_HPP
#ifndef MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
#define MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
#include <cstdlib>
#include <atomic>
@ -12,8 +12,8 @@
#include "transaction.hpp"
#include "utils/counters/simple_counter.hpp"
#include "sync/spinlock.hpp"
#include "sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
#include "threading/sync/lockable.hpp"
class TransactionEngine : Lockable<SpinLock>
{
@ -47,7 +47,6 @@ public:
finalize(t);
}
// id of the last finished transaction
uint64_t last_known_active()
{
auto guard = this->acquire();

View File

@ -1,8 +1,11 @@
#ifndef MEMGRAPH_STORAGE_MODEL_UTILS_VERSION_HPP
#define MEMGRAPH_STORAGE_MODEL_UTILS_VERSION_HPP
#ifndef MEMGRAPH_MVCC_VERSION_HPP
#define MEMGRAPH_MVCC_VERSION_HPP
#include <atomic>
namespace mvcc
{
template <class T>
class Version
{
@ -31,4 +34,6 @@ private:
std::atomic<T*> versions;
};
}
#endif

View File

@ -70,7 +70,11 @@ UvBuffer& UvBuffer::append(const char* data, size_t n)
buffer.base = static_cast<char*>(realloc(buffer.base, new_size));
}
std::memcpy(buffer.base + size(), data, n);
auto ptr = buffer.base + size();
for(size_t i = 0; i < n; ++i, ++ptr, ++data)
*ptr = *data;
buffer.len = new_size;
return *this;

View File

@ -1,14 +1,12 @@
#ifndef MEMGRAPH_DATA_MODEL_EDGE_HPP
#define MEMGRAPH_DATA_MODEL_EDGE_HPP
#ifndef MEMGRAPH_STORAGE_EDGE_HPP
#define MEMGRAPH_STORAGE_EDGE_HPP
#include <vector>
#include "record.hpp"
#include "model/record.hpp"
struct Vertex;
struct
struct Edge : public Record<Edge>
{
Vertex* from;

33
storage/graph.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef MEMGRAPH_STORAGE_GRAPH_HPP
#define MEMGRAPH_STORAGE_GRAPH_HPP
#include <list>
#include "mvcc/atom.hpp"
#include "mvcc/store.hpp"
#include "vertex.hpp"
#include "edge.hpp"
using VertexStore = mvcc::MvccStore<Vertex>;
using EdgeStore = mvcc::MvccStore<Edge>;
class Graph
{
public:
Graph() {}
EdgeStore::iterator connect(Vertex a, Vertex b, const Transaction& t)
{
auto it = edges.insert(t);
it->
return it;
}
VertexStore vertices;
EdgeStore edges;
};
#endif

View File

@ -1,16 +0,0 @@
#ifndef MEMGRAPH_DATA_MODEL_GRAPH_HPP
#define MEMGRAPH_DATA_MODEL_GRAPH_HPP
#include <list>
#include "vertex.hpp"
#include "edge.hpp"
class Graph
{
private:
std::list<
};
#endif

View File

@ -5,7 +5,7 @@
#include "property.hpp"
namespace props
namespace model
{
class Properties

View File

@ -4,7 +4,7 @@
#include <memory>
#include <string>
namespace props
namespace model
{
class Property

View File

@ -3,7 +3,7 @@
#include "property.hpp"
namespace props
namespace model
{
class String : public Value<std::string>

View File

@ -2,24 +2,27 @@
#define MEMGRAPH_STORAGE_RECORD_HPP
#include <mutex>
#include <set>
#include "utils/crtp.hpp"
#include "sync/spinlock.hpp"
#include "sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
#include "storage/model/utils/mvcc.hpp"
#include "mvcc/mvcc.hpp"
#include "properties/properties.hpp"
template <class Derived>
class Record
: public Crtp<Derived>,
public Mvcc<Derived>,
Lockable<SpinLock>
public mvcc::Mvcc<Derived>
{
public:
Properties props;
// a record contains a key value map containing data
model::Properties properties;
// each record can have one or more distinct labels.
std::set<uint16_t> labels;
};
#endif

View File

@ -1,19 +0,0 @@
#ifndef MEMGRAPH_STORAGE_MODEL_ROOT_HPP
#define MEMGRAPH_STORAGE_MODEL_ROOT_HPP
#include "utils/version.hpp"
template <class T>
class Root : public Version<T>
{
public:
Root(uint64_t id, T* first)
: Version<T>(first), id(id) {}
// every record has a unique id. 2^64 = 1.8 x 10^19. that should be enough
// for a looong time :) but keep in mind that some vacuuming would be nice
// to reuse indices for deleted nodes.
uint64_t id;
};
#endif

View File

@ -9,6 +9,8 @@
#include "memory/memory_engine.hpp"
#include "utils/counters/atomic_counter.hpp"
#include "storage/model/root.hpp"
class StorageEngine
{
public:

View File

@ -1,9 +1,9 @@
#ifndef MEMGRAPH_STORAGE_MODEL_VERTEX_HPP
#define MEMGRAPH_STORAGE_MODEL_VERTEX_HPP
#ifndef MEMGRAPH_STORAGE_VERTEX_HPP
#define MEMGRAPH_STORAGE_VERTEX_HPP
#include <vector>
#include "record.hpp"
#include "model/record.hpp"
#include "edge.hpp"
struct Vertex : public Record<Vertex>

View File

@ -1,5 +1,5 @@
#ifndef MEMGRAPH_UTILS_SYNC_SPINLOCK_HPP
#define MEMGRAPH_UTILS_SYNC_SPINLOCK_HPP
#ifndef MEMGRAPH_THREADING_SYNC_SPINLOCK_HPP
#define MEMGRAPH_THREADING_SYNC_SPINLOCK_HPP
#include <atomic>
#include <unistd.h>

View File

@ -0,0 +1,55 @@
#ifndef MEMGRAPH_THREADING_SYNC_TIMED_SPINLOCK_HPP
#define MEMGRAPH_THREADING_SYNC_TIMED_SPINLOCK_HPP
#include <atomic>
#include <chrono>
#include <stdexcept>
#include <unistd.h>
class LockExpiredError : public std::runtime_error
{
using runtime_error::runtime_error;
};
class TimedSpinLock
{
public:
TimedSpinLock(std::chrono::seconds expiration)
: expiration(expiration) {}
TimedSpinLock(std::chrono::milliseconds expiration)
: expiration(expiration) {}
void lock()
{
using clock = std::chrono::high_resolution_clock;
auto start = clock::now();
while(lock_flag.test_and_set(std::memory_order_acquire))
{
// how long have we been locked? if we exceeded the expiration
// time, throw an exception and stop being blocked because this
// might be a deadlock!
if(clock::now() - start > expiration)
throw LockExpiredError("This lock has expired");
usleep(250);
}
}
void unlock()
{
lock_flag.clear(std::memory_order_release);
}
private:
std::chrono::milliseconds expiration;
// guaranteed by standard to be lock free!
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
};
#endif

47
utils/buffer.hpp Normal file
View File

@ -0,0 +1,47 @@
#ifndef MEMGRAPH_UTILS_STRING_BUFFER_HPP
#define MEMGRAPH_UTILS_STRING_BUFFER_HPP
#include <string>
#include "numerics/ceil.hpp"
class Buffer
{
public:
Buffer(size_t capacity, size_t chunk_size)
: capacity(capacity), chunk_size(chunk_size) {}
Buffer& append(const std::string& string)
{
return this->append(string.c_str(), string.size());
}
Buffer& append(const char* string, size_t n)
{
auto new_size = size() + n;
if(capacity < new_size)
{
capacity = new_size;
data = static_cast<char*>(realloc(data, new_size));
}
size = new_size;
}
Buffer& operator<<(const std::string& string)
{
}
size_t size() const
{
return str.size();
}
private:
size_t size_, capacity, chunk_size;
char* data;
};
#endif

View File

@ -11,7 +11,7 @@ public:
T next()
{
return counter.fetch_add(1, std::memory_order_relaxed);
return counter.fetch_add(1);
}
private:

View File

@ -0,0 +1,29 @@
#ifndef MEMGRAPH_UTILS_MEMORY_ATOMIC_SHARED_PTR_HPP
#define MEMGRAPH_UTILS_MEMORY_ATOMIC_SHARED_PTR_HPP
#include <atomic>
#include <memory>
template<class T>
class atomic_shared_ptr final
{
public:
atomic_shared_ptr(std::shared_ptr<T>&& ptr)
: ptr(ptr) {}
std::shared_ptr<T> load()
{
return std::move(std::atomic_load(&ptr));
}
bool compare_exchange_weak(std::shared_ptr<T>& expected,
std::shared_ptr<T> desired)
{
return atomic_compare_exchange_weak(&ptr, &expected, desired);
}
private:
std::shared_ptr<T> ptr;
};
#endif

21
utils/numerics/ceil.hpp Normal file
View File

@ -0,0 +1,21 @@
#ifndef MEMGRAPH_UTILS_NUMERICS_COMMON_HPP
#define MEMGRAPH_UTILS_NUMERICS_COMMON_HPP
#include <type_traits>
namespace num
{
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
T iceil(T x, T y)
{
// this may seem inefficient, but on x86_64, when you already perform
// division (x / y) the remainder is already computed and therefore x % y
// is basically free!
return x / y + (x % y != 0);
}
}
#endif