connected the first working version of memgraph

This commit is contained in:
Dominik Tomičević 2015-10-11 20:59:27 +02:00
parent 9f8761032f
commit 768ad5e451
28 changed files with 695 additions and 304 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
*.swo
*.out
*.dSYM/
memgraph

View File

@ -1,6 +1,6 @@
CXX=clang++
CFLAGS=-std=c++1y -Wall -O2
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre
CFLAGS=-std=c++1y -O2 -Wall -Wno-unknown-pragmas
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre -pthread
INC=-I./
SOURCES=$(wildcard *.cpp)

View File

@ -1,6 +1,7 @@
# this script generates the include.hpp file for restful resources
import re
import os
from itertools import chain
print "generating include.hpp file"
@ -16,27 +17,38 @@ if os.path.isfile(include_path):
class Resource(object):
""" represents a restful resource class for speedy """
def __init__(self, filename):
def __init__(self, filename, class_name, url):
self.filename = filename
self.class_name = None
self.class_name = class_name
self.url = url
with open(os.path.join(resource_path, filename)) as f:
class_name = re.compile('\s*class\s*(\w+)\s*\:')
for line in f:
result = re.search(class_name, line)
def scan_resources(filename):
if result is None:
continue
with open(os.path.join(resource_path, filename)) as f:
url_regex = re.compile('#pragma\s+url\s+([^\s]+)\s+')
class_name_regex = re.compile('\s*class\s*(\w+)\s*\:')
self.class_name = result.group(1)
lines = f.readlines()
pairs = zip(lines, lines[1:])
break
for first, second in pairs:
url = re.search(url_regex, first)
if url is None:
continue
class_name = re.search(class_name_regex, second)
if class_name is None:
continue
yield Resource(filename, class_name.group(1), url.group(1))
def load_resources():
resources = [Resource(f) for f in os.listdir(resource_path)
if f.endswith('.hpp')]
resources = chain(*[scan_resources(f) for f in os.listdir(resource_path)
if f.endswith('.hpp')])
return [r for r in resources if r.class_name is not None]
@ -48,9 +60,10 @@ def write_includes(file, resources):
def write_inits(file, resources):
for class_name in [resource.class_name for resource in resources]:
print 'writing init for', class_name
file.write(' insert<{}>(app);\n'.format(class_name))
for class_name, url in [(r.class_name, r.url) for r in resources]:
print('writing init for {} -> {}'.format(class_name, url))
file.write(' insert<{}>(container, "{}");\n'
.format(class_name, url))
def make_include_file():

View File

@ -1,24 +0,0 @@
#ifndef MEMGRAPH_ANIMAL_HPP
#define MEMGRAPH_ANIMAL_HPP
#include "speedy/speedy.hpp"
#include "api/restful/resource.hpp"
/* class Ani mal : public Resource<Animal, GET, POST> */
/* { */
/* public: */
/* Animal(sp::Speedy& app) */
/* : Resource(app, "/animal/{id:\\d+}/{name:\\w+}/commit") {} */
/* void get(sp::Request& req, sp::Response& res) */
/* { */
/* return res.send("Ok, here is a Dog"); */
/* } */
/* void post(sp::Request& req, sp::Response& res) */
/* { */
/* return res.send("Oh, you gave me an animal?"); */
/* } */
/* }; */
#endif

View File

@ -4,6 +4,8 @@
* This file is autogenerated by the python script link_resources.py
*
* YOU SHOULD NOT EDIT THIS FILE MANUALLY!
*
* well, only if you're editing the template file, that's ok :)
*/
#ifndef MEMGRAPH_API_RESOURCES_INCLUDE_HPP
@ -12,22 +14,28 @@
#include <list>
#include <memory>
#include "utils/ioc/container.hpp"
#include "api/restful/resource.hpp"
#include "transactions/engine.hpp"
#include "threading/task.hpp"
#include "speedy/speedy.hpp"
#include "node.hpp"
static std::list<std::unique_ptr<RestfulResource>> resources;
#include "node.hpp"
template <class T>
void insert(sp::Speedy& app)
void insert(ioc::Container& container, const std::string& path)
{
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
auto app = container.resolve<sp::Speedy>();
auto resource = container.singleton<T, Task, Db>();
resource->link(*app, path);
}
void init(sp::Speedy& app)
void init(ioc::Container& container)
{
insert<Node>(app);
insert<Nodes>(container, "/node");
insert<Node>(container, "/node/{id:\\d+}");
}
#endif

View File

@ -4,6 +4,8 @@
* This file is autogenerated by the python script link_resources.py
*
* YOU SHOULD NOT EDIT THIS FILE MANUALLY!
*
* well, only if you're editing the template file, that's ok :)
*/
#ifndef MEMGRAPH_API_RESOURCES_INCLUDE_HPP
@ -12,21 +14,37 @@
#include <list>
#include <memory>
#include "utils/ioc/container.hpp"
#include "api/restful/resource.hpp"
#include "transactions/engine.hpp"
#include "threading/task.hpp"
#include "speedy/speedy.hpp"
// for each file in this folder a script will generate an include directive if
// this file contains any resources
// e.g.
// #include "node.hpp"
// #include "relationship.hpp"
<INCLUDE>
static std::list<std::unique_ptr<RestfulResource>> resources;
template <class T>
void insert(sp::Speedy& app)
void insert(ioc::Container& container, const std::string& path)
{
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
auto app = container.resolve<sp::Speedy>();
auto resource = container.singleton<T, Task, Db>();
resource->link(*app, path);
}
void init(sp::Speedy& app)
void init(ioc::Container& container)
{
// for each resource in a file included above, the script will generate a
// linkage command call to the function above
// e.g.
// insert<CLASS>(PATH);
//
// insert<Nodes>("/node");
// insert<Node>("/node/{id:\\d+}");
<INIT>
}

View File

@ -1,31 +1,83 @@
#ifndef MEMGRAPH_API_RESOURCES_NODE_HPP
#define MEMGRAPH_API_RESOURCES_NODE_HPP
#include "api/restful/resource.hpp"
#include <random>
class Node : public Resource<Node, GET, POST>
#include "api/restful/resource.hpp"
#include "debug/log.hpp"
#pragma url /node
class Nodes : public Resource<Nodes, POST>
{
public:
Node(sp::Speedy& app) : Resource(app, "/node") {}
void get(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
}
using Resource::Resource;
void post(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
task->run([this, &req]() {
// start a new transaction and obtain a reference to it
auto t = db->tx_engine.begin();
// insert a new vertex in the graph
auto atom = db->graph.vertices.insert(t);
// a new version was created and we got an atom along with the
// first version. obtain a pointer to the first version
//
// nullptr
// ^
// |
// [Vertex v1]
// ^
// |
// [Atom id=k] k {1, 2, ...}
//
auto node = atom->first();
// TODO read the JSON body and store the properties in the
// first version
//
// for(key, value in body)
// node->properties[key] = value;
// commit the transaction
db->tx_engine.commit(t);
// return the node we created so we can send it as a response body
return node;
},
[&req, &res](Vertex* node) {
// make a string buffer
std::string buffer;
// dump properties in this buffer
node->properties.dump(buffer);
// respond to the use with the buffer
return res.send(buffer);
});
}
};
#pragma url /node/{id:\\d+}
class Node : public Resource<Node, GET, PUT, DELETE>
{
public:
using Resource::Resource;
void get(sp::Request& req, sp::Response& res)
{
return res.send(req.url);
}
void put(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
return res.send(req.url);
}
void del(sp::Request& req, sp::Response& res)
{
return res.send("Ok, here is a Dog");
return res.send(req.url);
}
};

View File

@ -1,157 +1,53 @@
#ifndef MEMGRAPH_API_RESOURCE_HPP
#define MEMGRAPH_API_RESOURCE_HPP
#ifndef MEMGRAPH_API_RESTFUL_RESOURCE_HPP
#define MEMGRAPH_API_RESTFUL_RESOURCE_HPP
#include <memory>
#include "api/restful/restful_resource.hpp"
#include "database/db.hpp"
#include "threading/task.hpp"
#include "speedy/speedy.hpp"
#include "utils/crtp.hpp"
/** @brief GET handler method for the resource
* Contains the code for registering GET handler for a URL to Speedy
*/
struct GET
{
/** @brief Links ::get handler to speedy for a given path
*
* @tparam T Class type containing the required handler
* @param app Instance of speedy to register the method to
* @param path URL of the resource being registered
* @param resource Object containing ::get http::request_cb_t handler
*/
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.get(path, std::bind(&T::get, resource, _1, _2));
}
};
/** @brief POST handler method for the resource
* Contains the code for registering POST handler for a URL to Speedy
*/
struct POST
{
/** @brief Links ::post handler to speedy for a given path
*
* @tparam T Class type containing the required handler
* @param app Instance of speedy to register the method to
* @param path URL of the resource being registered
* @param resource Object containing ::post http::request_cb_t handler
*/
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.post(path, std::bind(&T::post, resource, _1, _2));
}
};
struct PUT
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.put(path, std::bind(&T::put, resource, _1, _2));
}
};
struct DELETE
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.del(path, std::bind(&T::del, resource, _1, _2));
}
};
namespace detail
{
/** @brief Registers a method for a path to speedy
*
* @tparam T Derived class containing the handler of the method
* @tparam M A method to register
*/
template <class T, class M>
struct Method : public M
{
/** Registers a route handler for the resource
*
* Registers a method handler for M on the given URL
*
* @param app instance of speedy to register the method to
* @param path URL of the resource being registered
*/
Method(sp::Speedy& app, const std::string& path)
{
M::link(app, path, static_cast<T&>(*this));
}
};
/** @brief Generates the Method<T, M> inheritance for each M in Ms
*
* Implemented inheriting recursively using variadic templates
*
* @tparam T Derived class containing handlers for each method M in Ms
* @tparam Ms... Methods to register
*/
template <class T, class... Ms>
struct Methods;
/** @brief specialization of the struct Methods<T, Ms...>
*
* Unrolls one method M and generates a handler for this method by inheriting
* from Method<T, M> and generates the rest of the handlers recursively by
* inheriting from Methods<T, Ms...> and unrolling one M each time.
*
* @tparam T Derived class containing handlers for each method M in Ms
* @tparam M Unrolled method M for which to generate a handler
* @tparam Ms... The rest of the methods
*/
template <class T, class M, class... Ms>
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
{
Methods(sp::Speedy& app, const std::string& path)
: Method<T, M>(app, path), Methods<T, Ms...>(app, path) {}
};
/** @brief specialization of the struct Methods<T, Ms...>
*
* Specializes the recursion termination case containing only one method M
*
* @tparam T Derived class containing handlers for method M
* @tparam M Unrolled method M for which to generate a handler
*/
template <class T, class M>
struct Methods<T, M> : public Method<T, M>
{
using Method<T, M>::Method;
};
}
struct RestfulResource {};
/** @brief Represents a restful resource
*
* Automatically registers get, put, post, del... methods inside the derived
* class T. Methods are given as a template parameter to the class. Valid
* template parameters are classes which implement a function
*
* void link(sp::Speedy&, const std::string&, T& resource)
*
* which registers a method you want to use with speedy
*
* @tparam T Derived class (CRTP)
* @tparam Ms... HTTP methods to register for this resource (GET, POST...)
*/
template <class T, class... Ms>
class Resource : public detail::Methods<T, Ms...>, public RestfulResource
class Resource
{
public:
Resource(sp::Speedy& app, const std::string& path)
: detail::Methods<T, Ms...>(app, path) {}
/** @brief Resource constructor
*
* List ALL the dependencies here so that ioc can resolve them and store
* them to protected members so that derived resources can access them
*
* @param task shared_ptr to the task dispatching library
* @param db shared_ptr to the database instance
*/
Resource(Task::sptr task, Db::sptr db)
: task(task), db(db) {}
/** @brief Link all resources to an instance of speedy
*
* link_resources.py generates an include.hpp file which includes and
* instantinates all available resources. The include.hpp file also calls
* this method to link the resources to speedy for a given path
*/
void link(sp::Speedy& app, const std::string& path)
{
// make sure this is called once even if someone actually calls this
// function multiple times
std::call_once(once_flag, [this, &app, &path]() {
Restful<T, Ms...>(static_cast<T&>(*this), app, path);
});
}
protected:
// all resources have pointers to these instances add everything else
// neccessary here as a shared_ptr and also include it in the constructor
// and modify the include.hpp.template to include the new dependencies for
// resource linking
Task::sptr task;
Db::sptr db;
private:
std::once_flag once_flag;
};
#endif

View File

@ -0,0 +1,156 @@
#ifndef MEMGRAPH_API_RESTFUL_RESTFUL_RESOURCE_HPP
#define MEMGRAPH_API_RESTFUL_RESTFUL_RESOURCE_HPP
#include <memory>
#include "speedy/speedy.hpp"
#include "utils/crtp.hpp"
/** @brief GET handler method for the resource
* Contains the code for registering GET handler for a URL to Speedy
*/
struct GET
{
/** @brief Links ::get handler to speedy for a given path
*
* @tparam T Class type containing the required handler
* @param app Instance of speedy to register the method to
* @param path URL of the resource being registered
* @param resource Object containing ::get http::request_cb_t handler
*/
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.get(path, std::bind(&T::get, std::ref(resource), _1, _2));
}
};
/** @brief POST handler method for the resource
* Contains the code for registering POST handler for a URL to Speedy
*/
struct POST
{
/** @brief Links ::post handler to speedy for a given path
*
* @tparam T Class type containing the required handler
* @param app Instance of speedy to register the method to
* @param path URL of the resource being registered
* @param resource Object containing ::post http::request_cb_t handler
*/
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.post(path, std::bind(&T::post, std::ref(resource), _1, _2));
}
};
struct PUT
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.put(path, std::bind(&T::put, std::ref(resource), _1, _2));
}
};
struct DELETE
{
template <class T>
void link(sp::Speedy& app, const std::string& path, T& resource)
{
using namespace std::placeholders;
app.del(path, std::bind(&T::del, std::ref(resource), _1, _2));
}
};
namespace detail
{
/** @brief Registers a method for a path to speedy
*
* @tparam T Derived class containing the handler of the method
* @tparam M A method to register
*/
template <class T, class M>
struct Method : public M
{
/** Registers a route handler for the resource
*
* Registers a method handler for M on the given URL
*
* @param app instance of speedy to register the method to
* @param path URL of the resource being registered
*/
Method(T& resource, sp::Speedy& app, const std::string& path)
{
M::link(app, path, resource);
}
};
/** @brief Generates the Method<T, M> inheritance for each M in Ms
*
* Implemented inheriting recursively using variadic templates
*
* @tparam T Derived class containing handlers for each method M in Ms
* @tparam Ms... Methods to register
*/
template <class T, class... Ms>
struct Methods;
/** @brief specialization of the struct Methods<T, Ms...>
*
* Unrolls one method M and generates a handler for this method by inheriting
* from Method<T, M> and generates the rest of the handlers recursively by
* inheriting from Methods<T, Ms...> and unrolling one M each time.
*
* @tparam T Derived class containing handlers for each method M in Ms
* @tparam M Unrolled method M for which to generate a handler
* @tparam Ms... The rest of the methods
*/
template <class T, class M, class... Ms>
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
{
Methods(T& resource, sp::Speedy& app, const std::string& path)
: Method<T, M>(resource, app, path),
Methods<T, Ms...>(resource, app, path) {}
};
/** @brief specialization of the struct Methods<T, Ms...>
*
* Specializes the recursion termination case containing only one method M
*
* @tparam T Derived class containing handlers for method M
* @tparam M Unrolled method M for which to generate a handler
*/
template <class T, class M>
struct Methods<T, M> : public Method<T, M>
{
using Method<T, M>::Method;
};
}
/** @brief Represents a restful resource
*
* Automatically registers get, put, post, del... methods inside the derived
* class T. Methods are given as a template parameter to the class. Valid
* template parameters are classes which implement a function
*
* void link(sp::Speedy&, const std::string&, T& resource)
*
* which registers a method you want to use with speedy
*
* @tparam T Derived class (CRTP)
* @tparam Ms... HTTP methods to register for this resource (GET, POST...)
*/
template <class T, class... Ms>
class Restful : public detail::Methods<T, Ms...>
{
public:
Restful(T& resource, sp::Speedy& app, const std::string& path)
: detail::Methods<T, Ms...>(resource, app, path) {}
};
#endif

View File

@ -1,20 +1,17 @@
#ifndef MEMGRAPH_DATABASE_DB_HPP
#define MEMGRAPH_DATABASE_DB_HPP
#include "transactions/transaction_engine.hpp"
#include "storage/graph.hpp"
#include "transactions/engine.hpp"
#include "transactions/commit_log.hpp"
class Db
{
public:
static Db& get()
{
static Db db;
return db;
}
using sptr = std::shared_ptr<Db>;
tx::CommitLog commit_log;
tx::TransactionEngine transaction_engine;
Graph graph;
tx::Engine tx_engine;
};
#endif

128
io/uv/blockbuffer.hpp Normal file
View File

@ -0,0 +1,128 @@
#ifndef MEMGRAPH_IO_UV_BLOCKBUFFER_HPP
#define MEMGRAPH_IO_UV_BLOCKBUFFER_HPP
#include <cstring>
#include <uv.h>
#include "utils/memory/block_allocator.hpp"
namespace uv
{
template <size_t block_size>
class BlockBuffer
{
static BlockAllocator<block_size> allocator;
struct Block : public uv_buf_t
{
Block()
{
// acquire a new block of memory for this buffer
base = static_cast<char*>(allocator.acquire());
len = 0;
}
~Block()
{
// release the block of memory previously acquired
allocator.release(base);
}
size_t append(const char* data, size_t size)
{
// compute the remaining capacity for this block
auto capacity = block_size - len;
// if the capacity is smaller than the requested size, copy only
// up to the remaining capacity
if(size > capacity)
size = capacity;
std::memcpy(base + len, data, size);
len += size;
// return how much we've copied
return size;
}
};
public:
BlockBuffer()
{
// create the first buffer
buffers.emplace_back();
}
~BlockBuffer()
{
buffers.clear();
}
BlockBuffer(BlockBuffer&) = delete;
BlockBuffer(BlockBuffer&&) = delete;
size_t count() const
{
return buffers.size();
}
void clear()
{
// pop all buffers except for the first one since we need to allocate
// it again anyway so why not keep it in the first place
while(count() > 1)
buffers.pop_back();
// pretend we just allocated our first buffer and set it's length to 0
buffers.back().len = 0;
}
BlockBuffer& operator<<(const std::string& data)
{
append(data);
return *this;
}
void append(const std::string& data)
{
append(data.c_str(), data.size());
}
void append(const char* data, size_t size)
{
while(true)
{
// try to copy as much data as possible
auto copied = buffers.back().append(data, size);
// if we managed to copy everything, we're done
if(copied == size)
break;
// move the pointer past the copied part
data += copied;
// reduce the total size by the number of copied items
size -= copied;
// since we ran out of space, construct a new buffer
buffers.emplace_back();
}
}
operator uv_buf_t*()
{
return buffers.data();
}
private:
std::vector<Block> buffers;
};
template <size_t block_size>
BlockAllocator<block_size> BlockBuffer<block_size>::allocator;
}
#endif

View File

@ -1,6 +1,7 @@
#include "tcpstream.hpp"
#include "uvbuffer.hpp"
#include "uvloop.hpp"
#include "blockbuffer.hpp"
#include "tcpstream.inl"
#include "uvbuffer.inl"

View File

@ -12,6 +12,8 @@ namespace uv
class UvLoop final
{
public:
using sptr = std::shared_ptr<UvLoop>;
enum Mode {
Default = UV_RUN_DEFAULT,
Once = UV_RUN_ONCE,

BIN
memgraph

Binary file not shown.

View File

@ -1,22 +1,35 @@
#include <iostream>
#include <vector>
#include "debug/log.hpp"
#include "utils/ioc/container.hpp"
#include "database/db.hpp"
#include "speedy/speedy.hpp"
#include "api/resources/include.hpp"
#include "utils/auto_scope.hpp"
#include "threading/pool.hpp"
#include "threading/task.hpp"
int main(int argc, char** argv)
int main()
{
uv::UvLoop loop;
sp::Speedy app(loop);
ioc::Container container;
init(app);
container.singleton<Db>();
http::Ipv4 ip("0.0.0.0", 3400);
app.listen(ip);
auto loop = container.singleton<uv::UvLoop>();
auto app = container.singleton<sp::Speedy, uv::UvLoop>("/db/data");
loop.run(uv::UvLoop::Mode::Default);
container.singleton<Pool>(4);
container.singleton<Task, uv::UvLoop, Pool>();
init(container);
http::Ipv4 ip("0.0.0.0", 7474);
app->listen(ip);
loop->run(uv::UvLoop::Mode::Default);
return 0;
}

View File

@ -7,8 +7,6 @@
#include "request.hpp"
#include "response.hpp"
#include "utils/memory/block_allocator.hpp"
namespace http
{

View File

@ -66,7 +66,6 @@ void HttpServer<Req, Res>::
conn.close();
buffer_allocator.release(buf->base);
//delete buf->base;
}
template <class Req, class Res>

View File

@ -9,6 +9,8 @@
namespace http
{
static constexpr size_t buffer_size = 65536;
template <class Req, class Res>
class HttpConnection;
@ -30,7 +32,7 @@ public:
private:
connection_t& connection;
uv::UvBuffer buffer;
uv::BlockBuffer<buffer_size> buffer;
};
}

View File

@ -9,14 +9,11 @@
namespace http
{
constexpr size_t buffer_size = 65536;
static BlockAllocator<sizeof(uv_write_t)> write_req_allocator;
template <class Req, class Res>
Response<Req, Res>::Response(connection_t& connection)
: status(Status::Ok), connection(connection),
buffer(buffer_size) {}
: status(Status::Ok), connection(connection), buffer() {}
template <class Req, class Res>
void Response<Req, Res>::send(Status status, const std::string& body)
@ -45,7 +42,7 @@ void Response<Req, Res>::send(const std::string& body)
buffer << "\r\n" << body;
uv_write(write_req, connection.client, buffer, 1,
uv_write(write_req, connection.client, buffer, buffer.count(),
[](uv_write_t* write_req, int) {
connection_t& conn = *reinterpret_cast<connection_t*>(write_req->data);

View File

@ -47,17 +47,18 @@ public:
}
}
private:
class MatchEntry
public:
class Route
{
public:
MatchEntry(Method method, const std::string& path)
Route(r3::node* node, Method method, const std::string& path)
{
entry = r3::match_entry_createl(path.c_str(), path.size());
entry->request_method = method;
route = r3::r3_tree_match_route(node, entry);
}
~MatchEntry()
~Route()
{
r3::match_entry_free(entry);
}
@ -67,22 +68,17 @@ private:
return entry;
}
private:
r3::match_entry* entry;
r3::route* route;
};
public:
class Route
{
public:
Route(r3::route* route) : route(route) {}
bool exists() const
{
return route != nullptr;
}
void populate(sp::Request& req)
{
for(int i = 0; i < entry->vars->len; ++i)
req.params.emplace_back(entry->vars->tokens[i]);
}
void operator()(sp::Request& req, sp::Response& res)
{
assert(route != nullptr);
@ -92,6 +88,7 @@ public:
}
private:
r3::match_entry* entry;
r3::route* route;
};
@ -124,24 +121,7 @@ public:
Route match(Method method, const std::string& path)
{
auto entry_m = MatchEntry(method, path);
return Route(r3::r3_tree_match_route(root, entry_m));
/* if(!route.exists()) */
/* return route; */
/* r3::match_entry* entry = entry_m; */
/* std::cout << "Eetry Matched!" << std::endl */
/* << "path = " << std::string(entry->path, entry->path_len) << std::endl */
/* << "host = " << std::string(entry->host, entry->host_len) << std::endl */
/* << "remote_addr = " << std::string(entry->remote_addr, entry->remote_addr_len) << std::endl */
/* << "tokens_len = " << entry->vars->len << std::endl; */
/* for(int i = 0; i < entry->vars->len; ++i) */
/* std::cout << "token " << i << " = " << std::string(entry->vars->tokens[i]) << std::endl; */
//return route;
return Route(root, method, path);
}
void compile()

View File

@ -1,6 +1,8 @@
#ifndef MEMGRAPH_SPEEDY_REQUEST_HPP
#define MEMGRAPH_SPEEDY_REQUEST_HPP
#include <vector>
#include "http/request.hpp"
namespace sp
@ -10,6 +12,8 @@ class Request : public http::Request
{
public:
using http::Request::Request;
std::vector<std::string> params;
};
}

View File

@ -23,33 +23,36 @@ namespace sp
class Speedy
{
public:
using sptr = std::shared_ptr<Speedy>;
using server_t = http::HttpServer<Request, Response>;
using request_cb_t = server_t::request_cb_t;
Speedy(uv::UvLoop& loop, size_t capacity = 100)
: server(loop), router(capacity) {}
Speedy(uv::UvLoop::sptr loop, const std::string& prefix = "",
size_t capacity = 100)
: server(*loop), prefix(std::move(prefix)), router(capacity) {}
Speedy(Speedy&) = delete;
Speedy(Speedy&&) = delete;
void get(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::GET, path, cb);
router.insert(R3::Method::GET, join(prefix, path), cb);
}
void post(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::POST, path, cb);
router.insert(R3::Method::POST, join(prefix, path), cb);
}
void put(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::PUT, path, cb);
router.insert(R3::Method::PUT, join(prefix, path), cb);
}
void del(const std::string& path, server_t::request_cb_t cb)
{
router.insert(R3::Method::DELETE, path, cb);
router.insert(R3::Method::DELETE, join(prefix, path), cb);
}
void listen(const http::Ipv4& ip)
@ -57,20 +60,43 @@ public:
router.compile();
server.listen(ip, [this](Request& req, Response& res) {
return res.send("Hello World");
auto route = router.match(R3::to_r3_method(req.method), req.url);
if(!route.exists())
return res.send(http::Status::NotFound, "Resource not found");
route.populate(req);
route(req, res);
});
}
private:
server_t server;
std::string prefix;
R3 router;
std::string join(const std::string& prefix, const std::string& path)
{
// check if prefix has a trailing /
if(prefix.back() == '/')
{
// prefix has a / on the end so remove the / from the path
// if it has it too. e.g /db/data/ + /node = /db/data/node
if(path.front() == '/')
return prefix + path.substr(1);
// there is only one / so it's ok to concat them
return prefix + path;
}
// the prefix doesn't have a slash
if(path.front() == '/')
// the path has though, so it's ok to concat them
return prefix + path;
// neither the prefix or the path have a / so we need to add it
return prefix + '/' + path;
}
};
}

View File

@ -10,15 +10,17 @@ namespace model
class Property
{
public:
// shared_ptr is being used because of MVCC - when you clone a record, you
// clone it's properties. when a single property is updated, a lot of
// memory is being wasted. this way it is shared until you need to change
// something and shared ptr ensures it's being properly tracked and
// cleaned up after no one is using it
using sptr = std::shared_ptr<Property>;
virtual ~Property() {}
template <class T, class... Args>
static Property::sptr make(Args&&... args)
{
return std::shared_ptr<Property>(new T(std::forward<Args>(args)...));
}
Property() = default;
virtual ~Property() = default;
virtual void dump(std::string& buffer) = 0;
template <class T>

View File

@ -13,6 +13,8 @@ class Pool : Lockable<std::mutex>
{
using task_t = std::function<void()>;
public:
using sptr = std::shared_ptr<Pool>;
Pool(size_t n = std::thread::hardware_concurrency()) : alive(true)
{
threads.reserve(n);
@ -33,15 +35,11 @@ public:
thread.join();
}
template <class F, class... Args>
void run(F&& f, Args&&... args)
void run(task_t f)
{
{
auto lock = acquire_unique();
tasks.emplace([&f, &args...]() {
f(std::forward<Args>(args)...);
});
tasks.push(f);
}
cond.notify_one();

83
threading/task.hpp Normal file
View File

@ -0,0 +1,83 @@
#ifndef MEMGRAPH_THREADING_TASK_HPP
#define MEMGRAPH_THREADING_TASK_HPP
#include <iostream>
#include "pool.hpp"
#include "io/uv/uvloop.hpp"
#include "utils/placeholder.hpp"
class Task
{
template <class T>
using work_t = std::function<T(void)>;
template <class T>
using after_work_t = std::function<void(T)>;
template <class T>
struct Work
{
Work(uv::UvLoop& loop, work_t<T> work, after_work_t<T> after_work)
: work(std::move(work)), after_work(std::move(after_work))
{
uv_async_init(loop, &this->async, async_cb);
}
void operator()()
{
result.set(std::move(work()));
async.data = static_cast<void*>(this);
uv_async_send(&this->async);
}
work_t<T> work;
after_work_t<T> after_work;
Placeholder<T> result;
uv_async_t async;
private:
static void async_cb(uv_async_t* handle)
{
auto work = static_cast<Work<T>*>(handle->data);
work->after_work(std::move(work->result.get()));
auto async_as_handle = reinterpret_cast<uv_handle_t*>(handle);
uv_close(async_as_handle, [](uv_handle_t* handle) {
auto work = static_cast<Work<T>*>(handle->data);
delete work;
});
}
};
public:
using sptr = std::shared_ptr<Task>;
Task(uv::UvLoop::sptr loop, Pool::sptr pool) : loop(loop), pool(pool) {}
Task(Task&) = delete;
Task(Task&&) = delete;
template <class F1, class F2>
void run(F1&& work, F2&& after_work)
{
using T = decltype(work());
auto w = new Work<T>(*loop, std::forward<F1>(work),
std::forward<F2>(after_work));
pool->run([w]() { w->operator()(); });
}
private:
uv::UvLoop::sptr loop;
Pool::sptr pool;
};
#endif

View File

@ -1,5 +1,5 @@
#ifndef MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
#define MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
#ifndef MEMGRAPH_TRANSACTIONS_ENGINE_HPP
#define MEMGRAPH_TRANSACTIONS_ENGINE_HPP
#include <atomic>
#include <vector>
@ -22,10 +22,12 @@ public:
using std::runtime_error::runtime_error;
};
class TransactionEngine : Lockable<SpinLock>
class Engine : Lockable<SpinLock>
{
public:
TransactionEngine() : counter(0) {}
using sptr = std::shared_ptr<Engine>;
Engine() : counter(0) {}
const Transaction& begin()
{

View File

@ -69,23 +69,17 @@ public:
return item->get();
}
template <class T, class... Args>
std::shared_ptr<T> singleton()
template <class T, class... Deps, class... Args>
std::shared_ptr<T> singleton(Args&&... args)
{
auto item = std::make_shared<T>(resolve<Args>()...);
auto item = std::make_shared<T>(resolve<Deps>()..., args...);
items.emplace(key<T>(), Holdable::uptr(new Instance<T>(item)));
return item;
}
template <class T>
void singleton(std::shared_ptr<T>&& item)
{
items.emplace(key<T>(), Holdable::uptr(new Instance<T>(item)));
}
template <class T>
void factory(typename Creator<T>::func&& f)
{
{
items[key<T>()] = std::move(Holdable::uptr(
new Creator<T>(std::forward<typename Creator<T>::func>(f))
));

45
utils/placeholder.hpp Normal file
View File

@ -0,0 +1,45 @@
#ifndef MEMGRAPH_UTILS_PLACEHOLDER_HPP
#define MEMGRAPH_UTILS_PLACEHOLDER_HPP
#include <utility>
#include <ext/aligned_buffer.h>
template <class T>
class Placeholder
{
public:
Placeholder() = default;
Placeholder(Placeholder&) = delete;
Placeholder(Placeholder&&) = delete;
~Placeholder()
{
if(initialized)
get().~T();
};
T& get() noexcept
{
assert(initialized);
return *data._M_ptr();
}
void set(const T& item)
{
new (data._M_addr()) T(item);
initialized = true;
}
void set(T&& item)
{
new (data._M_addr()) T(std::move(item));
initialized = true;
}
private:
__gnu_cxx::__aligned_buffer<T> data;
bool initialized = false;
};
#endif