This commit is contained in:
sale
2017-01-11 09:18:10 +00:00
100 changed files with 4750 additions and 1853 deletions

View File

@@ -7,8 +7,11 @@ namespace bolt
namespace config
{
static constexpr size_t N = 65535; /* chunk size */
static constexpr size_t C = N + 2; /* end mark */
/** chunk size */
static constexpr size_t N = 65535;
/** end mark */
static constexpr size_t C = N + 2;
}
}

View File

@@ -5,17 +5,38 @@ namespace bolt
enum class PackType
{
Null, // denotes absence of a value
Boolean, // denotes a type with two possible values (t/f)
Integer, // 64-bit signed integral number
Float, // 64-bit floating point number
Bytes, // binary data
String, // unicode string
List, // collection of values
Map, // collection of zero or more key/value pairs
Struct, // zero or more packstream values
EndOfStream, // denotes stream value end
Reserved // reserved for future use
/** denotes absence of a value */
Null,
/** denotes a type with two possible values (t/f) */
Boolean,
/** 64-bit signed integral number */
Integer,
/** 64-bit floating point number */
Float,
/** binary data */
Bytes,
/** unicode string */
String,
/** collection of values */
List,
/** collection of zero or more key/value pairs */
Map,
/** zero or more packstream values */
Struct,
/** denotes stream value end */
EndOfStream,
/** reserved for future use */
Reserved
};
}

View File

@@ -27,7 +27,7 @@ class BoltSerializer
public:
BoltSerializer(Stream &stream) : encoder(stream) {}
/* Serializes the vertex accessor into the packstream format
/** Serializes the vertex accessor into the packstream format
*
* struct[size = 3] Vertex [signature = 0x4E] {
* Integer node_id;
@@ -64,7 +64,7 @@ public:
}
}
/* Serializes the vertex accessor into the packstream format
/** Serializes the vertex accessor into the packstream format
*
* struct[size = 5] Edge [signature = 0x52] {
* Integer edge_id;
@@ -79,7 +79,7 @@ public:
void write_null() { encoder.write_null(); }
void write(const Null &v) { encoder.write_null(); }
void write(const Null &) { encoder.write_null(); }
void write(const Bool &prop) { encoder.write_bool(prop.value()); }

View File

@@ -10,9 +10,10 @@
namespace bolt
{
// compiled queries have to use this class in order to return results
// query code should not know about bolt protocol
/**
* compiled queries have to use this class in order to return results
* query code should not know about bolt protocol
*/
template <class Socket>
class RecordStream
{

View File

@@ -1,4 +1,6 @@
#pragma once
/* Memgraph Communication protocol
/* Memgraph communication protocol
* gate is the first name proposal for the protocol */
// TODO

View File

@@ -1,3 +1,3 @@
#pragma once
/* HTTP & HTTPS implementation */
/* TODO: HTTP & HTTPS implementations */

View File

@@ -1,67 +1,76 @@
#pragma once
#include <bitset>
#include <iostream>
#include <vector>
/*
Implementation of a generic Bloom Filter.
Read more about bloom filters here:
http://en.wikipedia.org/wiki/Bloom_filter
http://www.jasondavies.com/bloomfilter/
*/
// Type specifies the type of data stored
/**
* Implementation of a generic Bloom Filter.
* Read more about bloom filters here:
* http://en.wikipedia.org/wiki/Bloom_filter
* http://www.jasondavies.com/bloomfilter/
*
* Type specifies the type of data stored
*/
template <class Type, int BucketSize = 8>
class BloomFilter {
private:
using HashFunction = std::function<uint64_t(const Type&)>;
using CompresionFunction = std::function<int(uint64_t)>;
class BloomFilter
{
private:
using HashFunction = std::function<uint64_t(const Type &)>;
using CompresionFunction = std::function<int(uint64_t)>;
std::bitset<BucketSize> filter_;
std::vector<HashFunction> hashes_;
CompresionFunction compression_;
std::vector<int> buckets;
std::bitset<BucketSize> filter_;
std::vector<HashFunction> hashes_;
CompresionFunction compression_;
std::vector<int> buckets;
int default_compression(uint64_t hash) { return hash % BucketSize; }
int default_compression(uint64_t hash) { return hash % BucketSize; }
void get_buckets(const Type& data) {
for (int i = 0; i < hashes_.size(); i++)
buckets[i] = compression_(hashes_[i](data));
}
void print_buckets(std::vector<uint64_t>& buckets) {
for (int i = 0; i < buckets.size(); i++) {
std::cout << buckets[i] << " ";
void get_buckets(const Type &data)
{
for (int i = 0; i < hashes_.size(); i++)
buckets[i] = compression_(hashes_[i](data));
}
std::cout << std::endl;
}
public:
BloomFilter(std::vector<HashFunction> funcs,
CompresionFunction compression = {})
: hashes_(funcs) {
if (!compression)
compression_ = std::bind(&BloomFilter::default_compression, this,
std::placeholders::_1);
else
compression_ = compression;
void print_buckets(std::vector<uint64_t> &buckets)
{
for (int i = 0; i < buckets.size(); i++)
{
std::cout << buckets[i] << " ";
}
std::cout << std::endl;
}
buckets.resize(hashes_.size());
}
public:
BloomFilter(std::vector<HashFunction> funcs,
CompresionFunction compression = {})
: hashes_(funcs)
{
if (!compression)
compression_ = std::bind(&BloomFilter::default_compression, this,
std::placeholders::_1);
else
compression_ = compression;
bool contains(const Type& data) {
get_buckets(data);
bool contains_element = true;
buckets.resize(hashes_.size());
}
for (int i = 0; i < buckets.size(); i++)
contains_element &= filter_[buckets[i]];
bool contains(const Type &data)
{
get_buckets(data);
bool contains_element = true;
return contains_element;
}
for (int i = 0; i < buckets.size(); i++)
contains_element &= filter_[buckets[i]];
void insert(const Type& data) {
get_buckets(data);
return contains_element;
}
for (int i = 0; i < buckets.size(); i++) filter_[buckets[i]] = true;
}
void insert(const Type &data)
{
get_buckets(data);
for (int i = 0; i < buckets.size(); i++)
filter_[buckets[i]] = true;
}
};

View File

@@ -70,7 +70,7 @@ private:
{
assert(list != nullptr);
// Increment number of iterators accessing list.
list->count++;
list->active_threads_no_++;
// Start from the begining of list.
reset();
}
@@ -99,7 +99,7 @@ private:
// Fetch could be relaxed
// There exist possibility that no one will delete garbage at this
// time but it will be deleted at some other time.
if (list->count.fetch_sub(1) == 1 && // I am the last one accessing
if (list->active_threads_no_.fetch_sub(1) == 1 && // I am the last one accessing
head_rem != nullptr && // There is some garbage
cas<Node *>(list->removed, head_rem,
nullptr) // No new garbage was added.
@@ -177,6 +177,8 @@ private:
store(node->next, next);
// Then try to set as head.
} while (!cas(list->head, next, node));
list->count_.fetch_add(1);
}
// True only if this call removed the element. Only reason for fail is
@@ -200,6 +202,7 @@ private:
}
// Add to list of to be garbage collected.
store(curr->next_rem, swap(list->removed, curr));
list->count_.fetch_sub(1);
return true;
}
return false;
@@ -321,10 +324,14 @@ public:
ConstIterator cend() { return ConstIterator(); }
std::size_t size() { return count.load(std::memory_order_consume); }
std::size_t active_threads_no() { return active_threads_no_.load(); }
std::size_t size() { return count_.load(); }
private:
std::atomic<std::size_t> count{0};
// TODO: use lazy GC or something else as a garbage collection strategy
// use the same principle as in skiplist
std::atomic<std::size_t> active_threads_no_{0};
std::atomic<std::size_t> count_{0};
std::atomic<Node *> head{nullptr};
std::atomic<Node *> removed{nullptr};
};

View File

@@ -5,9 +5,12 @@
using std::pair;
// Multi thread safe map based on skiplist.
// K - type of key.
// T - type of data.
/**
* Multi thread safe map based on skiplist.
*
* @tparam K is a type of key.
* @tparam T is a type of data.
*/
template <typename K, typename T>
class ConcurrentMap
{

View File

@@ -5,9 +5,12 @@
using std::pair;
// Multi thread safe multi map based on skiplist.
// K - type of key.
// T - type of data.
/**
* Multi thread safe multi map based on skiplist.
*
* @tparam K is a type of key.
* @tparam T is a type of data.
*/
template <typename K, typename T>
class ConcurrentMultiMap
{

View File

@@ -12,7 +12,7 @@
#include "data_structures/concurrent/skiplist_gc.hpp"
/* @brief Concurrent lock-based skiplist with fine grained locking
/** @brief Concurrent lock-based skiplist with fine grained locking
*
* From Wikipedia:
* "A skip list is a data structure that allows fast search within an
@@ -97,11 +97,13 @@ template <class T, size_t H = 32, class lock_t = SpinLock>
class SkipList : private Lockable<lock_t>
{
public:
// computes the height for the new node from the interval [1...H]
// with p(k) = (1/2)^k for all k from the interval
/**
* computes the height for the new node from the interval [1...H]
* with p(k) = (1/2)^k for all k from the interval
*/
static thread_local FastBinomial<H> rnd;
/* @brief Wrapper class for flags used in the implementation
/** @brief Wrapper class for flags used in the implementation
*
* MARKED flag is used to logically delete a node.
* FULLY_LINKED is used to mark the node as fully inserted, i.e. linked
@@ -224,12 +226,14 @@ public:
Placeholder<T> data;
// this creates an array of the size zero. we can't put any sensible
// value here since we don't know what size it will be untill the
// node is allocated. we could make it a Node** but then we would
// have two memory allocations, one for node and one for the forward
// list. this way we avoid expensive malloc/free calls and also cache
// thrashing when following a pointer on the heap
/**
* this creates an array of the size zero. we can't put any sensible
* value here since we don't know what size it will be untill the
* node is allocated. we could make it a Node** but then we would
* have two memory allocations, one for node and one for the forward
* list. this way we avoid expensive malloc/free calls and also cache
* thrashing when following a pointer on the heap
*/
std::atomic<Node *> tower[0];
};
@@ -441,6 +445,7 @@ public:
}
private:
// TODO: figure why start is unused
static int update_path(SkipList *skiplist, int start, const K &item,
Node *preds[], Node *succs[])
{
@@ -664,14 +669,18 @@ private:
return (node == nullptr) || item < node->value();
}
// Returns first occurence of item if there exists one.
/**
* Returns first occurence of item if there exists one.
*/
template <class K>
ConstIterator find(const K &item) const
{
return const_cast<SkipList *>(this)->find_node<ConstIterator, K>(item);
}
// Returns first occurence of item if there exists one.
/**
* Returns first occurence of item if there exists one.
*/
template <class K>
Iterator find(const K &item)
{
@@ -689,7 +698,9 @@ private:
}
}
// Returns iterator on searched element or the first larger element.
/**
* Returns iterator on searched element or the first larger element.
*/
template <class It, class K>
It find_or_larger(const K &item)
{
@@ -758,8 +769,11 @@ private:
return valid;
}
// Inserts non unique data into list.
// NOTE: Uses modified logic from insert method.
/**
* Inserts non unique data into list.
*
* NOTE: Uses modified logic from insert method.
*/
Iterator insert_non_unique(T &&data, Node *preds[], Node *succs[])
{
while (true) {
@@ -823,9 +837,12 @@ private:
}
}
// Insert unique data
// F - type of funct which will create new node if needed. Recieves height
// of node.
/**
* Insert unique data
*
* F - type of funct which will create new node if needed. Recieves height
* of node.
*/
std::pair<Iterator, bool> insert(Node *preds[], Node *succs[], T &&data)
{
while (true) {
@@ -857,8 +874,11 @@ private:
}
}
// Insert unique data
// NOTE: This is almost all duplicate code from insert.
/**
* Insert unique data
*
* NOTE: This is almost all duplicate code from insert.
*/
template <class K, class... Args>
std::pair<Iterator, bool> emplace(Node *preds[], Node *succs[], K &key,
Args &&... args)
@@ -893,9 +913,11 @@ private:
}
}
// Inserts data to specified locked location.
/**
* Inserts data to specified locked location.
*/
Iterator insert_here(Node *new_node, Node *preds[], Node *succs[],
int height, guard_t guards[])
int height, guard_t guards[]) // TODO: querds unused
{
// Node::create(std::move(data), height)
// link the predecessors and successors, e.g.
@@ -921,10 +943,12 @@ private:
!node->flags.is_marked();
}
// Remove item found with fp with arguments skiplist,preds and succs.
// fp has to fill preds and succs which reflect location of item or return
// -1 as in not found otherwise returns level on which the item was first
// found.
/**
* Removes item found with fp with arguments skiplist, preds and succs.
* fp has to fill preds and succs which reflect location of item or return
* -1 as in not found otherwise returns level on which the item was first
* found.
*/
template <class K>
bool remove(const K &item, Node *preds[], Node *succs[],
int (*fp)(SkipList *, int, const K &, Node *[], Node *[]))
@@ -966,7 +990,9 @@ private:
}
}
// number of elements
/**
* number of elements
*/
std::atomic<size_t> count{0};
Node *header;
SkiplistGC<Node> gc;

View File

@@ -1,46 +0,0 @@
#pragma once
#include <list>
#include "threading/sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
template <typename value_type, typename lock_type = SpinLock>
class LinkedList : public Lockable<lock_type>
{
public:
std::size_t size() const
{
auto guard = this->acquire_unique();
return data.size();
}
void push_front(const value_type &value)
{
auto guard = this->acquire_unique();
data.push_front(value);
}
void push_front(value_type &&value)
{
auto guard = this->acquire_unique();
data.push_front(std::forward<value_type>(value));
}
void pop_front()
{
auto guard = this->acquire_unique();
data.pop_front();
}
// value_type& as return value
// would not be concurrent
value_type front()
{
auto guard = this->acquire_unique();
return data.front();
}
private:
std::list<value_type> data;
};

View File

@@ -1,34 +0,0 @@
#pragma once
#include <unordered_map>
#include "threading/sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
namespace lockfree
{
template <class K, class V>
class HashMap : Lockable<SpinLock>
{
public:
V at(const K& key)
{
auto guard = acquire_unique();
return hashmap[key];
}
void put(const K& key, const K& value)
{
auto guard = acquire_unique();
hashmap[key] = value;
}
private:
std::unordered_map<K, V> hashmap;
};
}

View File

@@ -10,44 +10,85 @@
class Indexes;
// Main class which represents Database concept in code.
// TODO: Maybe split this in another layer between Db and Dbms. Where the new
// layer would hold SnapshotEngine and his kind of concept objects. Some
// guidelines would be: retain objects which are necessary to implement querys
// in Db, the rest can be moved to the new layer.
/**
* Main class which represents Database concept in code.
*/
class Db
{
public:
using sptr = std::shared_ptr<Db>;
// import_snapshot will in constructor import latest snapshot into the db.
// NOTE: explicit is here to prevent compiler from evaluating const char *
// into a bool.
/**
* This constructor will create a database with the name "default"
*
* NOTE: explicit is here to prevent compiler from evaluating const char *
* into a bool.
*
* @param import_snapshot will in constructor import latest snapshot
* into the db.
*/
explicit Db(bool import_snapshot = true);
// import_snapshot will in constructor import latest snapshot into the db.
/**
* Construct database with a custom name.
*
* @param name database name
* @param import_snapshot will in constructor import latest snapshot
* into the db.
*/
Db(const char *name, bool import_snapshot = true);
// import_snapshot will in constructor import latest snapshot into the db.
/**
* Construct database with a custom name.
*
* @param name database name
* @param import_snapshot will in constructor import latest snapshot
* into the db.
*/
Db(const std::string &name, bool import_snapshot = true);
/**
* Database object can't be copied.
*/
Db(const Db &db) = delete;
private:
/** database name */
const std::string name_;
public:
/** transaction engine related to this database */
tx::Engine tx_engine;
/** graph related to this database */
Graph graph;
/** garbage collector related to this database*/
Garbage garbage = {tx_engine};
// This must be initialized after name.
/**
* snapshot engine related to this database
*
* \b IMPORTANT: has to be initialized after name
* */
SnapshotEngine snap_engine = {*this};
// Creates Indexes for this db.
/**
* Creates Indexes for this database.
*/
Indexes indexes();
// TODO: Indexes should be created only once somwhere Like Db or layer
// between Db and Dbms.
Indexes indexes();
/**
* Returns a name of the database.
*
* @return database name
*/
std::string const &name() const;
};

View File

@@ -5,14 +5,28 @@
#include "storage/label/label_store.hpp"
#include "storage/vertices.hpp"
/**
* Graph storage. Contains vertices and edges, labels and edges.
*/
class Graph
{
public:
Graph() {}
/**
* default constructor
*
* At the beginning the graph is empty.
*/
Graph() = default;
/** storage for all vertices related to this graph */
Vertices vertices;
/** storage for all edges related to this graph */
Edges edges;
/** storage for all labels */
LabelStore label_store;
/** storage for all types related for this graph */
EdgeTypeStore edge_type_store;
};

View File

@@ -25,9 +25,12 @@
// parmanant exception will always be executed
#define permanent_assert(condition, message) \
if (!(condition)) { \
if (!(condition)) \
{ \
std::ostringstream s; \
s << message; \
std::cout << s.str() << std::endl; \
std::exit(EXIT_FAILURE); \
}
// assert_error_handler_(__FILE__, __LINE__, s.str().c_str());

View File

@@ -2,33 +2,34 @@
#include <utility>
/* @brief Calls a cleanup function on scope exit
/**
* @brief Calls a cleanup function on scope exit
*
* consider this example:
* consider this example:
*
* void hard_worker()
* {
* resource.enable();
* do_stuff(); // throws exception
* resource.disable();
* }
* void hard_worker()
* {
* resource.enable();
* do_stuff(); // throws exception
* resource.disable();
* }
*
* if do_stuff throws an exception, resource.disable is never called
* and the app is left in an inconsistent state. ideally, you would like
* to call resource.disable regardles of the exception being thrown.
* OnScopeExit makes this possible and very convenient via a 'Auto' macro
* if do_stuff throws an exception, resource.disable is never called
* and the app is left in an inconsistent state. ideally, you would like
* to call resource.disable regardles of the exception being thrown.
* OnScopeExit makes this possible and very convenient via a 'Auto' macro
*
* void hard_worker()
* {
* resource.enable();
* Auto(resource.disable());
* do_stuff(); // throws exception
* }
* void hard_worker()
* {
* resource.enable();
* Auto(resource.disable());
* do_stuff(); // throws exception
* }
*
* now, resource.disable will be called every time it goes out of scope
* regardless of the exception
* now, resource.disable will be called every time it goes out of scope
* regardless of the exception
*
* @tparam F Lambda which holds a wrapper function around the cleanup code
* @tparam F Lambda which holds a wrapper function around the cleanup code
*/
template <class F>
class OnScopeExit
@@ -55,3 +56,10 @@ private:
TOKEN_PASTE(auto_, counter)(TOKEN_PASTE(auto_func_, counter));
#define Auto(Destructor) Auto_INTERNAL(Destructor, __COUNTER__)
// -- example:
// Auto(f());
// -- is expended to:
// auto auto_func_1 = [&]() { f(); };
// OnScopeExit<decltype(auto_func_1)> auto_1(auto_func_1);
// -- f() is called at the end of a scope

View File

@@ -4,39 +4,25 @@
#include <stdexcept>
#include "utils/auto_scope.hpp"
#include "utils/stacktrace.hpp"
#include "utils/stacktrace/stacktrace.hpp"
class BasicException : public std::exception {
public:
BasicException(const std::string &message, uint64_t stacktrace_size) noexcept
: message_(message),
stacktrace_size_(stacktrace_size) {
generate_stacktrace();
}
BasicException(const std::string &message) noexcept : message_(message),
stacktrace_size_(10) {
generate_stacktrace();
}
template <class... Args>
BasicException(const std::string &format, Args &&... args) noexcept
: BasicException(fmt::format(format, std::forward<Args>(args)...)) {}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
uint64_t stacktrace_size_;
void generate_stacktrace() {
#ifndef NDEBUG
Stacktrace stacktrace;
int size = std::min(stacktrace_size_, stacktrace.size());
for (int i = 0; i < size; i++) {
message_.append(fmt::format("\n at {} ({})", stacktrace[i].function,
stacktrace[i].location));
class BasicException : public std::exception
{
public:
BasicException(const std::string &message) noexcept : message_(message)
{
Stacktrace stacktrace;
message_.append(stacktrace.dump());
}
#endif
}
template <class... Args>
BasicException(const std::string &format, Args &&... args) noexcept
: BasicException(fmt::format(format, std::forward<Args>(args)...))
{
}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
};

View File

@@ -5,6 +5,9 @@
#include "utils/auto_scope.hpp"
/* @brief Allocates blocks of block_size and stores
* the pointers on allocated blocks inside a vector.
*/
template <size_t block_size>
class BlockAllocator
{
@@ -23,29 +26,45 @@ public:
BlockAllocator(size_t capacity = 0)
{
for (size_t i = 0; i < capacity; ++i)
blocks.emplace_back();
unused_.emplace_back();
}
~BlockAllocator()
{
for (auto b : blocks) {
free(b.data);
}
blocks.clear();
for (auto block : unused_)
free(block.data);
unused_.clear();
for (auto block : release_)
free(block.data);
release_.clear();
}
size_t unused_size() const
{
return unused_.size();
}
size_t release_size() const
{
return release_.size();
}
// Returns nullptr on no memory.
void *acquire()
{
if (blocks.size() == 0) blocks.emplace_back();
if (unused_.size() == 0) unused_.emplace_back();
auto ptr = blocks.back().data;
Auto(blocks.pop_back());
auto ptr = unused_.back().data;
Auto(unused_.pop_back());
return ptr;
}
void release(void *ptr) { blocks.emplace_back(ptr); }
void release(void *ptr) { release_.emplace_back(ptr); }
private:
std::vector<Block> blocks;
// TODO: try implement with just one vector
// but consecutive acquire release calls should work
// TODO: measure first!
std::vector<Block> unused_;
std::vector<Block> release_;
};

View File

@@ -3,6 +3,7 @@
#include <cmath>
#include "utils/exceptions/out_of_memory.hpp"
#include "utils/likely.hpp"
#include "utils/memory/block_allocator.hpp"
// http://en.cppreference.com/w/cpp/language/new

View File

@@ -8,34 +8,40 @@
using Function = std::function<void()>;
enum class Signal : int {
Terminate = SIGTERM,
SegmentationFault = SIGSEGV,
Interupt = SIGINT,
Quit = SIGQUIT,
Abort = SIGABRT
// TODO: align bits so signals can be combined
// Signal::Terminate | Signal::Interupt
enum class Signal : int
{
Terminate = SIGTERM,
SegmentationFault = SIGSEGV,
Interupt = SIGINT,
Quit = SIGQUIT,
Abort = SIGABRT,
BusError = SIGBUS,
};
class SignalHandler {
private:
static std::map<int, std::function<void()>> handlers_;
class SignalHandler
{
private:
static std::map<int, std::function<void()>> handlers_;
static void handle(int signal) { handlers_[signal](); }
static void handle(int signal) { handlers_[signal](); }
public:
static void register_handler(Signal signal, Function func) {
int signal_number = static_cast<int>(signal);
handlers_[signal_number] = func;
std::signal(signal_number, SignalHandler::handle);
}
// TODO possible changes if signelton needed later
/*
static SignalHandler& instance() {
static SignalHandler instance;
return instance;
public:
static void register_handler(Signal signal, Function func)
{
int signal_number = static_cast<int>(signal);
handlers_[signal_number] = func;
std::signal(signal_number, SignalHandler::handle);
}
*/
// TODO possible changes if signelton needed later
/*
static SignalHandler& instance() {
static SignalHandler instance;
return instance;
}
*/
};
std::map<int, std::function<void()>> SignalHandler::handlers_ = {};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "logging/default.hpp"
#include "utils/stacktrace/stacktrace.hpp"
void log_stacktrace(const std::string& title)
{
Stacktrace stacktrace;
logging::info(title);
logging::info(stacktrace.dump());
}

View File

@@ -1,10 +1,10 @@
#pragma once
#include <cxxabi.h>
#include <stdexcept>
#include <execinfo.h>
#include <fmt/format.h>
#include <stdexcept>
#include "utils/auto_scope.hpp"
class Stacktrace
@@ -13,11 +13,13 @@ public:
class Line
{
public:
Line(const std::string& original) : original(original) {}
Line(const std::string &original) : original(original) {}
Line(const std::string& original, const std::string& function,
const std::string& location)
: original(original), function(function), location(location) {}
Line(const std::string &original, const std::string &function,
const std::string &location)
: original(original), function(function), location(location)
{
}
std::string original, function, location;
};
@@ -26,17 +28,17 @@ public:
Stacktrace()
{
void* addresses[stacktrace_depth];
void *addresses[stacktrace_depth];
auto depth = backtrace(addresses, stacktrace_depth);
// will this leak if backtrace_symbols throws?
char** symbols = nullptr;
char **symbols = nullptr;
Auto(free(symbols));
symbols = backtrace_symbols(addresses, depth);
// skip the first one since it will be Stacktrace::Stacktrace()
for(int i = 1; i < depth; ++i)
for (int i = 1; i < depth; ++i)
lines.emplace_back(format(symbols[i]));
}
@@ -48,54 +50,53 @@ public:
auto end() const { return lines.end(); }
auto cend() const { return lines.cend(); }
const Line& operator[](size_t idx) const
{
return lines[idx];
}
const Line &operator[](size_t idx) const { return lines[idx]; }
size_t size() const
{
return lines.size();
}
size_t size() const { return lines.size(); }
template <class Stream>
void dump(Stream& stream) {
stream << dump();
void dump(Stream &stream)
{
stream << dump();
}
std::string dump() {
std::string message;
for (int i = 0; i < size(); i++) {
message.append(fmt::format("at {} ({}) \n", lines[i].function,
lines[i].location));
}
return message;
std::string dump()
{
std::string message;
for (size_t i = 0; i < size(); i++)
{
message.append(fmt::format("at {} ({}) \n", lines[i].function,
lines[i].location));
}
return message;
}
private:
std::vector<Line> lines;
Line format(const std::string& original)
Line format(const std::string &original)
{
using namespace abi;
auto line = original;
auto begin = line.find('(');
auto end = line.find('+');
auto end = line.find('+');
if(begin == std::string::npos || end == std::string::npos)
if (begin == std::string::npos || end == std::string::npos)
return {original};
line[end] = '\0';
int s;
auto demangled = __cxa_demangle(line.data() + begin + 1, nullptr,
nullptr, &s);
auto demangled =
__cxa_demangle(line.data() + begin + 1, nullptr, nullptr, &s);
auto location = line.substr(0, begin);
auto function = demangled ? std::string(demangled)
: fmt::format("{}()", original.substr(begin + 1, end - begin - 1));
auto function =
demangled ? std::string(demangled)
: fmt::format("{}()", original.substr(begin + 1,
end - begin - 1));
return {original, function, location};
}

View File

@@ -1,24 +1,67 @@
#pragma mark
#include "sys/types.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "sys/sysinfo.h"
#include "sys/types.h"
auto total_virtual_memory()
{
struct sysinfo mem_info;
sysinfo (&mem_info);
long long total_virtual_memory = mem_info.totalram;
total_virtual_memory += mem_info.totalswap;
total_virtual_memory *= mem_info.mem_unit;
return total_virtual_memory;
struct sysinfo mem_info;
sysinfo(&mem_info);
long long total_virtual_memory = mem_info.totalram;
total_virtual_memory += mem_info.totalswap;
total_virtual_memory *= mem_info.mem_unit;
return total_virtual_memory;
}
auto used_virtual_memory()
{
struct sysinfo mem_info;
sysinfo (&mem_info);
struct sysinfo mem_info;
sysinfo(&mem_info);
long long virtual_memory_used = mem_info.totalram - mem_info.freeram;
virtual_memory_used += mem_info.totalswap - mem_info.freeswap;
virtual_memory_used *= mem_info.mem_unit;
return virtual_memory_used;
}
// TODO: OS dependent
/**
* parses memory line from /proc/self/status
*/
auto parse_vm_size(char *line)
{
// This assumes that a digit will be found and the line ends in " Kb".
auto i = std::strlen(line);
const char *p = line;
while (*p < '0' || *p > '9')
p++;
line[i - 3] = '\0';
return std::atoll(p);
}
/**
* returns VmSize in kB
*/
auto vm_size()
{
std::FILE *file = std::fopen("/proc/self/status", "r");
auto result = -1LL;
char line[128];
while (fgets(line, 128, file) != NULL)
{
if (strncmp(line, "VmSize:", 7) == 0)
{
result = parse_vm_size(line);
break;
}
}
fclose(file);
return result;
}

View File

@@ -1,28 +1,34 @@
#pragma once
#include "utils/auto_scope.hpp"
#include "utils/stacktrace.hpp"
#include "utils/stacktrace/stacktrace.hpp"
#include <execinfo.h>
#include <iostream>
// TODO: log to local file or remote database
void stacktrace(std::ostream& stream) noexcept {
Stacktrace stacktrace;
stacktrace.dump(stream);
void stacktrace(std::ostream &stream) noexcept
{
Stacktrace stacktrace;
stacktrace.dump(stream);
}
// TODO: log to local file or remote database
void terminate_handler(std::ostream& stream) noexcept {
if (auto exc = std::current_exception()) {
try {
std::rethrow_exception(exc);
} catch (std::exception& ex) {
stream << ex.what() << std::endl << std::endl;
stacktrace(stream);
void terminate_handler(std::ostream &stream) noexcept
{
if (auto exc = std::current_exception())
{
try
{
std::rethrow_exception(exc);
}
catch (std::exception &ex)
{
stream << ex.what() << std::endl << std::endl;
stacktrace(stream);
}
}
}
std::abort();
std::abort();
}
void terminate_handler() noexcept { terminate_handler(std::cout); }

View File

@@ -0,0 +1,7 @@
#pragma once
#include <chrono>
using namespace std::chrono_literals;
using ms = std::chrono::milliseconds;

View File

@@ -1,14 +1,12 @@
#pragma once
#include <chrono>
#include <iostream>
#include <ratio>
#include <utility>
#define time_now() std::chrono::high_resolution_clock::now()
#include "utils/time/time.hpp"
using ns = std::chrono::nanoseconds;
using ms = std::chrono::milliseconds;
#define time_now() std::chrono::high_resolution_clock::now()
template <typename DurationUnit = std::chrono::nanoseconds>
auto to_duration(const std::chrono::duration<long, std::nano> &delta)

View File

@@ -8,13 +8,15 @@
#include "logging/default.hpp"
/** @class Timer
* @brief The timer contains counter and handler.
/**
* @class Timer
*
* With every clock interval the counter should be decresed for
* delta count. Delta count is one for now but it should be a variable in the
* near future. The handler is function that will be called when counter
* becomes zero or smaller than zero.
* @brief The timer contains counter and handler.
*
* With every clock interval the counter should be decresed for
* delta count. Delta count is one for now but it should be a variable in the
* near future. The handler is function that will be called when counter
* becomes zero or smaller than zero.
*/
struct Timer
{
@@ -48,14 +50,16 @@ struct Timer
* the process method.
*/
/** @class TimerSet
* @brief Trivial timer container implementation.
/**
* @class TimerSet
*
* Internal data stucture for storage of timers is std::set. So, the
* related timer complexities are:
* insertion: O(log(n))
* deletion: O(log(n))
* process: O(n)
* @brief Trivial timer container implementation.
*
* Internal data stucture for storage of timers is std::set. So, the
* related timer complexities are:
* insertion: O(log(n))
* deletion: O(log(n))
* process: O(n)
*/
class TimerSet
{
@@ -70,6 +74,11 @@ public:
timers.erase(timer);
}
uint64_t size() const
{
return timers.size();
}
void process()
{
for (auto it = timers.begin(); it != timers.end(); ) {
@@ -87,10 +96,17 @@ private:
std::set<std::shared_ptr<Timer>> timers;
};
/** @class TimerScheduler
* @brief TimerScheduler is a manager class and its responsibility is to
* take care of the time and call the timer_container process method in the
* appropriate time.
/**
* @class TimerScheduler
*
* @brief TimerScheduler is a manager class and its responsibility is to
* take care of the time and call the timer_container process method in the
* appropriate time.
*
* @tparam timer_container_type implements a strategy how the timers
* are processed
* @tparam delta_time_type type of a time distance between two events
* @tparam delta_time granularity between the two events, default value is 1
*/
template <
typename timer_container_type,
@@ -99,19 +115,47 @@ template <
> class TimerScheduler
{
public:
/**
* Adds a timer.
*
* @param timer shared pointer to the timer object \ref Timer
*/
void add(Timer::sptr timer)
{
timer_container.add(timer);
}
/**
* Removes a timer.
*
* @param timer shared pointer to the timer object \ref Timer
*/
void remove(Timer::sptr timer)
{
timer_container.remove(timer);
}
/**
* Provides the number of pending timers. The exact number has to be
* provided by a timer_container.
*
* @return uint64_t the number of pending timers.
*/
uint64_t size() const
{
return timer_container.size();
}
/**
* Runs a separate thread which responsibility is to run the process method
* at the appropriate time (every delta_time from the beginning of
* processing.
*/
void run()
{
is_running.store(true);
run_thread = std::thread([this]() {
while (is_running.load()) {
std::this_thread::sleep_for(delta_time_type(delta_time));
@@ -121,11 +165,17 @@ public:
});
}
/**
* Stops the whole processing.
*/
void stop()
{
is_running.store(false);
}
/**
* Joins the processing thread.
*/
~TimerScheduler()
{
run_thread.join();