diff --git a/.gitignore b/.gitignore index 125348867..81a453937 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.swo *.out *.dSYM/ +memgraph diff --git a/Makefile b/Makefile index 7ee745dd7..fe33c9735 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/api/link_resources.py b/api/link_resources.py index c3b8eb7de..5ef3886f5 100644 --- a/api/link_resources.py +++ b/api/link_resources.py @@ -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(): diff --git a/api/resources/animal.hpp b/api/resources/animal.hpp deleted file mode 100644 index 05003318c..000000000 --- a/api/resources/animal.hpp +++ /dev/null @@ -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 diff --git a/api/resources/include.hpp b/api/resources/include.hpp index 21b94a257..4fbc0825a 100644 --- a/api/resources/include.hpp +++ b/api/resources/include.hpp @@ -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 diff --git a/api/resources/include.hpp.template b/api/resources/include.hpp.template index 5204150db..c400bf923 100644 --- a/api/resources/include.hpp.template +++ b/api/resources/include.hpp.template @@ -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> } diff --git a/api/resources/node.hpp b/api/resources/node.hpp index 7fee61680..1ee8afe28 100644 --- a/api/resources/node.hpp +++ b/api/resources/node.hpp @@ -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); } }; diff --git a/api/restful/resource.hpp b/api/restful/resource.hpp index 37ce2bf07..ce9145247 100644 --- a/api/restful/resource.hpp +++ b/api/restful/resource.hpp @@ -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 diff --git a/api/restful/restful_resource.hpp b/api/restful/restful_resource.hpp new file mode 100644 index 000000000..470c55cc9 --- /dev/null +++ b/api/restful/restful_resource.hpp @@ -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 diff --git a/database/db.hpp b/database/db.hpp index 4796b9eb5..aa4a4db91 100644 --- a/database/db.hpp +++ b/database/db.hpp @@ -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 diff --git a/io/uv/blockbuffer.hpp b/io/uv/blockbuffer.hpp new file mode 100644 index 000000000..ae22895f5 --- /dev/null +++ b/io/uv/blockbuffer.hpp @@ -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 diff --git a/io/uv/uv.hpp b/io/uv/uv.hpp index b771aed53..ea3941e4a 100644 --- a/io/uv/uv.hpp +++ b/io/uv/uv.hpp @@ -1,6 +1,7 @@ #include "tcpstream.hpp" #include "uvbuffer.hpp" #include "uvloop.hpp" +#include "blockbuffer.hpp" #include "tcpstream.inl" #include "uvbuffer.inl" diff --git a/io/uv/uvloop.hpp b/io/uv/uvloop.hpp index cb2d8ae90..4ad881ff2 100644 --- a/io/uv/uvloop.hpp +++ b/io/uv/uvloop.hpp @@ -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, diff --git a/memgraph b/memgraph deleted file mode 100755 index b791f189d..000000000 Binary files a/memgraph and /dev/null differ diff --git a/memgraph.cpp b/memgraph.cpp index a737ed089..2d4255777 100644 --- a/memgraph.cpp +++ b/memgraph.cpp @@ -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; } diff --git a/speedy/http/httpconnection.hpp b/speedy/http/httpconnection.hpp index 7561a3b56..dcc3afbcb 100644 --- a/speedy/http/httpconnection.hpp +++ b/speedy/http/httpconnection.hpp @@ -7,8 +7,6 @@ #include "request.hpp" #include "response.hpp" -#include "utils/memory/block_allocator.hpp" - namespace http { diff --git a/speedy/http/httpserver.inl b/speedy/http/httpserver.inl index 224aaffa9..ea4df5f0d 100644 --- a/speedy/http/httpserver.inl +++ b/speedy/http/httpserver.inl @@ -66,7 +66,6 @@ void HttpServer<Req, Res>:: conn.close(); buffer_allocator.release(buf->base); - //delete buf->base; } template <class Req, class Res> diff --git a/speedy/http/response.hpp b/speedy/http/response.hpp index 886915da4..2fd233d77 100644 --- a/speedy/http/response.hpp +++ b/speedy/http/response.hpp @@ -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; }; } diff --git a/speedy/http/response.inl b/speedy/http/response.inl index dd2ed0c7c..e7e541ae7 100644 --- a/speedy/http/response.inl +++ b/speedy/http/response.inl @@ -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); diff --git a/speedy/r3.hpp b/speedy/r3.hpp index 939dd009c..a93d47f19 100644 --- a/speedy/r3.hpp +++ b/speedy/r3.hpp @@ -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() diff --git a/speedy/request.hpp b/speedy/request.hpp index 35d5341aa..11e417351 100644 --- a/speedy/request.hpp +++ b/speedy/request.hpp @@ -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; }; } diff --git a/speedy/speedy.hpp b/speedy/speedy.hpp index 7e7497fa2..13bce33a2 100644 --- a/speedy/speedy.hpp +++ b/speedy/speedy.hpp @@ -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; + } }; } diff --git a/storage/model/properties/property.hpp b/storage/model/properties/property.hpp index 1da19b419..61174225a 100644 --- a/storage/model/properties/property.hpp +++ b/storage/model/properties/property.hpp @@ -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> diff --git a/threading/pool.hpp b/threading/pool.hpp index 846a3f81d..f8d0b5cd5 100644 --- a/threading/pool.hpp +++ b/threading/pool.hpp @@ -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(); diff --git a/threading/task.hpp b/threading/task.hpp new file mode 100644 index 000000000..5ef9382bc --- /dev/null +++ b/threading/task.hpp @@ -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 diff --git a/transactions/transaction_engine.hpp b/transactions/engine.hpp similarity index 91% rename from transactions/transaction_engine.hpp rename to transactions/engine.hpp index 27a4d7504..a7929bc29 100644 --- a/transactions/transaction_engine.hpp +++ b/transactions/engine.hpp @@ -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() { diff --git a/utils/ioc/container.hpp b/utils/ioc/container.hpp index 9d00df900..05f9fc98c 100644 --- a/utils/ioc/container.hpp +++ b/utils/ioc/container.hpp @@ -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)) )); diff --git a/utils/placeholder.hpp b/utils/placeholder.hpp new file mode 100644 index 000000000..f5f5c1c6d --- /dev/null +++ b/utils/placeholder.hpp @@ -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