examples folder has been removed; src/test folder has been removed; the existing tests were moved to tests folder; StacktraceException has been introduced; query_plan_templated has been moved to query folder; hazard pointers have been deleted because they are not used any more
This commit is contained in:
parent
0198b37f21
commit
3642fb1312
@ -86,14 +86,16 @@ set(antlr_static_lib ${CMAKE_SOURCE_DIR}/dist/libantlr4-runtime.a)
|
||||
|
||||
# prepare template and destination folders for query engine (tests)
|
||||
# and memgraph server binary
|
||||
# copy query_engine's templates file
|
||||
FILE(COPY ${src_dir}/query_engine/template
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests)
|
||||
FILE(COPY ${src_dir}/query_engine/template
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests/integration)
|
||||
FILE(COPY ${src_dir}/query_engine/template
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests/manual)
|
||||
FILE(COPY ${src_dir}/query_engine/template DESTINATION ${CMAKE_BINARY_DIR})
|
||||
# copy query_engine template file
|
||||
set(query_engine_template_file ${src_dir}/query/plan_template_cpp)
|
||||
FILE(COPY ${query_engine_template_file}
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests/template)
|
||||
FILE(COPY ${query_engine_template_file}
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests/integration/template)
|
||||
FILE(COPY ${query_engine_template_file}
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/tests/manual/template)
|
||||
FILE(COPY ${query_engine_template_file}
|
||||
DESTINATION ${CMAKE_BINARY_DIR}/template)
|
||||
# create destination folder for compiled queries
|
||||
FILE(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tests/integration/compiled)
|
||||
FILE(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tests/manual/compiled)
|
||||
|
@ -9,7 +9,7 @@
|
||||
compile_path: "./compiled/"
|
||||
|
||||
# path to the template (cpp) for codes generation
|
||||
template_cpp_path: "./template/template_code_cpp"
|
||||
template_cpp_path: "./template/plan_template_cpp"
|
||||
|
||||
# path to the folder with snapshots
|
||||
snapshots_path: "snapshots"
|
||||
|
1
example/.gitignore
vendored
1
example/.gitignore
vendored
@ -1 +0,0 @@
|
||||
*.out
|
@ -1,67 +0,0 @@
|
||||
/* Plots the distribution histogram of the fast_binomial algorithm
|
||||
* (spoiler alert: it's pleasingly (1/2)^N all the way :D)
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <iomanip>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/random/fast_binomial.hpp"
|
||||
|
||||
static constexpr unsigned B = 24;
|
||||
static thread_local FastBinomial<B> rnd;
|
||||
|
||||
static constexpr unsigned M = 4;
|
||||
static constexpr size_t N = 1ULL << 34;
|
||||
static constexpr size_t per_thread_iters = N / M;
|
||||
|
||||
std::array<std::atomic<uint64_t>, B> buckets;
|
||||
|
||||
void generate()
|
||||
{
|
||||
for(size_t i = 0; i < per_thread_iters; ++i)
|
||||
buckets[rnd() - 1].fetch_add(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
auto bar_len = w.ws_col - 20;
|
||||
|
||||
std::array<std::thread, M> threads;
|
||||
|
||||
for(auto& bucket : buckets)
|
||||
bucket.store(0);
|
||||
|
||||
for(auto& t : threads)
|
||||
t = std::thread([]() { generate(); });
|
||||
|
||||
for(auto& t : threads)
|
||||
t.join();
|
||||
|
||||
auto max = std::accumulate(buckets.begin(), buckets.end(), (uint64_t)0,
|
||||
[](auto& acc, auto& x) { return std::max(acc, x.load()); });
|
||||
|
||||
std::cout << std::fixed;
|
||||
|
||||
for(size_t i = 0; i < buckets.size(); ++i)
|
||||
{
|
||||
auto x = buckets[i].load();
|
||||
auto rel = bar_len * x / max;
|
||||
|
||||
std::cout << std::setw(2) << i + 1 << " ";
|
||||
|
||||
for(size_t i = 0; i < rel; ++i)
|
||||
std::cout << "=";
|
||||
|
||||
std::cout << " " << 100 * (double)x / N << "%" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "debug/log.hpp"
|
||||
|
||||
#include "io/network/epoll.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "io/network/tcp/stream.hpp"
|
||||
#include "io/network/stream_reader.hpp"
|
||||
|
||||
#include "memory/literals.hpp"
|
||||
|
||||
using namespace memory::literals;
|
||||
|
||||
|
||||
class RandomString
|
||||
{
|
||||
static constexpr char charset[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
public:
|
||||
template <class Rg>
|
||||
std::string operator()(Rg&& gen, size_t len)
|
||||
{
|
||||
auto str = std::string();
|
||||
str.reserve(len + 2);
|
||||
str.push_back('\'');
|
||||
|
||||
while(str.size() < len)
|
||||
str.push_back(charset[rnd(std::forward<Rg>(gen))]);
|
||||
|
||||
str.push_back('\'');
|
||||
return str;
|
||||
}
|
||||
|
||||
private:
|
||||
std::uniform_int_distribution<> rnd {0, sizeof(charset) - 1};
|
||||
};
|
||||
|
||||
constexpr char RandomString::charset[];
|
||||
|
||||
static std::mt19937 mt {std::random_device{}()};
|
||||
|
||||
class CypherPost
|
||||
{
|
||||
static std::string templ;
|
||||
|
||||
public:
|
||||
CypherPost()
|
||||
{
|
||||
request.reserve(64_kB);
|
||||
}
|
||||
|
||||
void set(const std::string& query)
|
||||
{
|
||||
request.clear();
|
||||
|
||||
request += "POST /db/data/transaction/commit HTTP/1.1\r\n" \
|
||||
"Host: localhost:7474\r\n" \
|
||||
"Authorization: Basic bmVvNGo6cGFzcw==\r\n" \
|
||||
"Accept: application/json; charset=UTF-8\r\n" \
|
||||
"Content-Type: application/json\r\n" \
|
||||
"Content-Length: ";
|
||||
request += std::to_string(query.size() + templ.size() + 4);
|
||||
request += "\r\n\r\n";
|
||||
request += templ;
|
||||
request += query;
|
||||
request += "\"}]}";
|
||||
}
|
||||
|
||||
operator const std::string&() const { return request; }
|
||||
|
||||
private:
|
||||
std::string request;
|
||||
};
|
||||
|
||||
std::string CypherPost::templ = "{\"statements\":[{\"statement\":\"";
|
||||
|
||||
struct Result
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point start, end;
|
||||
uint64_t requests;
|
||||
};
|
||||
|
||||
class Worker : public io::StreamReader<Worker, io::tcp::Stream>
|
||||
{
|
||||
char buf[65535];
|
||||
CypherPost post;
|
||||
|
||||
std::uniform_int_distribution<> random_int;
|
||||
RandomString random_string;
|
||||
Replacer replacer;
|
||||
|
||||
public:
|
||||
Worker()
|
||||
{
|
||||
replacer.replace("#", [&]() { return std::to_string(random_int(mt)); })
|
||||
.replace("^", [&]() { return random_string(mt, 15); });
|
||||
}
|
||||
|
||||
io::tcp::Stream& on_connect(io::Socket&&)
|
||||
{
|
||||
// DUMMY, refactor StreamReader to be more generic
|
||||
return *streams.back();
|
||||
}
|
||||
|
||||
bool connect(const char* name, const char* port)
|
||||
{
|
||||
auto socket = io::Socket::connect(name, port);
|
||||
|
||||
if(!socket.is_open())
|
||||
return false;
|
||||
|
||||
socket.set_non_blocking();
|
||||
|
||||
streams.push_back(std::make_unique<io::tcp::Stream>(std::move(socket)));
|
||||
auto& stream = streams.back();
|
||||
|
||||
stream->event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
|
||||
this->add(*stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void on_error(io::tcp::Stream& conn)
|
||||
{
|
||||
LOG_DEBUG("error on socket " << conn.id());
|
||||
(void)conn;
|
||||
|
||||
LOG_DEBUG((errno == EBADF));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void on_wait_timeout() {}
|
||||
|
||||
Buffer on_alloc(io::tcp::Stream&)
|
||||
{
|
||||
return Buffer { buf, sizeof buf };
|
||||
}
|
||||
|
||||
void on_read(io::tcp::Stream& stream, Buffer& buf)
|
||||
{
|
||||
/* std::cout << "RESPONSE" << std::endl; */
|
||||
/* std::cout << std::string(buf.ptr, buf.len) << std::endl; */
|
||||
|
||||
requests++;
|
||||
|
||||
LOG_DEBUG("on_read");
|
||||
sendreq(stream.socket);
|
||||
}
|
||||
|
||||
void on_close(io::tcp::Stream&) {}
|
||||
|
||||
void sendreq(io::Socket& socket)
|
||||
{
|
||||
/* auto query = std::string("CREATE (n:Person {id: #, name: ^}) RETURN n"); */
|
||||
auto query = std::string("MATCH (n:Person {id: #}) RETURN n");
|
||||
|
||||
post.set(replacer(query));
|
||||
|
||||
/* std::cout << "REQUEST" << std::endl; */
|
||||
/* std::cout << static_cast<const std::string&>(post) << std::endl; */
|
||||
/* std::cout << "SIZE = " << static_cast<const std::string&>(post).size() << std::endl; */
|
||||
|
||||
auto n = socket.write(static_cast<const std::string&>(post));
|
||||
|
||||
/* std::cout << "Written N = " << n << " bytes." << std::endl; */
|
||||
|
||||
LOG_DEBUG("sent.");
|
||||
}
|
||||
|
||||
Result run_benchmark(std::chrono::duration<double> duration)
|
||||
{
|
||||
LOG_DEBUG("run_benchmark");
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
clock::time_point end, start = clock::now();
|
||||
|
||||
for(auto& stream : streams)
|
||||
sendreq(stream->socket);
|
||||
|
||||
LOG_DEBUG("sent req to all streams");
|
||||
|
||||
while(true)
|
||||
{
|
||||
LOG_DEBUG("WAIT AND PROCESS");
|
||||
this->wait_and_process_events();
|
||||
|
||||
if((end = clock::now()) - start > duration)
|
||||
break;
|
||||
}
|
||||
|
||||
return {start, end, requests};
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t requests {0};
|
||||
std::vector<std::unique_ptr<io::tcp::Stream>> streams;
|
||||
};
|
||||
|
||||
class WorkerRunner
|
||||
{
|
||||
public:
|
||||
WorkerRunner() : worker(std::make_unique<Worker>()) {}
|
||||
|
||||
Worker* operator->() { return worker.get(); }
|
||||
const Worker* operator->() const { return worker.get(); }
|
||||
|
||||
void operator()(std::chrono::duration<double> duration)
|
||||
{
|
||||
std::packaged_task<Result()> task([this, duration]() {
|
||||
return this->worker->run_benchmark(duration);
|
||||
});
|
||||
|
||||
result = std::move(task.get_future());
|
||||
std::thread(std::move(task)).detach();
|
||||
}
|
||||
|
||||
std::unique_ptr<Worker> worker;
|
||||
std::future<Result> result;
|
||||
};
|
||||
|
||||
std::atomic<bool> alive {true};
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
using namespace std::chrono;
|
||||
|
||||
if(argc < 4)
|
||||
std::abort();
|
||||
|
||||
auto threads = std::stoi(argv[1]);
|
||||
auto connections = std::stoi(argv[2]);
|
||||
auto duration = std::stoi(argv[3]);
|
||||
|
||||
std::vector<WorkerRunner> workers;
|
||||
|
||||
for(int i = 0; i < threads; ++i)
|
||||
workers.emplace_back();
|
||||
|
||||
for(int i = 0; i < connections; ++i)
|
||||
workers[i % threads]->connect("localhost", "7474");
|
||||
|
||||
std::vector<Result> results;
|
||||
|
||||
std::cout << "Running queries on " << connections << " connections "
|
||||
<< "using " << threads << " threads "
|
||||
<< "for " << duration << " seconds." << std::endl
|
||||
<< "..." << std::endl;
|
||||
|
||||
for(auto& worker : workers)
|
||||
worker(std::chrono::seconds(duration));
|
||||
|
||||
for(auto& worker : workers)
|
||||
{
|
||||
worker.result.wait();
|
||||
results.push_back(worker.result.get());
|
||||
}
|
||||
|
||||
auto start = std::min_element(results.begin(), results.end(),
|
||||
[](auto a, auto b) { return a.start < b.start; })->start;
|
||||
|
||||
auto end = std::max_element(results.begin(), results.end(),
|
||||
[](auto a, auto b) { return a.end < b.end; })->end;
|
||||
|
||||
auto requests = std::accumulate(results.begin() + 1, results.end(),
|
||||
results[0].requests, [](auto acc, auto r) { return acc + r.requests; });
|
||||
|
||||
auto elapsed = (end - start).count() / 1.0e9;
|
||||
|
||||
std::cout << "Total of " << requests << " requests in "
|
||||
<< elapsed << "s." << std::endl
|
||||
<< "Requests/sec: " << int(requests / elapsed)
|
||||
<< "." << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#include <byteswap.h>
|
||||
|
||||
char b[8] = {1, 2, 3, 4, 0, 0, 0, 1};
|
||||
|
||||
int64_t safe_int64(const char* b)
|
||||
{
|
||||
return int64_t(b[0]) << 56 | int64_t(b[1]) << 48
|
||||
| int64_t(b[2]) << 40 | int64_t(b[3]) << 32
|
||||
| int64_t(b[4]) << 24 | int64_t(b[5]) << 16
|
||||
| int64_t(b[6]) << 8 | int64_t(b[7]);
|
||||
}
|
||||
|
||||
int64_t unsafe_int64(const char* b)
|
||||
{
|
||||
auto i = reinterpret_cast<const int64_t*>(b);
|
||||
return __bswap_64(*i);
|
||||
}
|
||||
|
||||
int32_t safe_int32(const char* b)
|
||||
{
|
||||
return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
|
||||
}
|
||||
|
||||
int32_t unsafe_int32(const char* b)
|
||||
{
|
||||
auto i = reinterpret_cast<const int32_t*>(b);
|
||||
return __bswap_32(*i);
|
||||
}
|
||||
|
||||
[[clang::optnone]]
|
||||
void test(uint64_t n)
|
||||
{
|
||||
for(uint64_t i = 0; i < n; ++i)
|
||||
unsafe_int64(b);
|
||||
}
|
||||
|
||||
uint8_t f[8] = {0x3F, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A};
|
||||
|
||||
double ff = 1.1;
|
||||
|
||||
double get_double(const uint8_t* b)
|
||||
{
|
||||
auto v = __bswap_64(*reinterpret_cast<const uint64_t*>(b));
|
||||
return *reinterpret_cast<const double*>(&v);
|
||||
}
|
||||
|
||||
void print_hex(const char* buf, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
printf("%02X ", (unsigned char)buf[i]);
|
||||
}
|
||||
|
||||
void print_hex(const uint8_t* buf, size_t n)
|
||||
{
|
||||
print_hex((const char*)buf, n);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto dd = get_double(f);
|
||||
|
||||
print_hex(f, 8);
|
||||
|
||||
std::cout << std::endl;
|
||||
print_hex((const uint8_t*)(&ff), 8);
|
||||
|
||||
std::cout << std::endl;
|
||||
print_hex((const uint8_t*)(&dd), 8);
|
||||
|
||||
std::cout << dd << std::endl;
|
||||
|
||||
/* std::cout << safe_int64(b) << std::endl; */
|
||||
/* std::cout << unsafe_int64(b) << std::endl; */
|
||||
|
||||
/* test(1000000000ull); */
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
void i_will_throw()
|
||||
{
|
||||
throw BasicException("this is not {}", "ok!");
|
||||
}
|
||||
|
||||
void bar()
|
||||
{
|
||||
i_will_throw();
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
bar();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
foo();
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
#include "threading/sync/futex.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
//#include "debug/log.hpp"
|
||||
|
||||
Futex futex;
|
||||
//std::mutex mutex;
|
||||
//SpinLock spinlock;
|
||||
int x = 0;
|
||||
|
||||
void test_lock(int id)
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, 1000);
|
||||
|
||||
for(int i = 0; i < 5000000; ++i)
|
||||
{
|
||||
// uncomment sleeps and LOG_DEBUGs to test high contention
|
||||
|
||||
//LOG_DEBUG("Acquiring Futex (" << id << ")");
|
||||
|
||||
{
|
||||
//std::unique_lock<SpinLock> guard(spinlock);
|
||||
std::unique_lock<Futex> guard(futex);
|
||||
//std::unique_lock<std::mutex> guard(mutex);
|
||||
x++;
|
||||
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
||||
|
||||
//LOG_DEBUG("Critical section no. " << i << " (" << id << ")");
|
||||
assert(x == 1);
|
||||
|
||||
x--;
|
||||
}
|
||||
|
||||
//LOG_DEBUG("Non Critical section... (" << id << ")");
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
constexpr int N = 4;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
threads.push_back(std::thread(test_lock, i));
|
||||
|
||||
for(auto& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "threading/thread.hpp"
|
||||
#include "threading/hazard_ptr.hpp"
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int bar = 0;
|
||||
};
|
||||
|
||||
void scan_foos(const std::vector<Foo>& foos)
|
||||
{
|
||||
auto& hp = HazardStore::get();
|
||||
|
||||
std::unique_lock<std::mutex> cout_guard(mutex);
|
||||
std::cout << "Scanning foos..." << std::endl;
|
||||
|
||||
for(auto& foo : foos)
|
||||
{
|
||||
auto foo_ptr = &foo;
|
||||
|
||||
std::unique_lock<std::mutex> cout_guard(mutex);
|
||||
std::cout << "Foo taken? " << hp.scan(foo_ptr) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
static constexpr size_t NUM_THREADS = 8;
|
||||
|
||||
std::vector<Thread> threads;
|
||||
|
||||
std::vector<Foo> foos;
|
||||
foos.resize(NUM_THREADS + 2);
|
||||
|
||||
for(size_t i = 0; i < NUM_THREADS; ++i)
|
||||
threads.emplace_back([&foos]() {
|
||||
auto id = this_thread::id;
|
||||
|
||||
auto foo = &foos.at(id);
|
||||
auto hazard = hazard_ptr(foo);
|
||||
|
||||
foo->bar = id;
|
||||
|
||||
std::unique_lock<std::mutex> cout_guard(mutex);
|
||||
std::cout << "Hello from thread " << this_thread::id << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(5s);
|
||||
});
|
||||
|
||||
// 0 to NUM_THREADS foos should be taken
|
||||
// maybe none, maybe all!
|
||||
scan_foos(foos);
|
||||
|
||||
std::this_thread::sleep_for(3s);
|
||||
|
||||
// first NUM_THREADS foos should be taken
|
||||
scan_foos(foos);
|
||||
|
||||
std::this_thread::sleep_for(3s);
|
||||
|
||||
// all foos should be available now
|
||||
scan_foos(foos);
|
||||
|
||||
for(auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "mvcc/id.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
int main() {
|
||||
|
||||
Id id0(0);
|
||||
Id id1(1);
|
||||
Id id2(1);
|
||||
Id id3(id2);
|
||||
Id id4 = id3;
|
||||
Id id5(5);
|
||||
|
||||
cout << id5 << " " << id0 << endl;
|
||||
|
||||
if (id0 < id5)
|
||||
cout << "id0 < id5" << endl;
|
||||
|
||||
if (id1 == id2)
|
||||
cout << "are equal" << endl;
|
||||
|
||||
if (id3 == id4)
|
||||
cout << "id3 == id4" << endl;
|
||||
|
||||
if (id5 > id0)
|
||||
cout << "id5 > id0" << endl;
|
||||
|
||||
if (id5 != id3)
|
||||
cout << "id5 != id3" << endl;
|
||||
|
||||
if (id1 >= id2)
|
||||
cout << "id1 >= id2" << endl;
|
||||
|
||||
if (id3 <= id4)
|
||||
cout << "id3 <= id4" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "transactions/engine.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/vertex.hpp"
|
||||
|
||||
#include "storage/indexes/property_index.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using Record = mvcc::VersionList<Vertex>;
|
||||
|
||||
tx::Engine engine;
|
||||
Index<Vertex>* index = new PropertyIndex<Vertex>();
|
||||
|
||||
Record* create(int id, tx::Transaction& t)
|
||||
{
|
||||
auto v = new record_t(id);
|
||||
auto a = v->access(t);
|
||||
a.insert();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
auto insert(int id, tx::Transaction& t)
|
||||
{
|
||||
auto r = create(id, t);
|
||||
return index.insert(&r->id, r, t);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* v1->data.props.set<String>("name", "buda"); */
|
||||
/* v1->data.props.set<Int32>("age", 23); */
|
||||
|
||||
auto& t1 = engine.begin();
|
||||
insert(0, t1);
|
||||
insert(1, t1);
|
||||
insert(2, t1);
|
||||
insert(3, t1);
|
||||
insert(6, t1);
|
||||
insert(7, t1);
|
||||
t1.commit();
|
||||
|
||||
auto& t2 = engine.begin();
|
||||
insert(4, t2);
|
||||
insert(5, t2);
|
||||
insert(8, t2);
|
||||
t2.commit();
|
||||
|
||||
auto& t3 = engine.begin();
|
||||
auto cursor = index.scan(8, t3);
|
||||
|
||||
for(; not cursor.end(); cursor++)
|
||||
cout << cursor->id() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#include "logging/logger.hpp"
|
||||
#include "logging/logs/sync_log.hpp"
|
||||
#include "logging/logs/async_log.hpp"
|
||||
|
||||
#include "logging/streams/stdout.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
//Log::uptr log = std::make_unique<SyncLog>();
|
||||
Log::uptr log = std::make_unique<AsyncLog>();
|
||||
|
||||
log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
auto logger = log->logger("main");
|
||||
|
||||
logger.info("This is very {}!", "awesome");
|
||||
logger.warn("This is very {}!", "awesome");
|
||||
logger.error("This is very {}!", "awesome");
|
||||
logger.trace("This is very {}!", "awesome");
|
||||
logger.debug("This is very {}!", "awesome");
|
||||
|
||||
using namespace std::chrono;
|
||||
/* std::this_thread::sleep_for(1s); */
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "storage/model/properties/properties.hpp"
|
||||
#include "storage/model/properties/property.hpp"
|
||||
#include "storage/model/properties/traversers/jsonwriter.hpp"
|
||||
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Properties props;
|
||||
props.set<Bool>("awesome", true);
|
||||
props.set<Bool>("lame", false);
|
||||
props.set<Int32>("age", 32);
|
||||
|
||||
// integral
|
||||
Int32 a = 12;
|
||||
Int32 b = 24;
|
||||
Int32 c = b;
|
||||
|
||||
Property& d = b;
|
||||
|
||||
cout << "a = " << a << "; b = " << b << endl;
|
||||
cout << (a > b) << (a < b) << (a == b) << (a != b) << endl;
|
||||
|
||||
cout << "b == d" << " -> " << (b == d) << endl;
|
||||
|
||||
Float x = 3.14;
|
||||
Float y = 6.28;
|
||||
|
||||
Float z = x * 3.28 / y + a * b + 3;
|
||||
|
||||
cout << x << endl;
|
||||
cout << z << endl;
|
||||
|
||||
props.set<Float>("pi", z);
|
||||
|
||||
cout << props.at("awesome") << endl;
|
||||
cout << props.at("lame") << endl;
|
||||
cout << props.at("age") << endl;
|
||||
cout << props.at("pi") << endl;
|
||||
cout << props.at("lol") << endl;
|
||||
|
||||
StringBuffer buffer;
|
||||
JsonWriter<StringBuffer> writer(buffer);
|
||||
|
||||
props.accept(writer);
|
||||
cout << buffer.str() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,53 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/skiplist/skiplist.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using skiplist_t = SkipList<int, int>;
|
||||
|
||||
void print_skiplist(const skiplist_t::Accessor& skiplist)
|
||||
{
|
||||
cout << "---- skiplist now has: ";
|
||||
|
||||
for(auto& kv : skiplist)
|
||||
cout << "(" << kv.first << ", " << kv.second << ") ";
|
||||
|
||||
cout << "----" << endl;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cout << std::boolalpha;
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
cout << "added non-existing (1, 10)? (true) "
|
||||
<< accessor.insert_unique(1, 10).second << endl;
|
||||
|
||||
cout << "added already existing (1, 10)? (false) "
|
||||
<< accessor.insert_unique(1, 10).second << endl;
|
||||
|
||||
accessor.insert_unique(2, 20);
|
||||
print_skiplist(accessor);
|
||||
|
||||
cout << "value at key 3 exists? (false) "
|
||||
<< (accessor.find(3) == accessor.end()) << endl;
|
||||
|
||||
cout << "value at key 2 exists? (true) "
|
||||
<< (accessor.find(2) != accessor.end()) << endl;
|
||||
|
||||
cout << "at key 2 is? (20) " << accessor.find(2)->second << endl;
|
||||
|
||||
cout << "removed existing (1)? (true) " << accessor.remove(1) << endl;
|
||||
cout << "removed non-existing (3)? (false) " << accessor.remove(3) << endl;
|
||||
|
||||
accessor.insert_unique(1, 10);
|
||||
accessor.insert_unique(4, 40);
|
||||
|
||||
print_skiplist(accessor);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/skiplist/skiplist.hpp"
|
||||
#include "storage/indexes/keys/unique_key.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using skiplist_t = SkipList<UniqueKeyAsc<int>, int>;
|
||||
|
||||
void print_skiplist(const skiplist_t::Accessor& skiplist)
|
||||
{
|
||||
cout << "---- skiplist now has: ";
|
||||
|
||||
for(auto& kv : skiplist)
|
||||
cout << "(" << kv.first << ", " << kv.second << ") ";
|
||||
|
||||
cout << "----" << endl;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
skiplist_t skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
// this has to be here since UniqueKey<> class takes references!
|
||||
int keys[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
accessor.insert_unique(keys[1], 10);
|
||||
accessor.insert_unique(keys[2], 20);
|
||||
accessor.insert_unique(keys[7], 70);
|
||||
accessor.insert_unique(keys[4], 40);
|
||||
accessor.insert_unique(keys[8], 80);
|
||||
accessor.insert_unique(keys[3], 30);
|
||||
|
||||
print_skiplist(accessor);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/skiplist/skiplistset.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
void print_skiplist(const SkipListSet<int>::Accessor& skiplist)
|
||||
{
|
||||
cout << "---- skiplist set now has: ";
|
||||
|
||||
for(auto& item : skiplist)
|
||||
cout << item << ", ";
|
||||
|
||||
cout << "----" << endl;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SkipListSet<int> set;
|
||||
auto accessor = set.access();
|
||||
|
||||
cout << std::boolalpha;
|
||||
|
||||
cout << "added non-existing 1? (true) "
|
||||
<< accessor.insert(1).second << endl;
|
||||
|
||||
cout << "added already existing 1? (false) "
|
||||
<< accessor.insert(1).second << endl;
|
||||
|
||||
accessor.insert(2);
|
||||
print_skiplist(accessor);
|
||||
|
||||
cout << "item 3 doesn't exist? (true) "
|
||||
<< (accessor.find(3) == accessor.end()) << endl;
|
||||
|
||||
cout << "item 3 exists? (false) "
|
||||
<< accessor.contains(3) << endl;
|
||||
|
||||
cout << "item 2 exists? (true) "
|
||||
<< (accessor.find(2) != accessor.end()) << endl;
|
||||
|
||||
cout << "at item 2 is? 2 " << *accessor.find(2) << endl;
|
||||
|
||||
cout << "removed existing 1? (true) " << accessor.remove(1) << endl;
|
||||
cout << "removed non-existing 3? (false) " << accessor.remove(3) << endl;
|
||||
|
||||
accessor.insert(1);
|
||||
accessor.insert(4);
|
||||
|
||||
print_skiplist(accessor);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
std::string load_file(const std::string& fname)
|
||||
{
|
||||
std::ifstream in(fname);
|
||||
return std::string((std::istreambuf_iterator<char>(in)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if(argc < 3)
|
||||
return -1;
|
||||
|
||||
auto a = load_file(argv[1]);
|
||||
auto b = load_file(argv[2]);
|
||||
|
||||
bool result = true;
|
||||
|
||||
for(size_t i = 0; i < a.size(); ++i)
|
||||
result &= strcmp(a.c_str() + i, b.c_str() + i) == 0;
|
||||
|
||||
std::cout << result << std::endl;
|
||||
return 0;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "utils/datetime/timestamp.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto timestamp = Timestamp::now();
|
||||
|
||||
std::cout << timestamp << std::endl;
|
||||
std::cout << Timestamp::now() << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
std::cout << Timestamp::now().to_iso8601() << std::endl;
|
||||
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
std::cout << (timestamp == Timestamp::now()) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "transactions/engine.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/vertex.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tx::Engine engine;
|
||||
VertexRecord vertex;
|
||||
|
||||
auto& t1 = engine.begin();
|
||||
auto a1 = vertex.access(t1);
|
||||
auto v1 = a1.insert();
|
||||
|
||||
v1->data.props.set<String>("name", "buda");
|
||||
v1->data.props.set<Int32>("age", 23);
|
||||
|
||||
cout << vertex;
|
||||
|
||||
t1.commit();
|
||||
|
||||
auto& t2 = engine.begin();
|
||||
auto a2 = vertex.access(t2);
|
||||
auto v2 = a2.update();
|
||||
|
||||
v2->data.props.set<Int32>("age", 24);
|
||||
|
||||
cout << vertex;
|
||||
|
||||
t2.abort();
|
||||
|
||||
auto& t3 = engine.begin();
|
||||
auto a3 = vertex.access(t3);
|
||||
auto v3 = a3.update();
|
||||
|
||||
v3->data.props.set<Int32>("age", 25);
|
||||
|
||||
cout << vertex;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/* Plots the distribution histogram of the xorshift algorithm
|
||||
* (spoiler alert: it's pleasingly uniform all the way :D)
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/random/xorshift128plus.hpp"
|
||||
|
||||
static thread_local Xorshift128plus rnd;
|
||||
static constexpr unsigned B = 1 << 10;
|
||||
static constexpr uint64_t K = (uint64_t)(-1) / B;
|
||||
|
||||
static constexpr unsigned M = 4;
|
||||
static constexpr size_t N = 1ULL << 34;
|
||||
static constexpr size_t per_thread_iters = N / M;
|
||||
|
||||
std::array<std::atomic<unsigned>, B> buckets;
|
||||
|
||||
void generate()
|
||||
{
|
||||
for(size_t i = 0; i < per_thread_iters; ++i)
|
||||
buckets[rnd() / K].fetch_add(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
auto bar_len = w.ws_col - 20;
|
||||
|
||||
std::array<std::thread, M> threads;
|
||||
|
||||
for(auto& bucket : buckets)
|
||||
bucket.store(0);
|
||||
|
||||
for(auto& t : threads)
|
||||
t = std::thread([]() { generate(); });
|
||||
|
||||
for(auto& t : threads)
|
||||
t.join();
|
||||
|
||||
auto max = std::accumulate(buckets.begin(), buckets.end(), 0u,
|
||||
[](auto& acc, auto& x) { return std::max(acc, x.load()); });
|
||||
assert(max != 0u);
|
||||
|
||||
std::cout << std::fixed;
|
||||
|
||||
for(auto& bucket : buckets)
|
||||
{
|
||||
auto x = bucket.load();
|
||||
auto rel = bar_len * x / max;
|
||||
|
||||
for(size_t i = 0; i < rel; ++i)
|
||||
std::cout << "=";
|
||||
|
||||
std::cout << " " << 100.0 * x / N * B - 100 << "%" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "logging/default.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/types/byte.hpp"
|
||||
|
||||
@ -14,9 +14,9 @@ namespace bolt {
|
||||
template <class Stream>
|
||||
class ChunkedDecoder {
|
||||
public:
|
||||
class DecoderError : public BasicException {
|
||||
class DecoderError : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
ChunkedDecoder(Stream &stream) : stream(stream) {}
|
||||
|
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
namespace bolt {
|
||||
|
||||
class StreamError : BasicException {
|
||||
class StreamError : StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T>
|
||||
class SlQueue : Lockable<SpinLock> {
|
||||
public:
|
||||
template <class... Args>
|
||||
void emplace(Args&&... args) {
|
||||
auto guard = acquire_unique();
|
||||
queue.emplace(args...);
|
||||
}
|
||||
|
||||
void push(const T& item) {
|
||||
auto guard = acquire_unique();
|
||||
queue.push(item);
|
||||
}
|
||||
|
||||
T front() {
|
||||
auto guard = acquire_unique();
|
||||
return queue.front();
|
||||
}
|
||||
|
||||
void pop() {
|
||||
auto guard = acquire_unique();
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
bool pop(T& item) {
|
||||
auto guard = acquire_unique();
|
||||
if (queue.empty()) return false;
|
||||
|
||||
item = std::move(queue.front());
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
auto guard = acquire_unique();
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
auto guard = acquire_unique();
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<T> queue;
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class K, class T>
|
||||
class SlRbTree : Lockable<SpinLock> {
|
||||
public:
|
||||
private:
|
||||
std::map<K, T> tree;
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stack>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T>
|
||||
class SpinLockStack : Lockable<SpinLock> {
|
||||
public:
|
||||
T pop() {
|
||||
auto guard = acquire();
|
||||
|
||||
T elem = stack.top();
|
||||
stack.pop();
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
void push(const T& elem) {
|
||||
auto guard = acquire();
|
||||
|
||||
stack.push(elem);
|
||||
}
|
||||
|
||||
private:
|
||||
std::stack<T> stack;
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
template <class T>
|
||||
class ArrayStack {
|
||||
private:
|
||||
};
|
@ -5,13 +5,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
/**
|
||||
* Thrown when something (Edge or a Vertex) can not
|
||||
* be created. Typically due to database overload.
|
||||
*/
|
||||
class CreationException : public BasicException {
|
||||
class CreationException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
1
src/io/network/.gitignore
vendored
1
src/io/network/.gitignore
vendored
@ -1 +0,0 @@
|
||||
test
|
@ -9,9 +9,9 @@
|
||||
|
||||
namespace io {
|
||||
|
||||
class EpollError : BasicException {
|
||||
class EpollError : StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
class Epoll {
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
namespace io {
|
||||
|
||||
class NetworkError : public BasicException {
|
||||
class NetworkError : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
}
|
||||
|
@ -1,126 +0,0 @@
|
||||
#include <signal.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "http/request.hpp"
|
||||
#include "http/response.hpp"
|
||||
|
||||
#include "http/worker.hpp"
|
||||
#include "socket.hpp"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LOG_DEBUG(x) std::cout << x << std::endl;
|
||||
#else
|
||||
#define LOG_DEBUG(x)
|
||||
#endif
|
||||
|
||||
std::hash<std::thread::id> hash;
|
||||
|
||||
constexpr unsigned K = 128;
|
||||
|
||||
std::array<http::Parser<http::Request, http::Response>, K> workers;
|
||||
std::array<std::thread, K> threads;
|
||||
|
||||
std::atomic<bool> alive{true};
|
||||
|
||||
void exiting() { LOG_DEBUG("Exiting..."); }
|
||||
|
||||
void sigint_handler(int) {
|
||||
exiting();
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
#define MAXEVENTS 64
|
||||
|
||||
int main(void) {
|
||||
// std::atexit(exiting);
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
for (size_t i = 0; i < workers.size(); ++i) {
|
||||
auto &w = workers[i];
|
||||
|
||||
threads[i] = std::thread([i, &w]() {
|
||||
while (alive) {
|
||||
LOG_DEBUG("waiting for events on thread " << i);
|
||||
w.wait_and_process_events();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* size_t WORKERS = std::thread::hardware_concurrency(); */
|
||||
|
||||
/* std::vector<io::Worker> workers; */
|
||||
/* workers.resize(WORKERS); */
|
||||
|
||||
/* for(size_t i = 0; i < WORKERS; ++i) */
|
||||
/* { */
|
||||
/* workers.push_back(std::move(io::Worker())); */
|
||||
/* workers.back().start(); */
|
||||
/* } */
|
||||
|
||||
int idx = 0;
|
||||
|
||||
auto socket = io::Socket::bind("0.0.0.0", "7474");
|
||||
socket.set_non_blocking();
|
||||
socket.listen(1024);
|
||||
|
||||
int efd, s;
|
||||
struct epoll_event event;
|
||||
struct epoll_event *events;
|
||||
|
||||
efd = epoll_create1(0);
|
||||
if (efd == -1) {
|
||||
perror("epoll_create");
|
||||
abort();
|
||||
}
|
||||
|
||||
event.data.fd = socket;
|
||||
event.events = EPOLLIN | EPOLLET;
|
||||
s = epoll_ctl(efd, EPOLL_CTL_ADD, socket, &event);
|
||||
if (s == -1) {
|
||||
perror("epoll_ctl");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Buffer where events are returned */
|
||||
events = static_cast<struct epoll_event *>(calloc(MAXEVENTS, sizeof event));
|
||||
|
||||
/* The event loop */
|
||||
while (1) {
|
||||
int n, i;
|
||||
|
||||
LOG_DEBUG("acceptor waiting for events");
|
||||
n = epoll_wait(efd, events, MAXEVENTS, -1);
|
||||
|
||||
LOG_DEBUG("acceptor recieved " << n << " connection requests");
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||
|
||||
(!(events[i].events & EPOLLIN))) {
|
||||
/* An error has occured on this fd, or the socket is not
|
||||
ready for reading (why were we notified then?) */
|
||||
fprintf(stderr, "epoll error\n");
|
||||
close(events[i].data.fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (socket == events[i].data.fd) {
|
||||
/* We have a notification on the listening socket, which
|
||||
means one or more incoming connections. */
|
||||
while (true) {
|
||||
LOG_DEBUG("trying to accept connection on thread " << idx);
|
||||
if (!workers[idx].accept(socket)) break;
|
||||
|
||||
LOG_DEBUG("Accepted a new connection on thread " << idx);
|
||||
idx = (idx + 1) % workers.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(events);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
namespace io {
|
||||
|
||||
class TlsError : public BasicException {
|
||||
class TlsError : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
}
|
||||
|
@ -79,9 +79,9 @@ class QueryEngine : public Loggable {
|
||||
logger.error("QueryEngineException: {}", std::string(e.what()));
|
||||
throw e;
|
||||
} catch (std::exception &e) {
|
||||
throw BasicException(e.what());
|
||||
throw StacktraceException(e.what());
|
||||
} catch (...) {
|
||||
throw BasicException("unknown query engine exception");
|
||||
throw StacktraceException("unknown query engine exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class CppCodeGeneratorException : public BasicException {
|
||||
class CppCodeGeneratorException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class DecoderException : public BasicException {
|
||||
class DecoderException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class PlanCompilationException : public BasicException {
|
||||
class PlanCompilationException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class PlanExecutionException : public BasicException {
|
||||
class PlanExecutionException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class QueryEngineException : public BasicException {
|
||||
class QueryEngineException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -10,25 +10,19 @@ using std::endl;
|
||||
|
||||
// Query: {{query}}
|
||||
|
||||
class {{class_name}} : public PlanInterface<{{stream}}>
|
||||
{
|
||||
class {{class_name}} : public PlanInterface<{{stream}}> {
|
||||
public:
|
||||
|
||||
bool run(Db &db, const PlanArgsT &args,
|
||||
{{stream}} &stream) override
|
||||
{
|
||||
{{code}}
|
||||
bool run(Db &db, const PlanArgsT &args, {{stream}} &stream) override {
|
||||
{{code}}
|
||||
}
|
||||
|
||||
~{{class_name}}() {}
|
||||
};
|
||||
|
||||
extern "C" PlanInterface<{{stream}}>* produce()
|
||||
{
|
||||
extern "C" PlanInterface<{{stream}}>* produce() {
|
||||
return new {{class_name}}();
|
||||
}
|
||||
|
||||
extern "C" void destruct(PlanInterface<{{stream}}>* p)
|
||||
{
|
||||
extern "C" void destruct(PlanInterface<{{stream}}>* p) {
|
||||
delete p;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "logging/default.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
@ -18,9 +18,9 @@ using std::endl;
|
||||
// but sometimes that might be a problem
|
||||
namespace {
|
||||
|
||||
class CodeLineFormatException : public BasicException {
|
||||
class CodeLineFormatException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/total_ordering.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
@ -82,9 +82,9 @@ class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||
* trying to perform operations (such as addition) on TypedValues
|
||||
* of incompatible Types.
|
||||
*/
|
||||
class TypedValueException : public BasicException {
|
||||
class TypedValueException : public StacktraceException {
|
||||
public:
|
||||
using ::BasicException::BasicException;
|
||||
using ::StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
// comparison operators
|
||||
|
6
src/test/.gitignore
vendored
6
src/test/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
# ignore object and executable files
|
||||
*.o
|
||||
tests
|
||||
|
||||
# ignore the library, download your own copy via install.sh
|
||||
catch.hpp
|
@ -1,29 +0,0 @@
|
||||
TARGET = tests
|
||||
LIBS = -lm
|
||||
CC = c++
|
||||
CFLAGS = -g -Wall -std=c++1y
|
||||
INCLUDE = "../"
|
||||
|
||||
.PHONY: default all clean
|
||||
|
||||
default: $(TARGET)
|
||||
all: default
|
||||
|
||||
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
|
||||
HEADERS = $(wildcard *.hpp)
|
||||
|
||||
%.o: %.cpp $(HEADERS)
|
||||
$(CC) $(CFLAGS) -I $(INCLUDE) -c $< -o $@
|
||||
|
||||
.PRECIOUS: $(TARGET) $(OBJECTS)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(OBJECTS) -Wall $(LIBS) -o $@
|
||||
|
||||
clean:
|
||||
-rm -f *.o
|
||||
-rm -f $(TARGET)
|
||||
|
||||
test:
|
||||
make
|
||||
./tests
|
@ -1,3 +0,0 @@
|
||||
# NOTE
|
||||
Files with .old extension are old test files. They have to be rewritten because of changes in the appropriate project files.
|
||||
bitblock.hpp is going to be replaced with the dynamic_bitset
|
@ -1,186 +0,0 @@
|
||||
#include <thread>
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "data_structures/bitset/bitblock.hpp"
|
||||
#include "sync/spinlock.hpp"
|
||||
|
||||
TEST_CASE("BitBlock should be empty on construction")
|
||||
{
|
||||
BitBlock<> bb;
|
||||
REQUIRE(bb.block.load(std::memory_order_relaxed) == 0);
|
||||
}
|
||||
|
||||
template <class block_t, size_t N>
|
||||
void test_sizes()
|
||||
{
|
||||
BitBlock<block_t, N> bb;
|
||||
auto bits = bb.bits;
|
||||
auto size = bb.size;
|
||||
|
||||
REQUIRE(bits == (8 * sizeof(block_t)));
|
||||
REQUIRE(size == (8 * sizeof(block_t) / N));
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
struct test_sizes_loop : test_sizes_loop<T, N - 1>
|
||||
{
|
||||
test_sizes_loop()
|
||||
{
|
||||
test_sizes<T, N>();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct test_sizes_loop<T, 1>
|
||||
{
|
||||
test_sizes_loop()
|
||||
{
|
||||
test_sizes<T, 1>();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("BitBlock bit sizes should be set correctly")
|
||||
{
|
||||
test_sizes_loop<uint8_t, 8> size_test8;
|
||||
test_sizes_loop<uint16_t, 8> size_test16;
|
||||
test_sizes_loop<uint32_t, 8> size_test32;
|
||||
test_sizes_loop<uint64_t, 8> size_test64;
|
||||
}
|
||||
|
||||
TEST_CASE("Values load correctly from the BitBlock")
|
||||
{
|
||||
constexpr uint32_t k = 0xCA3F;
|
||||
|
||||
SECTION("Block size = 1")
|
||||
{
|
||||
constexpr size_t N = 1;
|
||||
|
||||
BitBlock<uint32_t, N> bb;
|
||||
bb.block.store(k);
|
||||
|
||||
for(size_t i = 0; i < bb.size; ++i)
|
||||
REQUIRE(bb.at(i) == ((k >> i) & 1));
|
||||
}
|
||||
|
||||
SECTION("Block size = 4")
|
||||
{
|
||||
constexpr size_t N = 4;
|
||||
|
||||
BitBlock<uint32_t, N> bb;
|
||||
bb.block.store(k);
|
||||
|
||||
for(size_t i = 0; i < bb.size; ++i)
|
||||
REQUIRE(bb.at(i) == ((k >> (N * i)) & 0xF));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("You can set a bit in a BitBlock and get the result")
|
||||
{
|
||||
SECTION("Block size = 1")
|
||||
{
|
||||
BitBlock<uint8_t, 1> bb;
|
||||
|
||||
for(size_t i = 0; i < bb.bits; ++i)
|
||||
{
|
||||
bb.set(i, 1);
|
||||
|
||||
for(size_t j = 0; j < bb.bits; ++j)
|
||||
{
|
||||
if(j <= i)
|
||||
REQUIRE(bb.at(j) == 1);
|
||||
else
|
||||
REQUIRE(bb.at(j) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Block size = 2")
|
||||
{
|
||||
constexpr size_t N = 2;
|
||||
|
||||
BitBlock<uint16_t, N> bb;
|
||||
|
||||
for(size_t i = 0; i < bb.size; ++i)
|
||||
{
|
||||
auto k = i % (1 << N);
|
||||
bb.set(i, k);
|
||||
|
||||
for(size_t j = 0; j < bb.size; j++)
|
||||
{
|
||||
auto l = j % (1 << N);
|
||||
|
||||
if(j <= i)
|
||||
REQUIRE(bb.at(j) == l);
|
||||
else
|
||||
REQUIRE(bb.at(j) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Block size = 5")
|
||||
{
|
||||
constexpr size_t N = 5;
|
||||
|
||||
BitBlock<uint16_t, N> bb;
|
||||
|
||||
for(size_t i = 0; i < bb.size; ++i)
|
||||
{
|
||||
auto k = i % (1 << N);
|
||||
bb.set(i, k);
|
||||
|
||||
for(size_t j = 0; j < bb.size; j++)
|
||||
{
|
||||
auto l = j % (1 << N);
|
||||
|
||||
if(j <= i)
|
||||
REQUIRE(bb.at(j) == l);
|
||||
else
|
||||
REQUIRE(bb.at(j) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
void bitblock_thead_test(BitBlock<T, N>* bb, size_t idx, size_t n)
|
||||
{
|
||||
static SpinLock lock;
|
||||
uint8_t x;
|
||||
|
||||
uint64_t sum = 0, actual = 0;
|
||||
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
int y = i % 2;
|
||||
actual += i * y;
|
||||
|
||||
bb->set(idx, y);
|
||||
x = bb->at(idx);
|
||||
|
||||
sum += i * x;
|
||||
|
||||
bb->clear(idx);
|
||||
x = bb->at(idx);
|
||||
}
|
||||
|
||||
auto guard = std::unique_lock<SpinLock>(lock);
|
||||
REQUIRE(sum == actual);
|
||||
}
|
||||
|
||||
TEST_CASE("(try to) Test multithreaded correctness")
|
||||
{
|
||||
BitBlock<uint64_t, 1> bb;
|
||||
|
||||
constexpr int N = 2;
|
||||
constexpr int K = 500000;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
threads.push_back(std::thread(
|
||||
bitblock_thead_test<uint64_t, 1>, &bb, i, K));
|
||||
|
||||
for(auto& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Installing Catch..."
|
||||
|
||||
wget https://raw.githubusercontent.com/philsquared/Catch/develop/single_include/catch.hpp
|
||||
|
||||
echo "All done!"
|
@ -1,48 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "data_structures/skiplist/new_height.hpp"
|
||||
|
||||
TEST_CASE("New height distribution must be approx. 1/2 1/4 1/8 ...")
|
||||
{
|
||||
// NEVER forget to do this.
|
||||
xorshift::init();
|
||||
|
||||
constexpr int N = 1e8;
|
||||
constexpr int max_height = 8;
|
||||
|
||||
// 2% is a good margin to start with, no?
|
||||
// depends on the max_height and N, beware.
|
||||
constexpr double error_margin = 0.02;
|
||||
|
||||
// array to store the number of i-height towers generated
|
||||
std::array<int, max_height> heights;
|
||||
heights.fill(0);
|
||||
|
||||
// generate a tower and put it in a box with his same-height brothers
|
||||
for(int i = 0; i < N; ++i)
|
||||
heights[new_height(max_height) - 1]++;
|
||||
|
||||
// evaluate the number of towers in all of the boxes
|
||||
for(int i = 1; i < max_height; ++i)
|
||||
{
|
||||
// compute how much towers should be in this box
|
||||
int x = N / (2 << i);
|
||||
|
||||
// the actual number of towers
|
||||
int xx = heights[i];
|
||||
|
||||
// relative error
|
||||
double relative_error = (double)std::abs(xx - x) / xx;
|
||||
|
||||
// this might fail actually in some cases, especially if N is not big
|
||||
// enough. it's probabilistic after all :D
|
||||
REQUIRE(relative_error < error_margin);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("You can add an item to the skiplist")
|
||||
{
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "sync/spinlock.hpp"
|
||||
|
||||
TEST_CASE("a thread can acquire and release the lock", "[spinlock]")
|
||||
{
|
||||
{
|
||||
std::unique_lock<SpinLock> lock;
|
||||
// I HAS A LOCK!
|
||||
}
|
||||
|
||||
REQUIRE(true);
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
|
||||
SpinLock lock;
|
||||
|
||||
void test_lock()
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
{
|
||||
std::unique_lock<SpinLock> guard(lock);
|
||||
x++;
|
||||
|
||||
std::this_thread::sleep_for(25ms);
|
||||
|
||||
REQUIRE(x < 2);
|
||||
x--;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("only one thread at a time can own the lock", "[spinlock]")
|
||||
{
|
||||
constexpr int N = 64;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
threads.push_back(std::thread(test_lock));
|
||||
|
||||
for(auto& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "transaction/transactionengine.hpp"
|
||||
#include "sync/spinlock.hpp"
|
||||
|
||||
TEST_CASE("(try to) test correctness of the transaction life cycle")
|
||||
{
|
||||
constexpr int THREADS = 16;
|
||||
constexpr int TRANSACTIONS = 10;
|
||||
|
||||
TransactionEngine<uint64_t, SpinLock> engine(0);
|
||||
std::vector<uint64_t> sums;
|
||||
|
||||
sums.resize(THREADS);
|
||||
|
||||
auto f = [&engine, &sums](int idx, int n)
|
||||
{
|
||||
uint64_t sum = 0;
|
||||
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
auto t = engine.begin();
|
||||
sum += t.id;
|
||||
engine.commit(t);
|
||||
}
|
||||
|
||||
sums[idx] = sum;
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < THREADS; ++i)
|
||||
threads.push_back(std::thread(f, i, TRANSACTIONS));
|
||||
|
||||
for(auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
uint64_t sum_computed = 0;
|
||||
|
||||
for(int i = 0; i < THREADS; ++i)
|
||||
sum_computed += sums[i];
|
||||
|
||||
uint64_t sum_actual = 0;
|
||||
|
||||
for(uint64_t i = 0; i <= THREADS * TRANSACTIONS; ++i)
|
||||
sum_actual += i;
|
||||
|
||||
REQUIRE(sum_computed == sum_actual);
|
||||
}
|
1
src/threading/.gitignore
vendored
1
src/threading/.gitignore
vendored
@ -1 +0,0 @@
|
||||
test
|
@ -1,98 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include "hazard_store.hpp"
|
||||
|
||||
class hazard_ptr {
|
||||
static constexpr size_t EMPTY = -1;
|
||||
static constexpr uintptr_t NULLPTR = 0;
|
||||
|
||||
public:
|
||||
hazard_ptr() = default;
|
||||
|
||||
template <class T>
|
||||
hazard_ptr(const T* ptr) : ptr(reinterpret_cast<uintptr_t>(ptr)) {
|
||||
if (ptr == nullptr) return;
|
||||
|
||||
idx = HazardStore::get().acquire(this->ptr);
|
||||
}
|
||||
|
||||
hazard_ptr(const hazard_ptr&) = delete;
|
||||
|
||||
hazard_ptr(hazard_ptr&& other) { *this = std::move(other); }
|
||||
|
||||
~hazard_ptr() { reset(); }
|
||||
|
||||
void reset() {
|
||||
if (idx == EMPTY) return;
|
||||
|
||||
HazardStore::get().release(idx);
|
||||
detach();
|
||||
}
|
||||
|
||||
hazard_ptr& operator=(hazard_ptr&& other) {
|
||||
reset();
|
||||
|
||||
ptr = other.ptr;
|
||||
idx = other.idx;
|
||||
|
||||
other.detach();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
uintptr_t get() const { return ptr; }
|
||||
|
||||
template <class T>
|
||||
operator T*() const {
|
||||
return reinterpret_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
friend bool operator==(const hazard_ptr& lhs, uintptr_t rhs) {
|
||||
return lhs.ptr == rhs;
|
||||
}
|
||||
|
||||
friend bool operator==(uintptr_t lhs, const hazard_ptr& rhs) {
|
||||
return operator==(rhs, lhs);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend bool operator==(const hazard_ptr& lhs, const T* const rhs) {
|
||||
return lhs.ptr == reinterpret_cast<uintptr_t>(rhs);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend bool operator==(const T* const lhs, const hazard_ptr& rhs) {
|
||||
return operator==(rhs, lhs);
|
||||
}
|
||||
|
||||
friend bool operator!=(const hazard_ptr& lhs, uintptr_t rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend bool operator!=(uintptr_t lhs, const hazard_ptr& rhs) {
|
||||
return operator!=(rhs, lhs);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend bool operator!=(const hazard_ptr& lhs, const T* const rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend bool operator!=(const T* const lhs, const hazard_ptr& rhs) {
|
||||
return operator!=(rhs, lhs);
|
||||
}
|
||||
|
||||
private:
|
||||
uintptr_t ptr{NULLPTR};
|
||||
size_t idx{EMPTY};
|
||||
|
||||
void detach() {
|
||||
ptr = NULLPTR;
|
||||
idx = EMPTY;
|
||||
}
|
||||
};
|
@ -1,81 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "id.hpp"
|
||||
|
||||
class HazardPointerError : std::runtime_error {
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class HazardStore {
|
||||
using atomic_hp_t = std::atomic<uintptr_t>;
|
||||
|
||||
static constexpr uintptr_t NULLPTR = 0;
|
||||
|
||||
friend class hazard_ptr;
|
||||
|
||||
HazardStore(size_t N, size_t K) : N(N), K(K), ptrs(new atomic_hp_t[N * K]) {}
|
||||
|
||||
public:
|
||||
HazardStore(const HazardStore&) = delete;
|
||||
HazardStore(HazardStore&&) = delete;
|
||||
|
||||
HazardStore& operator=(const HazardStore&) = delete;
|
||||
|
||||
static HazardStore& get() {
|
||||
static constexpr size_t N = 16; // number of threds
|
||||
static constexpr size_t K = 128; // pointers per thread
|
||||
|
||||
static HazardStore hp(N, K);
|
||||
return hp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool scan(T* ptr) {
|
||||
return scan(reinterpret_cast<uintptr_t>(ptr));
|
||||
}
|
||||
|
||||
bool scan(uintptr_t ptr) {
|
||||
assert(ptr != NULLPTR);
|
||||
|
||||
for (size_t i = 0; i < N * K; ++i) {
|
||||
auto& hazard = ptrs[i];
|
||||
|
||||
if (hazard == ptr) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t N, K;
|
||||
std::unique_ptr<atomic_hp_t[]> ptrs;
|
||||
|
||||
size_t acquire(uintptr_t ptr) {
|
||||
assert(ptr != NULLPTR);
|
||||
auto idx = this_thread::id;
|
||||
|
||||
for (auto i = N * idx; i < N * idx + K; ++i) {
|
||||
auto& hazard = ptrs[i];
|
||||
|
||||
if (hazard.load(std::memory_order_relaxed) == NULLPTR) continue;
|
||||
|
||||
// this MUST be seq_cst, otherwise garbage collector might not see
|
||||
// the hazard pointer even if it is set
|
||||
hazard.store(ptr, std::memory_order_seq_cst);
|
||||
return i;
|
||||
}
|
||||
|
||||
throw HazardPointerError("Exhausted all hazard pointers");
|
||||
}
|
||||
|
||||
void release(size_t idx) {
|
||||
assert(ptrs[idx] != NULLPTR);
|
||||
ptrs[idx].store(NULLPTR, std::memory_order_release);
|
||||
}
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace this_thread {
|
||||
// thread_local unsigned id = 0;
|
||||
};
|
@ -1,3 +1,6 @@
|
||||
//
|
||||
// Created by buda on 18/02/17.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
8
src/threading/sync/lock_timeout_exception.hpp
Normal file
8
src/threading/sync/lock_timeout_exception.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
class LockTimeoutException : public BasicException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
};
|
@ -1,22 +1,16 @@
|
||||
#pragma onces
|
||||
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
class LockExpiredError : public std::runtime_error {
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
#include "lock_timeout_exception.hpp"
|
||||
|
||||
template <size_t microseconds = 250>
|
||||
class TimedSpinLock {
|
||||
public:
|
||||
TimedSpinLock(std::chrono::seconds expiration) : expiration(expiration) {}
|
||||
|
||||
TimedSpinLock(std::chrono::milliseconds expiration)
|
||||
: expiration(expiration) {}
|
||||
TimedSpinLock(std::chrono::seconds expiration) : expiration_(expiration) {}
|
||||
|
||||
void lock() {
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
@ -28,8 +22,8 @@ class TimedSpinLock {
|
||||
// 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");
|
||||
if (clock::now() - start > expiration_)
|
||||
throw LockTimeoutException("This lock has expired");
|
||||
|
||||
usleep(microseconds);
|
||||
}
|
||||
@ -38,7 +32,7 @@ class TimedSpinLock {
|
||||
void unlock() { lock_flag.clear(std::memory_order_release); }
|
||||
|
||||
private:
|
||||
std::chrono::milliseconds expiration;
|
||||
std::chrono::milliseconds expiration_;
|
||||
|
||||
// guaranteed by standard to be lock free!
|
||||
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
|
||||
|
@ -1,21 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "pool.hpp"
|
||||
|
||||
int main(void) {
|
||||
auto size = 7;
|
||||
auto N = 1000000;
|
||||
|
||||
Pool pool(size);
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
pool.run(
|
||||
[](int) {
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 2000; ++i) sum += i % 7;
|
||||
},
|
||||
i);
|
||||
|
||||
return 0;
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
#include "id.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
class Thread {
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/option.hpp"
|
||||
|
||||
#define REGISTER_ARGS(argc, argv) \
|
||||
@ -29,10 +29,10 @@ namespace {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
class ProgramArgumentException : public BasicException {
|
||||
class ProgramArgumentException : public StacktraceException {
|
||||
public:
|
||||
ProgramArgumentException(const std::string &mess)
|
||||
: BasicException("ProgramArgumentException: " + mess + ".") {}
|
||||
: StacktraceException("ProgramArgumentException: " + mess + ".") {}
|
||||
};
|
||||
|
||||
class ProgramArguments {
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class Datetime {
|
||||
public:
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class DatetimeError : public BasicException {
|
||||
class DatetimeError : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,30 +1,50 @@
|
||||
//
|
||||
// Created by buda on 18/02/17.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
#include "utils/stacktrace/stacktrace.hpp"
|
||||
|
||||
/**
|
||||
* @brief BasicException
|
||||
*
|
||||
* Just a wrapper around std::exception.
|
||||
*/
|
||||
class BasicException : public std::exception {
|
||||
public:
|
||||
BasicException(const std::string &message) noexcept : message_(message) {
|
||||
Stacktrace stacktrace;
|
||||
message_.append(stacktrace.dump());
|
||||
}
|
||||
/** Constructor (C strings).
|
||||
*
|
||||
* @param message C-style string error message.
|
||||
* The string contents are copied upon construction.
|
||||
* Hence, responsibility for deleting the char* lies
|
||||
* with the caller.
|
||||
*/
|
||||
explicit BasicException(const char* message) : msg_(message) {}
|
||||
|
||||
template <class... Args>
|
||||
BasicException(const std::string &format, Args &&... args) noexcept
|
||||
: BasicException(fmt::format(format, std::forward<Args>(args)...)) {}
|
||||
/**
|
||||
* Constructor (C++ STL strings).
|
||||
*
|
||||
* @param message The error message.
|
||||
*/
|
||||
explicit BasicException(const std::string& message) : msg_(message) {}
|
||||
|
||||
template <class... Args>
|
||||
BasicException(const char *format, Args &&... args) noexcept
|
||||
: BasicException(fmt::format(std::string(format),
|
||||
std::forward<Args>(args)...)) {}
|
||||
/**
|
||||
* Destructor. Virtual to allow for subclassing.
|
||||
*/
|
||||
virtual ~BasicException() {}
|
||||
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
/**
|
||||
* Returns a pointer to the (constant) error description.
|
||||
*
|
||||
* @return A pointer to a const char*. The underlying memory
|
||||
* is in possession of the BasicException object. Callers must
|
||||
* not attempt to free the memory.
|
||||
*/
|
||||
const char* what() const noexcept override { return msg_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
protected:
|
||||
/**
|
||||
* Error message.
|
||||
*/
|
||||
std::string msg_;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class NonExhaustiveSwitch : public BasicException {
|
||||
class NonExhaustiveSwitch : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class NotYetImplemented : public BasicException {
|
||||
class NotYetImplemented : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
|
||||
NotYetImplemented() : BasicException("") {}
|
||||
NotYetImplemented() : StacktraceException("") {}
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
class OutOfMemory : public BasicException {
|
||||
class OutOfMemory : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
30
src/utils/exceptions/stacktrace_exception.hpp
Normal file
30
src/utils/exceptions/stacktrace_exception.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
#include "utils/stacktrace/stacktrace.hpp"
|
||||
|
||||
class StacktraceException : public std::exception {
|
||||
public:
|
||||
StacktraceException(const std::string &message) noexcept : message_(message) {
|
||||
Stacktrace stacktrace;
|
||||
message_.append(stacktrace.dump());
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
StacktraceException(const std::string &format, Args &&... args) noexcept
|
||||
: StacktraceException(fmt::format(format, std::forward<Args>(args)...)) {}
|
||||
|
||||
template <class... Args>
|
||||
StacktraceException(const char *format, Args &&... args) noexcept
|
||||
: StacktraceException(fmt::format(std::string(format),
|
||||
std::forward<Args>(args)...)) {}
|
||||
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
@ -23,7 +23,7 @@ namespace fs = std::experimental::filesystem;
|
||||
#include "logging/loggable.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/linux.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
@ -131,9 +131,9 @@ struct FSEvent : public FSEventBase {
|
||||
/**
|
||||
* Custom FSWatcher Exception
|
||||
*/
|
||||
class FSWatcherException : public BasicException {
|
||||
class FSWatcherException : public StacktraceException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -15,13 +15,13 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/not_yet_implemented.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
|
||||
namespace linux_os {
|
||||
class LinuxException : public BasicException {
|
||||
using BasicException::BasicException;
|
||||
class LinuxException : public StacktraceException {
|
||||
using StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "utils/hashing/fnv64.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
using utils::random::StringGenerator;
|
||||
using StringHashFunction = std::function<uint64_t(const std::string &)>;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "utils/hashing/fnv64.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
using utils::random::NumberGenerator;
|
||||
using IntegerGenerator = NumberGenerator<std::uniform_int_distribution<int>,
|
||||
|
@ -1,9 +1,9 @@
|
||||
#define LOG_NO_INFO 1
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "benchmark/benchmark_api.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
auto BM_Strip = [](benchmark::State &state, auto &function, std::string query) {
|
||||
|
42
tests/concurrent/futex.cpp
Normal file
42
tests/concurrent/futex.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include "threading/sync/futex.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
Futex futex;
|
||||
int x = 0;
|
||||
|
||||
void test_lock(int id) {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, 1000);
|
||||
|
||||
for (int i = 0; i < 5000000; ++i) {
|
||||
{
|
||||
std::unique_lock<Futex> guard(futex);
|
||||
x++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
||||
permanent_assert(x == 1,
|
||||
"Other thread shouldn't be able to "
|
||||
"change the value of x");
|
||||
x--;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
constexpr int N = 16;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for (int i = 0; i < N; ++i) threads.push_back(std::thread(test_lock, i));
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
41
tests/concurrent/spinlock.cpp
Normal file
41
tests/concurrent/spinlock.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
int x = 0;
|
||||
SpinLock lock;
|
||||
|
||||
void test_lock() {
|
||||
using namespace std::literals;
|
||||
|
||||
{
|
||||
std::unique_lock<SpinLock> guard(lock);
|
||||
x++;
|
||||
|
||||
std::this_thread::sleep_for(25ms);
|
||||
|
||||
permanent_assert(
|
||||
x < 2,
|
||||
"x always has to be less than 2 (other "
|
||||
"threads shouldn't be able to change the x simultaneously");
|
||||
x--;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr int N = 16;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for (int i = 0; i < N; ++i) threads.push_back(std::thread(test_lock));
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
46
tests/concurrent/transaction_engine.cpp
Normal file
46
tests/concurrent/transaction_engine.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "transactions/engine.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
int main() {
|
||||
// (try to) test correctness of the transaction life cycle
|
||||
constexpr int THREADS = 16;
|
||||
constexpr int TRANSACTIONS = 10;
|
||||
|
||||
tx::Engine engine;
|
||||
std::vector<uint64_t> sums;
|
||||
|
||||
sums.resize(THREADS);
|
||||
|
||||
auto f = [&engine, &sums](int idx, int n) {
|
||||
uint64_t sum = 0;
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
auto& t = engine.begin();
|
||||
sum += t.id;
|
||||
engine.commit(t);
|
||||
}
|
||||
|
||||
sums[idx] = sum;
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for (int i = 0; i < THREADS; ++i)
|
||||
threads.push_back(std::thread(f, i, TRANSACTIONS));
|
||||
|
||||
for (auto& thread : threads) thread.join();
|
||||
|
||||
uint64_t sum_computed = 0;
|
||||
|
||||
for (int i = 0; i < THREADS; ++i) sum_computed += sums[i];
|
||||
|
||||
uint64_t sum_actual = 0;
|
||||
for (uint64_t i = 2; i <= THREADS * TRANSACTIONS + 1; ++i) sum_actual += i;
|
||||
// the range is strange because the first transaction gets transaction id 2
|
||||
|
||||
std::cout << sum_computed << " " << sum_actual << std::endl;
|
||||
permanent_assert(sum_computed == sum_actual, "sums have to be the same");
|
||||
}
|
60
tests/manual/binomial.cpp
Normal file
60
tests/manual/binomial.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* Plots the distribution histogram of the fast_binomial algorithm
|
||||
* (spoiler alert: it's pleasingly (1/2)^N all the way :D)
|
||||
*/
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/random/fast_binomial.hpp"
|
||||
|
||||
static constexpr unsigned B = 24;
|
||||
static thread_local FastBinomial<B> rnd;
|
||||
|
||||
static constexpr unsigned M = 4;
|
||||
static constexpr size_t N = 1ULL << 34;
|
||||
static constexpr size_t per_thread_iters = N / M;
|
||||
|
||||
std::array<std::atomic<uint64_t>, B> buckets;
|
||||
|
||||
void generate() {
|
||||
for (size_t i = 0; i < per_thread_iters; ++i) buckets[rnd() - 1].fetch_add(1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
auto bar_len = w.ws_col - 20;
|
||||
|
||||
std::array<std::thread, M> threads;
|
||||
|
||||
for (auto& bucket : buckets) bucket.store(0);
|
||||
|
||||
for (auto& t : threads) t = std::thread([]() { generate(); });
|
||||
|
||||
for (auto& t : threads) t.join();
|
||||
|
||||
auto max = std::accumulate(
|
||||
buckets.begin(), buckets.end(), (uint64_t)0,
|
||||
[](auto& acc, auto& x) { return std::max(acc, x.load()); });
|
||||
|
||||
std::cout << std::fixed;
|
||||
|
||||
for (size_t i = 0; i < buckets.size(); ++i) {
|
||||
auto x = buckets[i].load();
|
||||
auto rel = bar_len * x / max;
|
||||
|
||||
std::cout << std::setw(2) << i + 1 << " ";
|
||||
|
||||
for (size_t i = 0; i < rel; ++i) std::cout << "=";
|
||||
|
||||
std::cout << " " << 100 * (double)x / N << "%" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
65
tests/manual/endinan.cpp
Normal file
65
tests/manual/endinan.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#include <byteswap.h>
|
||||
|
||||
char b[8] = {1, 2, 3, 4, 0, 0, 0, 1};
|
||||
|
||||
int64_t safe_int64(const char* b) {
|
||||
return int64_t(b[0]) << 56 | int64_t(b[1]) << 48 | int64_t(b[2]) << 40 |
|
||||
int64_t(b[3]) << 32 | int64_t(b[4]) << 24 | int64_t(b[5]) << 16 |
|
||||
int64_t(b[6]) << 8 | int64_t(b[7]);
|
||||
}
|
||||
|
||||
int64_t unsafe_int64(const char* b) {
|
||||
auto i = reinterpret_cast<const int64_t*>(b);
|
||||
return __bswap_64(*i);
|
||||
}
|
||||
|
||||
int32_t safe_int32(const char* b) {
|
||||
return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
|
||||
}
|
||||
|
||||
int32_t unsafe_int32(const char* b) {
|
||||
auto i = reinterpret_cast<const int32_t*>(b);
|
||||
return __bswap_32(*i);
|
||||
}
|
||||
|
||||
[[clang::optnone]] void test(uint64_t n) {
|
||||
for (uint64_t i = 0; i < n; ++i) unsafe_int64(b);
|
||||
}
|
||||
|
||||
uint8_t f[8] = {0x3F, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A};
|
||||
|
||||
double ff = 1.1;
|
||||
|
||||
double get_double(const uint8_t* b) {
|
||||
auto v = __bswap_64(*reinterpret_cast<const uint64_t*>(b));
|
||||
return *reinterpret_cast<const double*>(&v);
|
||||
}
|
||||
|
||||
void print_hex(const char* buf, size_t n) {
|
||||
for (size_t i = 0; i < n; ++i) printf("%02X ", (unsigned char)buf[i]);
|
||||
}
|
||||
|
||||
void print_hex(const uint8_t* buf, size_t n) { print_hex((const char*)buf, n); }
|
||||
|
||||
int main(void) {
|
||||
auto dd = get_double(f);
|
||||
|
||||
print_hex(f, 8);
|
||||
|
||||
std::cout << std::endl;
|
||||
print_hex((const uint8_t*)(&ff), 8);
|
||||
|
||||
std::cout << std::endl;
|
||||
print_hex((const uint8_t*)(&dd), 8);
|
||||
|
||||
std::cout << "dd: " << dd << std::endl;
|
||||
std::cout << "Safe: " << safe_int64(b) << std::endl;
|
||||
std::cout << unsafe_int64(b) << std::endl;
|
||||
|
||||
test(1000000ull);
|
||||
|
||||
return 0;
|
||||
}
|
60
tests/manual/xorshift.cpp
Normal file
60
tests/manual/xorshift.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* Plots the distribution histogram of the xorshift algorithm
|
||||
* (spoiler alert: it's pleasingly uniform all the way :D)
|
||||
*/
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/random/xorshift128plus.hpp"
|
||||
|
||||
static thread_local Xorshift128plus rnd;
|
||||
static constexpr unsigned B = 1 << 10;
|
||||
static constexpr uint64_t K = (uint64_t)(-1) / B;
|
||||
|
||||
static constexpr unsigned M = 4;
|
||||
static constexpr size_t N = 1ULL << 34;
|
||||
static constexpr size_t per_thread_iters = N / M;
|
||||
|
||||
std::array<std::atomic<unsigned>, B> buckets;
|
||||
|
||||
void generate() {
|
||||
for (size_t i = 0; i < per_thread_iters; ++i) buckets[rnd() / K].fetch_add(1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
auto bar_len = w.ws_col - 20;
|
||||
|
||||
std::array<std::thread, M> threads;
|
||||
|
||||
for (auto& bucket : buckets) bucket.store(0);
|
||||
|
||||
for (auto& t : threads) t = std::thread([]() { generate(); });
|
||||
|
||||
for (auto& t : threads) t.join();
|
||||
|
||||
auto max = std::accumulate(
|
||||
buckets.begin(), buckets.end(), 0u,
|
||||
[](auto& acc, auto& x) { return std::max(acc, x.load()); });
|
||||
assert(max != 0u);
|
||||
|
||||
std::cout << std::fixed;
|
||||
|
||||
for (auto& bucket : buckets) {
|
||||
auto x = bucket.load();
|
||||
auto rel = bar_len * x / max;
|
||||
|
||||
for (size_t i = 0; i < rel; ++i) std::cout << "=";
|
||||
|
||||
std::cout << " " << 100.0 * x / N * B - 100 << "%" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
All unit test should be gtest because in that case the test infrastructure can
|
||||
then visualize the results. (JUnit xml output)
|
@ -1,8 +1,8 @@
|
||||
#include "catch.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "utils/memory/allocator.hpp"
|
||||
|
||||
TEST_CASE("A block of integers can be allocated") {
|
||||
TEST(AllocatorTest, ABlockOfIntegersCanBeAllocated) {
|
||||
constexpr int N = 100;
|
||||
|
||||
fast_allocator<int> a;
|
||||
@ -12,13 +12,13 @@ TEST_CASE("A block of integers can be allocated") {
|
||||
for (int i = 0; i < N; ++i) xs[i] = i;
|
||||
|
||||
// can we read them back?
|
||||
for (int i = 0; i < N; ++i) REQUIRE(xs[i] == i);
|
||||
for (int i = 0; i < N; ++i) ASSERT_EQ(xs[i], i);
|
||||
|
||||
// we should be able to free the memory
|
||||
a.deallocate(xs, N);
|
||||
}
|
||||
|
||||
TEST_CASE("Allocator should work with structures") {
|
||||
TEST(AllocatorTest, AllocatorShouldWorkWithStructures) {
|
||||
struct TestObject {
|
||||
TestObject(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {}
|
||||
|
||||
@ -27,19 +27,21 @@ TEST_CASE("Allocator should work with structures") {
|
||||
|
||||
fast_allocator<TestObject> a;
|
||||
|
||||
SECTION("Allocate a single object") {
|
||||
// allocate a single object
|
||||
{
|
||||
auto* test = a.allocate(1);
|
||||
*test = TestObject(1, 2, 3, 4);
|
||||
|
||||
REQUIRE(test->a == 1);
|
||||
REQUIRE(test->b == 2);
|
||||
REQUIRE(test->c == 3);
|
||||
REQUIRE(test->d == 4);
|
||||
ASSERT_EQ(test->a, 1);
|
||||
ASSERT_EQ(test->b, 2);
|
||||
ASSERT_EQ(test->c, 3);
|
||||
ASSERT_EQ(test->d, 4);
|
||||
|
||||
a.deallocate(test, 1);
|
||||
}
|
||||
|
||||
SECTION("Allocate a block of structures") {
|
||||
// Allocate a block of structures
|
||||
{
|
||||
constexpr int N = 8;
|
||||
auto* tests = a.allocate(N);
|
||||
|
||||
@ -47,12 +49,17 @@ TEST_CASE("Allocator should work with structures") {
|
||||
for (int i = 0; i < N; ++i) tests[i] = TestObject(i, i, i, i);
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
REQUIRE(tests[i].a == i);
|
||||
REQUIRE(tests[i].b == i);
|
||||
REQUIRE(tests[i].c == i);
|
||||
REQUIRE(tests[i].d == i);
|
||||
ASSERT_EQ(tests[i].a, i);
|
||||
ASSERT_EQ(tests[i].b, i);
|
||||
ASSERT_EQ(tests[i].c, i);
|
||||
ASSERT_EQ(tests[i].d, i);
|
||||
}
|
||||
|
||||
a.deallocate(tests, N);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -9,16 +9,15 @@
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
using skiplist_t = ConcurrentMap<int, int>;
|
||||
using concurrent_map_t = ConcurrentMap<int, int>;
|
||||
|
||||
void print_skiplist(const skiplist_t::Accessor &skiplist) {
|
||||
logging::info("Skiplist now has: ");
|
||||
|
||||
for (auto &kv : skiplist) logging::info(" ({}, {})", kv.first, kv.second);
|
||||
void print_skiplist(const concurrent_map_t::Accessor &map) {
|
||||
logging::info("Map now has: ");
|
||||
for (auto &kv : map) logging::info(" ({}, {})", kv.first, kv.second);
|
||||
}
|
||||
|
||||
TEST(ConcurrentMapSkiplist, Mix) {
|
||||
skiplist_t skiplist;
|
||||
concurrent_map_t skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
// insert 10
|
||||
|
28
tests/unit/exceptions.cpp
Normal file
28
tests/unit/exceptions.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
|
||||
void i_will_throw() { throw BasicException("this is not ok"); }
|
||||
|
||||
void bar() { i_will_throw(); }
|
||||
|
||||
void foo() { bar(); }
|
||||
|
||||
void i_will_throw_stacktrace_exception() {
|
||||
throw StacktraceException("this is not {}", "ok!");
|
||||
}
|
||||
|
||||
void bar_stacktrace() { i_will_throw_stacktrace_exception(); }
|
||||
|
||||
void foo_stacktrace() { bar_stacktrace(); }
|
||||
|
||||
TEST(ExceptionsTest, ThrowBasicAndStackExceptions) {
|
||||
ASSERT_THROW(foo(), BasicException);
|
||||
ASSERT_THROW(foo_stacktrace(), StacktraceException);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
33
tests/unit/id.cpp
Normal file
33
tests/unit/id.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "mvcc/id.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
int main() {
|
||||
Id id0(0);
|
||||
Id id1(1);
|
||||
Id id2(1);
|
||||
Id id3(id2);
|
||||
Id id4 = id3;
|
||||
Id id5(5);
|
||||
|
||||
cout << id5 << " " << id0 << endl;
|
||||
|
||||
if (id0 < id5) cout << "id0 < id5" << endl;
|
||||
|
||||
if (id1 == id2) cout << "are equal" << endl;
|
||||
|
||||
if (id3 == id4) cout << "id3 == id4" << endl;
|
||||
|
||||
if (id5 > id0) cout << "id5 > id0" << endl;
|
||||
|
||||
if (id5 != id3) cout << "id5 != id3" << endl;
|
||||
|
||||
if (id1 >= id2) cout << "id1 >= id2" << endl;
|
||||
|
||||
if (id3 <= id4) cout << "id3 <= id4" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
25
tests/unit/log.cpp
Normal file
25
tests/unit/log.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "logging/logger.hpp"
|
||||
#include "logging/logs/async_log.hpp"
|
||||
#include "logging/logs/sync_log.hpp"
|
||||
|
||||
#include "logging/streams/stdout.hpp"
|
||||
|
||||
int main(void) {
|
||||
// Log::uptr log = std::make_unique<SyncLog>();
|
||||
Log::uptr log = std::make_unique<AsyncLog>();
|
||||
|
||||
log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
auto logger = log->logger("main");
|
||||
|
||||
logger.info("This is very {}!", "awesome");
|
||||
logger.warn("This is very {}!", "awesome");
|
||||
logger.error("This is very {}!", "awesome");
|
||||
logger.trace("This is very {}!", "awesome");
|
||||
logger.debug("This is very {}!", "awesome");
|
||||
|
||||
using namespace std::chrono;
|
||||
/* std::this_thread::sleep_for(1s); */
|
||||
|
||||
return 0;
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/default.cpp"
|
||||
#include "utils/random/generator.h"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
||||
using utils::random::NumberGenerator;
|
||||
using IntegerGenerator = NumberGenerator<std::uniform_int_distribution<int>,
|
||||
|
@ -1,54 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/concurrent/concurrent_set.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
void print_skiplist(const ConcurrentSet<int>::Accessor &skiplist) {
|
||||
cout << "---- skiplist set now has: ";
|
||||
|
||||
for (auto &item : skiplist) cout << item << ", ";
|
||||
|
||||
cout << "----" << endl;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
logging::init_async();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
ConcurrentSet<int> set;
|
||||
auto accessor = set.access();
|
||||
|
||||
cout << std::boolalpha;
|
||||
|
||||
cout << "added non-existing 1? (true) " << accessor.insert(1).second << endl;
|
||||
|
||||
cout << "added already existing 1? (false) " << accessor.insert(1).second
|
||||
<< endl;
|
||||
|
||||
accessor.insert(2);
|
||||
print_skiplist(accessor);
|
||||
|
||||
cout << "item 3 doesn't exist? (true) "
|
||||
<< (accessor.find(3) == accessor.end()) << endl;
|
||||
|
||||
cout << "item 3 exists? (false) " << accessor.contains(3) << endl;
|
||||
|
||||
cout << "item 2 exists? (true) " << (accessor.find(2) != accessor.end())
|
||||
<< endl;
|
||||
|
||||
cout << "at item 2 is? 2 " << *accessor.find(2) << endl;
|
||||
|
||||
cout << "removed existing 1? (true) " << accessor.remove(1) << endl;
|
||||
cout << "removed non-existing 3? (false) " << accessor.remove(3) << endl;
|
||||
|
||||
accessor.insert(1);
|
||||
accessor.insert(4);
|
||||
|
||||
print_skiplist(accessor);
|
||||
|
||||
return 0;
|
||||
}
|
22
tests/unit/timestamp.cpp
Normal file
22
tests/unit/timestamp.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "utils/datetime/timestamp.hpp"
|
||||
|
||||
int main(void) {
|
||||
auto timestamp = Timestamp::now();
|
||||
|
||||
std::cout << timestamp << std::endl;
|
||||
std::cout << Timestamp::now() << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
std::cout << Timestamp::now().to_iso8601() << std::endl;
|
||||
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
std::cout << (timestamp == Timestamp::now()) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user