diff --git a/README.md b/README.md index 018cd530..0a03cb43 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ **Please, note that `uvw` is still a work in progress**. `uvw` is a header-only, event based, tiny and easy to use C++ wrapper for *libuv*. -The basic idea is to hide completely the *C-ish* interace of *libuv* behind a graceful C++ API. Currently, no `uv_*_t` data structure is actually exposed by the library. -Note that `uvw` stays true to the API of *libuv* and it doesn't add anything to its interface. For the same reason, users of the library must follow the same rules who are used to follow with *libuv*. +The basic idea is to hide completely the *C-ish* interface of *libuv* behind a graceful C++ API. Currently, no `uv_*_t` data structure is actually exposed by the library. +Note that `uvw` stays true to the API of *libuv* and it doesn't add anything to its interface. For the same reasons, users of the library must follow the same rules who are used to follow with *libuv*. As an example, a *handle* should be initialized before any other operation and closed once it is no longer in use. ## Code Example @@ -14,10 +14,10 @@ As an example, a *handle* should be initialized before any other operation and c #include void listen(uvw::Loop &loop) { - std::shared_ptr tcp = loop.handle(); + std::shared_ptr tcp = loop.resource(); tcp->once([](const uvw::ListenEvent &event, uvw::Tcp &srv) mutable { - std::shared_ptr client = srv.loop().handle(); + std::shared_ptr client = srv.loop().resource(); client->on([ptr = srv.shared_from_this()](const uvw::CloseEvent &, uvw::Tcp &) mutable { ptr->close(); }); client->on([](const uvw::EndEvent &, uvw::Tcp &client) { client.close(); }); @@ -31,7 +31,7 @@ void listen(uvw::Loop &loop) { } void conn(uvw::Loop &loop) { - auto tcp = loop.handle(); + auto tcp = loop.resource(); tcp->on([](const uvw::ErrorEvent &, uvw::Tcp &) { /* handle errors */ }); @@ -90,7 +90,7 @@ To navigate it with your favorite browser: There is only one rule when using `uvw`: always initialize the handles and close them. Handles keep themselves alive until one closes them. Because of that, leaks are possible if users simply forget about a handle. -To be honest, initialization is performed under the hood and can be even passed over, as far as resources are created using the `Loop::handle` member method. +To be honest, initialization is performed under the hood and can be even passed over, as far as resources are created using the `Loop::resource` member method. Thus the rule quickly becomes *always close your handles*. It's simple as calling the `close` member method on them. The first thing to do to use `uvw` is to create a loop. In case the default one is enough, it's easy as doing this: @@ -102,9 +102,9 @@ Loops can be run using the `run`, `runOnce` and `runWait` member methods. Please In order to create a handle and to bind it to the given loop, just do the following: - auto tcp = loop.handle(); + auto tcp = loop.resource(); -A tcp handle will be created and initialized, thus a shared pointer to that handle will be returned. +A tcp handle will be created and initialized, thus a shared pointer to the handle will be returned. Users should check if pointers have been correctly initialized: in case of errors, they won't be. Another way to create a handle is: @@ -113,7 +113,7 @@ Another way to create a handle is: Pretty annoying indeed. Using a loop is the recommended approach. -Once a handle has been created, it will keep itself alive until one invoke the `close` member method on it. +Once a handle has been created, it will keep itself alive until one invokes the `close` member method on it. To know what are the handles that are still alive and bound to a given loop, just do the following: loop.walk([](uvw::BaseHandle &){ /* application code here */ }); @@ -151,12 +151,12 @@ The code below shows how to create a simple tcp server using `uvw`: ``` auto loop = uvw::Loop::getDefault(); -auto tcp = loop.handle(); +auto tcp = loop.resource(); tcp->on([](const uvw::ErrorEvent &, uvw::Tcp &srv) { /* something went wrong */ }); tcp->on([](const uvw::ListenEvent &event, uvw::Tcp &srv) mutable { - std::shared_ptr client = srv.loop().handle(); + std::shared_ptr client = srv.loop().resource(); client->once([](const uvw::EndEvent &, uvw::Tcp &client) { client.close(); }); client->on([](const uvw::DataEvent &, uvw::Tcp &) { /* data received */ }); srv.accept(*client); diff --git a/src/uvw.hpp b/src/uvw.hpp index 8870260f..b676d082 100644 --- a/src/uvw.hpp +++ b/src/uvw.hpp @@ -1,15 +1,12 @@ #include "uvw/async.hpp" #include "uvw/check.hpp" -#include "uvw/emitter.hpp" #include "uvw/event.hpp" -#include "uvw/handle.hpp" #include "uvw/idle.hpp" #include "uvw/loop.hpp" #include "uvw/prepare.hpp" -#include "uvw/request.hpp" #include "uvw/signal.hpp" -#include "uvw/stream.hpp" #include "uvw/tcp.hpp" #include "uvw/timer.hpp" #include "uvw/tty.hpp" #include "uvw/util.hpp" +#include "uvw/work.hpp" diff --git a/src/uvw/async.hpp b/src/uvw/async.hpp index 23f4a5e6..ab9c521a 100644 --- a/src/uvw/async.hpp +++ b/src/uvw/async.hpp @@ -19,7 +19,7 @@ class Async final: public Handle { } explicit Async(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/check.hpp b/src/uvw/check.hpp index fc6a2f06..920e802c 100644 --- a/src/uvw/check.hpp +++ b/src/uvw/check.hpp @@ -19,7 +19,7 @@ class Check final: public Handle { } explicit Check(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/event.hpp b/src/uvw/event.hpp index fb11693d..a96cb0d0 100644 --- a/src/uvw/event.hpp +++ b/src/uvw/event.hpp @@ -30,6 +30,7 @@ struct Event: BaseEvent { }; +struct AfterWorkEvent: Event { }; struct AsyncEvent: Event { }; struct CheckEvent: Event { }; struct CloseEvent: Event { }; @@ -82,6 +83,7 @@ private: struct TimerEvent: Event { }; struct UninitializedEvent: Event { }; +struct WorkEvent: Event { }; struct WriteEvent: Event { }; diff --git a/src/uvw/handle.hpp b/src/uvw/handle.hpp index 7d5c6925..f20cc338 100644 --- a/src/uvw/handle.hpp +++ b/src/uvw/handle.hpp @@ -1,124 +1,73 @@ #pragma once +#include #include #include #include -#include "emitter.hpp" -#include "loop.hpp" +#include "resource.hpp" namespace uvw { -template -struct HandleType; - -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; -template<> struct HandleType { }; - - template -class Handle - : public BaseHandle, - public Emitter, - public std::enable_shared_from_this -{ - struct BaseWrapper { - virtual ~BaseWrapper() = default; - virtual void * get() const noexcept = 0; - }; - - template - struct Wrapper: BaseWrapper { - Wrapper(): handle{std::make_unique()} { } - void * get() const noexcept override { return handle.get(); } - private: - std::unique_ptr handle; - }; - +class Handle: public BaseHandle, public Resource { static void closeCallback(uv_handle_t *handle) { Handle &ref = *(static_cast(handle->data)); - ref.initialized = false; + auto ptr = ref.shared_from_this(); + ptr->reset(); ref.publish(CloseEvent{}); - ref.leak.reset(); } protected: - template - explicit Handle(HandleType, std::shared_ptr ref) - : Emitter{}, - std::enable_shared_from_this{}, - wrapper{std::make_unique>()}, - pLoop{std::move(ref)}, - leak{nullptr}, - initialized{false} - { - this->template get()->data = static_cast(this); - } - - uv_loop_t* parent() const noexcept { return pLoop->loop.get(); } - - template - U* get() const noexcept { return reinterpret_cast(wrapper->get()); } + using Resource::Resource; template bool initialize(F &&f, Args&&... args) { - bool ret = true; - - if(!initialized) { - auto err = std::forward(f)(parent(), get(), std::forward(args)...); + if(!this->self()) { + auto err = std::forward(f)(this->parent(), this->template get(), std::forward(args)...); if(err) { this->publish(ErrorEvent{err}); - ret = false; } else { - leak = this->shared_from_this(); - initialized = true; + this->leak(); } } - return ret; - } - - template - auto invoke(F &&f, Args&&... args) { - auto err = std::forward(f)(std::forward(args)...); - if(err) { Emitter::publish(ErrorEvent{err}); } - return err; + return this->self(); } public: - virtual ~Handle() { - static_assert(std::is_base_of, T>::value, "!"); + bool active() const noexcept override { + return !(uv_is_active(this->template get()) == 0); } - Loop& loop() const noexcept { return *pLoop; } - - bool active() const noexcept override { return !(uv_is_active(get()) == 0); } - bool closing() const noexcept override { return !(uv_is_closing(get()) == 0); } - - void reference() noexcept override { uv_ref(get()); } - void unreference() noexcept override { uv_unref(get()); } - bool referenced() const noexcept override { return !(uv_has_ref(get()) == 0); } + bool closing() const noexcept override { + return !(uv_is_closing(this->template get()) == 0); + } void close() noexcept override { if(!closing()) { - uv_close(get(), &Handle::closeCallback); + uv_close(this->template get(), &Handle::closeCallback); } } -private: - std::unique_ptr wrapper; - std::shared_ptr pLoop; - std::shared_ptr leak; - bool initialized; + void reference() noexcept override { + uv_ref(this->template get()); + } + + void unreference() noexcept override { + uv_unref(this->template get()); + } + + bool referenced() const noexcept override { + return !(uv_has_ref(this->template get()) == 0); + } + + std::size_t size() const noexcept { + return uv_handle_size(this->template get()->type); + } }; diff --git a/src/uvw/idle.hpp b/src/uvw/idle.hpp index 8374bb57..2d175f79 100644 --- a/src/uvw/idle.hpp +++ b/src/uvw/idle.hpp @@ -19,7 +19,7 @@ class Idle final: public Handle { } explicit Idle(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/loop.hpp b/src/uvw/loop.hpp index b01095dd..27daa66e 100644 --- a/src/uvw/loop.hpp +++ b/src/uvw/loop.hpp @@ -26,7 +26,7 @@ public: class Loop final: public Emitter, public std::enable_shared_from_this { template - friend class Handle; + friend class Resource; using Deleter = std::function; @@ -76,12 +76,19 @@ public: } template - std::shared_ptr handle(Args&&... args) { + std::enable_if_t::value, std::shared_ptr> + resource(Args&&... args) { auto ptr = R::create(shared_from_this(), std::forward(args)...); ptr = ptr->init() ? ptr : nullptr; return ptr; } + template + std::enable_if_t::value, std::shared_ptr> + resource(Args&&... args) { + return R::create(shared_from_this(), std::forward(args)...); + } + void close() noexcept { auto err = uv_loop_close(loop.get()); if(err) { publish(ErrorEvent{err}); } diff --git a/src/uvw/prepare.hpp b/src/uvw/prepare.hpp index 0929dee2..c5948494 100644 --- a/src/uvw/prepare.hpp +++ b/src/uvw/prepare.hpp @@ -19,7 +19,7 @@ class Prepare final: public Handle { } explicit Prepare(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/request.hpp b/src/uvw/request.hpp index a50a8da6..ec3603f3 100644 --- a/src/uvw/request.hpp +++ b/src/uvw/request.hpp @@ -1,28 +1,28 @@ #pragma once +#include +#include #include +#include "resource.hpp" namespace uvw { -template -struct RequestType; - -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; -template<> struct RequestType { }; - - template -struct Request: T { - // TODO room for a pointer to a memory pool and a better memory management +class Request: public Resource { +protected: + using Resource::Resource; + +public: + void cancel() noexcept { + invoke(&uv_cancel, this->template get()); + } + + std::size_t size() const noexcept { + return uv_req_size(this->template get()->type); + } }; diff --git a/src/uvw/resource.hpp b/src/uvw/resource.hpp new file mode 100644 index 00000000..808cbe65 --- /dev/null +++ b/src/uvw/resource.hpp @@ -0,0 +1,80 @@ +#pragma once + + +#include +#include +#include +#include "emitter.hpp" +#include "self.hpp" +#include "loop.hpp" + + +namespace uvw { + + +template +struct ResourceType; + +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; +template<> struct ResourceType { }; + + +template +class Resource: public Emitter, public Self { + struct BaseWrapper { + virtual ~BaseWrapper() = default; + virtual void * get() const noexcept = 0; + }; + + template + struct Wrapper: BaseWrapper { + Wrapper(): resource{std::make_unique()} { } + void * get() const noexcept override { return resource.get(); } + private: + std::unique_ptr resource; + }; + +protected: + template + explicit Resource(ResourceType, std::shared_ptr ref) + : Emitter{}, + Self{}, + wrapper{std::make_unique>()}, + pLoop{std::move(ref)} + { + this->template get()->data = static_cast(this); + } + + uv_loop_t* parent() const noexcept { return pLoop->loop.get(); } + + template + U* get() const noexcept { return reinterpret_cast(wrapper->get()); } + + template + auto invoke(F &&f, Args&&... args) { + auto err = std::forward(f)(std::forward(args)...); + if(err) { Emitter::publish(ErrorEvent{err}); } + return err; + } + +public: + virtual ~Resource() { + static_assert(std::is_base_of, T>::value, "!"); + } + + Loop& loop() const noexcept { return *pLoop; } + +private: + std::unique_ptr wrapper; + std::shared_ptr pLoop; +}; + + +} diff --git a/src/uvw/self.hpp b/src/uvw/self.hpp new file mode 100644 index 00000000..aff2e3d1 --- /dev/null +++ b/src/uvw/self.hpp @@ -0,0 +1,21 @@ +#pragma once + + +#include + + +namespace uvw { + + +template +struct Self: std::enable_shared_from_this { + void leak() noexcept { ptr = this->shared_from_this(); } + void reset() noexcept { ptr.reset(); } + bool self() const noexcept { return static_cast(ptr); } + +private: + std::shared_ptr ptr{nullptr}; +}; + + +} diff --git a/src/uvw/signal.hpp b/src/uvw/signal.hpp index 66747061..5a7a20e2 100644 --- a/src/uvw/signal.hpp +++ b/src/uvw/signal.hpp @@ -19,7 +19,7 @@ class Signal final: public Handle { } explicit Signal(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/stream.hpp b/src/uvw/stream.hpp index 3c0adf01..c38aebd8 100644 --- a/src/uvw/stream.hpp +++ b/src/uvw/stream.hpp @@ -37,6 +37,7 @@ class Stream: public Handle { } static void writeCallback(uv_write_t *req, int status) { + // TODO migrate to Request (see request.hpp for further details) T &ref = *(static_cast(req->handle->data)); if(status) { ref.publish(ErrorEvent{status}); } else { ref.publish(WriteEvent{}); } @@ -44,6 +45,7 @@ class Stream: public Handle { } static void shutdownCallback(uv_shutdown_t *req, int status) { + // TODO migrate to Request (see request.hpp for further details) T &ref = *(static_cast(req->handle->data)); if(status) { ref.publish(ErrorEvent{status}); } else { ref.publish(ShutdownEvent{}); } @@ -57,9 +59,9 @@ class Stream: public Handle { protected: template - Stream(HandleType rt, std::shared_ptr ref) - : Handle{std::move(rt), - std::move(ref)}, + Stream(ResourceType rt, std::shared_ptr ref) + : Handle{std::move(rt), std::move(ref)}, + // TODO migrate to Request (see request.hpp for further details) sdown{std::make_unique()} { } @@ -86,6 +88,7 @@ public: void write(char *data, ssize_t length) { uv_buf_t bufs[] = { uv_buf_init(data, length) }; + // TODO migrate to Request (see request.hpp for further details) uv_write_t *req = new uv_write_t; auto err = uv_write(req, this->template get(), bufs, 1, &Stream::writeCallback); @@ -125,6 +128,7 @@ public: } private: + // TODO migrate to Request (see request.hpp for further details) std::unique_ptr sdown; }; diff --git a/src/uvw/tcp.hpp b/src/uvw/tcp.hpp index ec31b91c..e3b47c3e 100644 --- a/src/uvw/tcp.hpp +++ b/src/uvw/tcp.hpp @@ -16,13 +16,15 @@ namespace uvw { class Tcp final: public Stream { static void connectCallback(uv_connect_t *req, int status) { + // TODO migrate to Request (see request.hpp for further details) Tcp &tcp = *(static_cast(req->handle->data)); if(status) { tcp.publish(ErrorEvent{status}); } else { tcp.publish(ConnectEvent{}); } } explicit Tcp(std::shared_ptr ref) - : Stream{HandleType{}, std::move(ref)}, + // TODO migrate to Request (see request.hpp for further details) + : Stream{ResourceType{}, std::move(ref)}, conn{std::make_unique()} { } @@ -112,6 +114,7 @@ public: } private: + // TODO migrate to Request (see request.hpp for further details) std::unique_ptr conn; }; diff --git a/src/uvw/timer.hpp b/src/uvw/timer.hpp index c321af80..37747755 100644 --- a/src/uvw/timer.hpp +++ b/src/uvw/timer.hpp @@ -20,7 +20,7 @@ class Timer final: public Handle { } explicit Timer(std::shared_ptr ref) - : Handle{HandleType{}, std::move(ref)} + : Handle{ResourceType{}, std::move(ref)} { } public: diff --git a/src/uvw/tty.hpp b/src/uvw/tty.hpp index 2a7ecc24..243fd949 100644 --- a/src/uvw/tty.hpp +++ b/src/uvw/tty.hpp @@ -31,7 +31,7 @@ class TTY final: public Stream { explicit TTY(std::shared_ptr ref, details::FileDescriptor, bool readable) - : Stream{HandleType{}, std::move(ref)}, + : Stream{ResourceType{}, std::move(ref)}, fd{FD}, rw{readable ? 1 : 0} { } diff --git a/src/uvw/work.hpp b/src/uvw/work.hpp new file mode 100644 index 00000000..1efa6f9a --- /dev/null +++ b/src/uvw/work.hpp @@ -0,0 +1,47 @@ +#pragma once + + +#include +#include +#include +#include "event.hpp" +#include "request.hpp" +#include "util.hpp" + + +namespace uvw { + + +class Work final: public Request { + static void workCallback(uv_work_t *req) { + Work &work = *(static_cast(req->data)); + work.publish(WorkEvent{}); + } + + static void afterWorkCallback(uv_work_t *req, int status) { + Work &work = *(static_cast(req->data)); + auto ptr = work.shared_from_this(); + ptr->reset(); + if(status) { work.publish(ErrorEvent{status}); } + else { work.publish(AfterWorkEvent{}); } + } + + explicit Work(std::shared_ptr ref) + : Request{ResourceType{}, std::move(ref)} + { } + +public: + template + static std::shared_ptr create(Args&&... args) { + return std::shared_ptr{new Work{std::forward(args)...}}; + } + + void queue() noexcept { + if(0 == invoke(&uv_queue_work, parent(), get(), &workCallback, &afterWorkCallback)) { + leak(); + } + } +}; + + +} diff --git a/test/main.cpp b/test/main.cpp index b085f446..1f87e951 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -6,7 +6,7 @@ void listen(uvw::Loop &loop) { - std::shared_ptr tcp = loop.handle(); + std::shared_ptr tcp = loop.resource(); tcp->on([](const uvw::ErrorEvent &, uvw::Tcp &) { std::cout << "error " << std::endl; @@ -15,7 +15,7 @@ void listen(uvw::Loop &loop) { tcp->once([](const uvw::ListenEvent &event, uvw::Tcp &srv) mutable { std::cout << "listen" << std::endl; - std::shared_ptr client = srv.loop().handle(); + std::shared_ptr client = srv.loop().resource(); client->on([](const uvw::ErrorEvent &, uvw::Tcp &) { std::cout << "error " << std::endl; @@ -62,7 +62,7 @@ void listen(uvw::Loop &loop) { void conn(uvw::Loop &loop) { - auto tcp = loop.handle(); + auto tcp = loop.resource(); tcp->on([](const uvw::ErrorEvent &, uvw::Tcp &) { std::cout << "error " << std::endl;