WIP: requests + refactoring

This commit is contained in:
Michele Caini 2016-07-01 16:18:51 +02:00
parent 176a8f0b31
commit 09acc1accf
14 changed files with 231 additions and 165 deletions

View File

@ -5,7 +5,7 @@
#include "uvw/idle.hpp"
#include "uvw/loop.hpp"
#include "uvw/prepare.hpp"
#include "uvw/resource.hpp"
#include "uvw/request.hpp"
#include "uvw/self.hpp"
#include "uvw/stream.hpp"
#include "uvw/tcp.hpp"

View File

@ -17,11 +17,7 @@ class Check final: public Handle<Check> {
check.publish(CheckEvent{});
}
explicit Check(std::shared_ptr<Loop> ref)
: Handle{ResourceType<uv_check_t>{}, std::move(ref)}
{
initialized = (uv_check_init(parent(), get<uv_check_t>()) == 0);
}
using Handle<Check>::Handle;
public:
template<typename... Args>
@ -29,9 +25,13 @@ public:
return std::shared_ptr<Check>{new Check{std::forward<Args>(args)...}};
}
bool init() {
return Handle<Check>::init<uv_check_t>(&uv_check_init);
}
void start() {
using CBF = CallbackFactory<void(uv_check_t *)>;
auto func = CBF::create<&Check::startCallback>(*this);
auto func = &CBF::template proto<&Check::startCallback>;
auto err = uv_check_start(get<uv_check_t>(), func);
if(err) publish(ErrorEvent{err});
}
@ -41,10 +41,8 @@ public:
if(err) publish(ErrorEvent{err});
}
explicit operator bool() const noexcept { return initialized; }
private:
bool initialized;
bool initialized = false;
};

View File

@ -32,6 +32,12 @@ struct CheckEvent: Event<CheckEvent> { };
struct CloseEvent: Event<CloseEvent> { };
struct ConnectEvent: Event<ConnectEvent> { };
struct DataEvent: Event<DataEvent> {
Buffer buffer{};
};
struct EndEvent: Event<EndEvent> { };
struct ErrorEvent: Event<ErrorEvent> {
explicit ErrorEvent(int code = 0): ec(code) { }
@ -47,6 +53,8 @@ struct ListenEvent: Event<ListenEvent> { };
struct PrepareEvent: Event<PrepareEvent> { };
struct ShutdownEvent: Event<ShutdownEvent> { };
struct TimerEvent: Event<TimerEvent> { };
struct UninitializedEvent: Event<UninitializedEvent> { };
struct WriteEvent: Event<WriteEvent> { };
}

View File

@ -1,10 +1,10 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "emitter.hpp"
#include "resource.hpp"
#include "self.hpp"
#include "loop.hpp"
@ -24,10 +24,6 @@ struct UVCallbackFactory;
template<typename T, typename H, typename... Args>
struct UVCallbackFactory<T, void(H, Args...)> {
template<void(*F)(T &, H, Args...)>
static auto create(T &) noexcept;
private:
template<void(*F)(T &, H, Args...)>
static void proto(H, Args...) noexcept;
};
@ -36,13 +32,37 @@ private:
}
template<typename>
struct HandleType;
template<> struct HandleType<uv_timer_t> { };
template<> struct HandleType<uv_prepare_t> { };
template<> struct HandleType<uv_check_t> { };
template<> struct HandleType<uv_idle_t> { };
template<> struct HandleType<uv_tcp_t> { };
template<typename T>
class Handle: public Emitter<T>, public Self<T>, public ResourceWrapper {
class Handle: public Emitter<T>, public Self<T> {
template<typename, typename>
friend struct details::UVCallbackFactory;
struct BaseWrapper {
virtual ~BaseWrapper() = default;
virtual void * get() const noexcept = 0;
};
template<typename U>
struct Wrapper: BaseWrapper {
Wrapper(): handle{std::make_unique<U>()} { }
void * get() const noexcept override { return handle.get(); }
private:
std::unique_ptr<U> handle;
};
static void closeCallback(T &ref, uv_handle_t *) {
ref.publish(CloseEvent{});
ref.reset();
}
protected:
@ -50,12 +70,37 @@ protected:
using CallbackFactory = details::UVCallbackFactory<T, F>;
template<typename U>
explicit Handle(ResourceType<U> rt, std::shared_ptr<Loop> ref)
: Emitter<T>{}, Self<T>{}, ResourceWrapper{std::move(rt)}, pLoop{std::move(ref)}
{ }
explicit Handle(HandleType<U>, std::shared_ptr<Loop> ref)
: Emitter<T>{}, Self<T>{},
wrapper{std::make_unique<Wrapper<U>>()},
pLoop{std::move(ref)}
{
this->template get<uv_handle_t>()->data = static_cast<T*>(this);
}
uv_loop_t* parent() const noexcept { return pLoop->loop.get(); }
template<typename U>
U* get() const noexcept { return reinterpret_cast<U*>(wrapper->get()); }
template<typename U, typename F>
bool init(F &&f) {
bool ret = true;
if(!active()) {
auto err = std::forward<F>(f)(parent(), get<U>());
if(err) {
this->publish(ErrorEvent{err});
ret = false;
} else {
this->leak();
}
}
return ret;
}
public:
virtual ~Handle() {
static_assert(std::is_base_of<Handle<T>, T>::value, "!");
@ -71,17 +116,15 @@ public:
bool referenced() const noexcept { return !(uv_has_ref(get<uv_handle_t>()) == 0); }
void close() noexcept {
auto handle = get<uv_handle_t>();
if(!uv_is_closing(handle)) {
if(!closing()) {
using CBF = CallbackFactory<void(uv_handle_t *)>;
T &ref = *static_cast<T*>(this);
auto func = CBF::template create<&Handle<T>::closeCallback>(ref);
uv_close(handle, func);
auto func = &CBF::template proto<&Handle<T>::closeCallback>;
uv_close(get<uv_handle_t>(), func);
}
}
private:
std::unique_ptr<BaseWrapper> wrapper;
std::shared_ptr<Loop> pLoop;
};
@ -89,28 +132,11 @@ private:
namespace details {
template<typename T, typename H, typename... Args>
template<void(*F)(T &, H, Args...)>
auto UVCallbackFactory<T, void(H, Args...)>::create(T &ref) noexcept {
Handle<T> &handle = ref;
handle.leak();
handle.template get<uv_handle_t>()->data = &ref;
return &UVCallbackFactory<T, void(H, Args...)>::proto<F>;
}
template<typename T, typename H, typename... Args>
template<void(*F)(T &, H, Args...)>
void UVCallbackFactory<T, void(H, Args...)>::proto(H handle, Args... args) noexcept {
T &ref = *(static_cast<T*>(details::get(handle)));
if(0 == uv_is_active(ref.template get<uv_handle_t>())) {
auto ptr = ref.shared_from_this();
ref.reset();
F(*ptr, handle, args...);
} else {
F(ref, handle, args...);
}
F(ref, handle, args...);
}

View File

@ -17,11 +17,7 @@ class Idle final: public Handle<Idle> {
idle.publish(IdleEvent{});
}
explicit Idle(std::shared_ptr<Loop> ref)
: Handle{ResourceType<uv_idle_t>{}, std::move(ref)}
{
initialized = (uv_idle_init(parent(), get<uv_idle_t>()) == 0);
}
using Handle<Idle>::Handle;
public:
template<typename... Args>
@ -29,9 +25,13 @@ public:
return std::shared_ptr<Idle>{new Idle{std::forward<Args>(args)...}};
}
bool init() {
return Handle<Idle>::init<uv_idle_t>(&uv_idle_init);
}
void start() {
using CBF = CallbackFactory<void(uv_idle_t *)>;
auto func = CBF::create<&Idle::startCallback>(*this);
auto func = &CBF::template proto<&Idle::startCallback>;
auto err = uv_idle_start(get<uv_idle_t>(), func);
if(err) publish(ErrorEvent{err});
}
@ -40,11 +40,6 @@ public:
auto err = uv_idle_stop(get<uv_idle_t>());
if(err) publish(ErrorEvent{err});
}
explicit operator bool() const noexcept { return initialized; }
private:
bool initialized;
};

View File

@ -66,7 +66,9 @@ public:
template<typename R, typename... Args>
std::shared_ptr<R> resource(Args&&... args) {
return R::create(shared_from_this(), std::forward<Args>(args)...);
auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
ptr = ptr->init() ? ptr : nullptr;
return ptr;
}
void close() noexcept {

View File

@ -17,11 +17,7 @@ class Prepare final: public Handle<Prepare> {
prepare.publish(PrepareEvent{});
}
explicit Prepare(std::shared_ptr<Loop> ref)
: Handle{ResourceType<uv_prepare_t>{}, std::move(ref)}
{
initialized = (uv_prepare_init(parent(), get<uv_prepare_t>()) == 0);
}
using Handle<Prepare>::Handle;
public:
template<typename... Args>
@ -29,9 +25,13 @@ public:
return std::shared_ptr<Prepare>{new Prepare{std::forward<Args>(args)...}};
}
bool init() {
return Handle<Prepare>::init<uv_prepare_t>(&uv_prepare_init);
}
void start() {
using CBF = CallbackFactory<void(uv_prepare_t *)>;
auto func = CBF::create<&Prepare::startCallback>(*this);
auto func = &CBF::template proto<&Prepare::startCallback>;
auto err = uv_prepare_start(get<uv_prepare_t>(), func);
if(err) publish(ErrorEvent{err});
}
@ -40,11 +40,6 @@ public:
auto err = uv_prepare_stop(get<uv_prepare_t>());
if(err) publish(ErrorEvent{err});
}
explicit operator bool() const noexcept { return initialized; }
private:
bool initialized;
};

29
src/uvw/request.hpp Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <uv.h>
namespace uvw {
template<uv_req_type>
struct RequestType;
template<> struct RequestType<UV_CONNECT> { };
template<> struct RequestType<UV_WRITE> { };
template<> struct RequestType<UV_SHUTDOWN> { };
template<> struct RequestType<UV_UDP_SEND> { };
template<> struct RequestType<UV_FS> { };
template<> struct RequestType<UV_WORK> { };
template<> struct RequestType<UV_GETADDRINFO> { };
template<> struct RequestType<UV_GETNAMEINFO> { };
template<typename T>
struct Request: T {
// TODO room for a pointer to a memory pool and a better memory management
};
}

View File

@ -1,44 +0,0 @@
#pragma once
#include <memory>
namespace uvw {
template<typename>
struct ResourceType { };
class ResourceWrapper {
struct BaseWrapper {
virtual ~BaseWrapper() = default;
virtual void * get() const noexcept = 0;
};
template<typename U>
struct Wrapper: BaseWrapper {
Wrapper(): handle{std::make_unique<U>()} { }
void * get() const noexcept override { return handle.get(); }
private:
std::unique_ptr<U> handle;
};
protected:
template<typename U>
explicit ResourceWrapper(ResourceType<U>)
: wrapper{std::make_unique<Wrapper<U>>()}
{ }
virtual ~ResourceWrapper() = default;
template<typename U>
U* get() const noexcept { return reinterpret_cast<U*>(wrapper->get()); }
private:
std::unique_ptr<BaseWrapper> wrapper;
};
}

View File

@ -8,46 +8,51 @@
#include <uv.h>
#include "event.hpp"
#include "handle.hpp"
#include "util.hpp"
namespace uvw {
template<typename T>
class Stream;
class Buffer final {
template<typename>
friend class Stream;
uv_buf_t uvBuf() const noexcept {
return uv_buf_init(data.get(), size);
}
public:
Buffer(std::unique_ptr<char[]> dt, std::size_t s)
: data{std::move(dt)}, size{s}
{ }
Buffer(Buffer &&) = default;
Buffer& operator=(Buffer &&) = default;
void reset(std::unique_ptr<char[]> dt, std::size_t s) noexcept {
data.swap(dt);
size = s;
}
private:
std::unique_ptr<char[]> data;
std::size_t size;
};
template<typename T>
class Stream: public Handle<T> {
static constexpr unsigned int DEFAULT_BACKLOG = 128;
static void allocCallback(T &, uv_handle_t *, std::size_t suggested, uv_buf_t *buf) {
buf->base = new char[suggested];
buf->len = suggested;
}
static void readCallback(T &ref, uv_stream_t *, ssize_t nread, const uv_buf_t *cbuf) {
uv_buf_t *buf = const_cast<uv_buf_t *>(cbuf);
if(nread == UV_EOF) {
ref.publish(EndEvent{});
delete buf->base;
} else if(nread > 0) {
std::unique_ptr<char[]> data{buf->base};
DataEvent event;
event.buffer.reset(std::move(data), nread);
ref.publish(std::move(event));
} else {
ref.publish(ErrorEvent(nread));
delete buf->base;
}
buf->base = nullptr;
buf->len = 0;
}
static void writeCallback(T &ref, uv_write_t *req, int status) {
if(status) {
ref.publish(ErrorEvent{status});
} else {
ref.publish(WriteEvent{});
}
delete req;
}
static void shutdownCallback(T &ref, uv_shutdown_t *, int status) {
if(status) ref.publish(ErrorEvent{status});
else ref.publish(ShutdownEvent{});
@ -60,21 +65,21 @@ class Stream: public Handle<T> {
protected:
template<typename U>
Stream(ResourceType<U> rt, std::shared_ptr<Loop> ref)
Stream(HandleType<U> rt, std::shared_ptr<Loop> ref)
: Handle<T>{std::move(rt), std::move(ref)}, sdown{std::make_unique<uv_shutdown_t>()}
{ }
public:
void shutdown() noexcept {
using CBF = typename Handle<T>::template CallbackFactory<void(uv_shutdown_t *, int)>;
auto func = CBF::template create<&Stream<T>::shutdownCallback>(*static_cast<T*>(this));
auto func = &CBF::template proto<&Stream<T>::shutdownCallback>;
auto err = uv_shutdown(sdown.get(), this->template get<uv_stream_t>(), func);
if(err) this->publish(ErrorEvent{err});
}
void listen(int backlog) noexcept {
using CBF = typename Handle<T>::template CallbackFactory<void(uv_stream_t *, int)>;
auto func = CBF::template create<&Stream<T>::listenCallback>(*static_cast<T*>(this));
auto func = &CBF::template proto<&Stream<T>::listenCallback>;
auto err = uv_listen(this->template get<uv_stream_t>(), backlog, func);
if(err) this->publish(ErrorEvent{err});
}
@ -83,14 +88,31 @@ public:
listen(DEFAULT_BACKLOG);
}
// TODO read
void read() {
using CBFAlloc = typename Handle<T>::template CallbackFactory<void(uv_handle_t *, std::size_t, uv_buf_t *)>;
using CBFRead = typename Handle<T>::template CallbackFactory<void(uv_stream_t *, ssize_t, const uv_buf_t *)>;
auto allocFunc = &CBFAlloc::template proto<&Stream<T>::allocCallback>;
auto readFunc = &CBFRead::template proto<&Stream<T>::readCallback>;
auto err = uv_read_start(this->template get<uv_stream_t>(), allocFunc, readFunc);
if(err) this->publish(ErrorEvent{err});
}
void stop() noexcept {
auto err = uv_read_stop(this->template get<uv_stream_t>());
if(err) this->publish(ErrorEvent{err});
}
// TODO write
void write(Buffer buf) {
using CBF = typename Handle<T>::template CallbackFactory<void(uv_write_t *, int)>;
auto func = &CBF::template proto<&Stream<T>::writeCallback>;
uv_buf_t data[] = { buf.uvBuf() };
uv_write_t *req = new uv_write_t;
auto err = uv_write(req, this->template get<uv_stream_t>(), data, 1, func);
if(err) {
delete req;
this->publish(ErrorEvent{err});
}
}
int tryWrite(Buffer buf) noexcept {
uv_buf_t data[] = { buf.uvBuf() };

View File

@ -21,11 +21,9 @@ class Tcp final: public Stream<Tcp> {
}
explicit Tcp(std::shared_ptr<Loop> ref)
: Stream{ResourceType<uv_tcp_t>{}, std::move(ref)},
: Stream{HandleType<uv_tcp_t>{}, std::move(ref)},
conn{std::make_unique<uv_connect_t>()}
{
initialized = (uv_tcp_init(parent(), get<uv_tcp_t>()) == 0);
}
{ }
template<typename I, typename F, typename..., typename Traits = details::IpTraits<I>>
Addr address(F &&f) {
@ -63,6 +61,10 @@ public:
return std::shared_ptr<Tcp>{new Tcp{std::forward<Args>(args)...}};
}
bool init() {
return Stream<Tcp>::init<uv_tcp_t>(&uv_tcp_init);
}
void noDelay(bool value = false) noexcept {
auto err = uv_tcp_nodelay(get<uv_tcp_t>(), value ? 1 : 0);
if(err) publish(ErrorEvent{err});
@ -102,7 +104,7 @@ public:
typename Traits::Type addr;
Traits::AddrFunc(ip.c_str(), port, &addr);
using CBF = CallbackFactory<void(uv_connect_t *, int)>;
auto func = CBF::create<&Tcp::connectCallback>(*this);
auto func = &CBF::proto<&Tcp::connectCallback>;
auto err = uv_tcp_connect(conn.get(), get<uv_tcp_t>(), reinterpret_cast<const sockaddr *>(&addr), func);
if(err) publish(ErrorEvent{err});
}

View File

@ -18,11 +18,7 @@ class Timer final: public Handle<Timer> {
timer.publish(TimerEvent{});
}
explicit Timer(std::shared_ptr<Loop> ref)
: Handle{ResourceType<uv_timer_t>{}, std::move(ref)}
{
initialized = (uv_timer_init(parent(), get<uv_timer_t>()) == 0);
}
using Handle<Timer>::Handle;
public:
using Time = std::chrono::milliseconds;
@ -32,9 +28,13 @@ public:
return std::shared_ptr<Timer>{new Timer{std::forward<Args>(args)...}};
}
bool init() {
return Handle<Timer>::init<uv_timer_t>(&uv_timer_init);
}
void start(Time timeout, Time repeat) {
using CBF = CallbackFactory<void(uv_timer_t *)>;
auto func = CBF::create<&Timer::startCallback>(*this);
auto func = &CBF::template proto<&Timer::startCallback>;
auto err = uv_timer_start(get<uv_timer_t>(), func, timeout.count(), repeat.count());
if(err) publish(ErrorEvent{err});
}
@ -56,11 +56,6 @@ public:
Time repeat() {
return Time{uv_timer_get_repeat(get<uv_timer_t>())};
}
explicit operator bool() const noexcept { return initialized; }
private:
bool initialized;
};

View File

@ -70,4 +70,33 @@ private:
using Addr = std::pair<std::string, unsigned int>;
class Buffer final {
template<typename>
friend class Stream;
uv_buf_t uvBuf() const noexcept {
return uv_buf_init(data.get(), size);
}
public:
Buffer(): data{}, size{} { }
Buffer(std::unique_ptr<char[]> dt, std::size_t s)
: data{std::move(dt)}, size{s}
{ }
Buffer(Buffer &&) = default;
Buffer& operator=(Buffer &&) = default;
void reset(std::unique_ptr<char[]> dt, std::size_t s) noexcept {
data.swap(dt);
size = s;
}
private:
std::unique_ptr<char[]> data;
std::size_t size;
};
}

View File

@ -36,7 +36,16 @@ void listen(uvw::Loop &loop) {
uvw::Addr remote = client->remote<uvw::Tcp::IPv4>();
std::cout << "remote: " << remote.first << " " << remote.second << std::endl;
client->close();
client->on<uvw::DataEvent>([](const uvw::DataEvent &event, uvw::Tcp &) {
std::cout << "data" << std::endl;
});
client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::Tcp &client) {
std::cout << "end" << std::endl;
client.close();
});
client->read();
});
tcp->once<uvw::CloseEvent>([](const uvw::CloseEvent &, uvw::Tcp &) mutable {
@ -55,11 +64,11 @@ void conn(uvw::Loop &loop) {
std::cout << "error " << std::endl;
});
tcp->once<uvw::ConnectEvent>([](const uvw::ConnectEvent &event, uvw::Tcp &tcp) mutable {
tcp->once<uvw::ConnectEvent>([](const uvw::ConnectEvent &, uvw::Tcp &tcp) mutable {
std::cout << "connect" << std::endl;
auto data = std::unique_ptr<char[]>(new char[1]);
data[0] = 42;
data[0] = 'a';
uvw::Buffer buf{std::move(data), 1};
int bw = tcp.tryWrite(std::move(buf));
std::cout << "written: " << ((int)bw) << std::endl;