Commit c9a8e990 authored by octal's avatar octal

Squashed commit of the following:

commit e60bcb0bc15fbba453340d69d106c5850b9a6d67
Author: octal <mathieu.stefani@supinfo.com>
Date:   Thu Nov 12 14:14:53 2015 +0100

    Added a NoExcept concept that will call std::terminate if a promise has been rejected

commit bae200046427b93e6f124c848ab36d246a7580c3
Author: octal <mathieu.stefani@supinfo.com>
Date:   Wed Nov 11 14:50:27 2015 +0100

    Updated Optional to be able to move the result out when chaining a Promise

commit da60bc8b02a1fb89a91985a0da9ddf4b7219f5b3
Author: octal <mathieu.stefani@supinfo.com>
Date:   Fri Nov 6 17:43:36 2015 +0100

    Started working on a proof-of-concept implementation of an asynchronous primitive based on the Promises/A+ specification
parent 0d8215d1
......@@ -18,7 +18,9 @@ class MyHandler : public Net::Http::Handler {
.add(std::make_shared<Header::Server>("lys"))
.add(std::make_shared<Header::ContentType>(MIME(Text, Plain)));
response.send(Net::Http::Code::Ok, "PONG");
response.send(Net::Http::Code::Ok, "PONG").then([](ssize_t bytes) {
std::cout << "Sent total of " << bytes << " bytes" << std::endl;
}, Async::IgnoreException);
}
}
......
set(SOURCE_FILES
tcp.cc
io.cc
listener.cc
net.cc
peer.cc
......
/* async.h
Mathieu Stefani, 05 novembre 2015
This header brings a Promise<T> class inspired by the Promises/A+
specification for asynchronous operations
*/
#pragma once
#include <type_traits>
#include <functional>
#include <memory>
#include "optional.h"
namespace Async {
class Error : public std::runtime_error {
public:
explicit Error(const char* what) : std::runtime_error(what) { }
explicit Error(const std::string& what) : std::runtime_error(what) { }
};
/*
- Direct: The continuation will be directly called in the context
of the same thread
- Deferred: The continuation will be called in an asynchronous way
Note that Deferred requires the Promise to be bound to an event
loop
*/
enum class Continuation {
Direct,
Deferred
};
enum class State {
Pending, Fulfilled, Rejected
};
namespace Private {
struct IgnoreException {
void operator()(std::exception_ptr) const { }
};
struct NoExcept {
void operator()(std::exception_ptr) const { std::terminate(); }
};
}
static constexpr Private::IgnoreException IgnoreException;
static constexpr Private::NoExcept NoExcept;
template<typename T> class Promise;
class PromiseBase {
public:
virtual ~PromiseBase() { }
virtual bool isPending() const = 0;
virtual bool isFulfilled() const = 0;
virtual bool isRejected() const = 0;
bool isSettled() const { return isFulfilled() || isRejected(); }
};
namespace Private {
class Core;
class Request {
public:
virtual void resolve(const std::shared_ptr<Private::Core>& core) = 0;
virtual void reject(const std::shared_ptr<Private::Core>& core) = 0;
virtual void disconnect() = 0;
};
struct Core {
Core(State state)
: state(state)
, promise(nullptr)
{ }
State state;
std::exception_ptr exc;
PromiseBase* promise;
std::vector<std::shared_ptr<Request>> requests;
void attach(PromiseBase* p) {
if (promise)
throw Error("Trying to double-attach a Promise");
promise = p;
}
void detach() {
if (!promise)
throw Error("Trying to detach a non attached Promise");
promise = nullptr;
}
virtual void* memory() = 0;
};
}
class Resolver {
public:
Resolver(const std::shared_ptr<Private::Core> &core)
: core_(core)
{ }
template<typename Arg>
bool operator()(Arg&& arg) {
typedef typename std::remove_reference<Arg>::type Type;
auto promise = core_->promise;
if (!promise) return false;
if (core_->state != State::Pending)
throw Error("Attempt to resolve a fulfilled promise");
void *mem = core_->memory();
new (mem) Type(std::forward<Arg>(arg));
core_->state = State::Fulfilled;
for (const auto& req: core_->requests) {
req->resolve(core_);
}
return true;
}
private:
std::shared_ptr<Private::Core> core_;
};
class Rejection {
public:
Rejection(const std::shared_ptr<Private::Core>& core)
: core_(core)
{ }
template<typename Exc>
bool operator()(Exc exc) {
auto promise = core_->promise;
if (!promise)
return false;
if (core_->state != State::Pending)
throw Error("Attempt to reject a fulfilled promise");
core_->exc = std::make_exception_ptr(exc);
core_->state = State::Rejected;
for (const auto& req: core_->requests) {
req->reject(core_);
}
return true;
}
private:
std::shared_ptr<Private::Core> core_;
};
namespace detail {
template<typename Func, typename T>
struct IsCallable {
template<typename U>
static auto test(U *) -> decltype(std::declval<Func>()(std::declval<T>()), std::true_type());
template<typename U>
static auto test(...) -> std::false_type;
static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
}
template<typename T>
class Promise : public PromiseBase
{
public:
typedef std::function<void (Resolver&, Rejection&)> ResolveFunc;
struct Request : public Private::Request {
virtual Promise<T> chain() = 0;
void disconnect() {
}
};
Promise(ResolveFunc func)
: core_(std::make_shared<CoreT>())
, resolver_(core_)
, rejection_(core_)
{
core_->attach(this);
func(resolver_, rejection_);
}
Promise(const Promise<T>& other) = delete;
Promise& operator=(const Promise<T>& other) = delete;
Promise(Promise<T>&& other) = default;
Promise& operator=(Promise<T>&& other) = default;
~Promise()
{
core_->detach();
}
bool isPending() const { return core_->state == State::Pending; }
bool isFulfilled() const { return core_->state == State::Fulfilled; }
bool isRejected() const { return core_->state == State::Rejected; }
template<typename ResolveFunc, typename RejectFunc>
std::shared_ptr<Request> then(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc, Continuation type = Continuation::Direct) {
static_assert(detail::IsCallable<ResolveFunc, T>::value, "Function is not compatible with underlying promise type");
// Due to how template argument deduction works on universal references, we need to remove any reference from
// the deduced function type, fun fun fun
typedef typename std::remove_reference<ResolveFunc>::type ResolveFuncType;
typedef typename std::remove_reference<RejectFunc>::type RejectFuncType;
std::shared_ptr<Request> req;
req.reset(ContinuationFactory<ResolveFuncType>::create(
std::forward<ResolveFunc>(resolveFunc), std::forward<RejectFunc>(rejectFunc)));
if (isFulfilled()) {
req->resolve(core_);
}
else if (isRejected()) {
req->reject(core_);
}
core_->requests.push_back(req);
return req;
}
private:
template<typename U>
struct Core : public Private::Core {
Core()
: Private::Core(State::Pending)
{ }
template<class Other>
struct Rebind {
typedef Core<Other> Type;
};
typedef typename std::aligned_storage<sizeof(U), alignof(U)>::type Storage;
Storage storage;
const U& value() const {
if (state != State::Fulfilled)
throw Error("Attempted to take the value of a not fulfilled promise");
return *reinterpret_cast<const U*>(&storage);
}
void *memory() {
return &storage;
}
};
typedef Core<T> CoreT;
Promise(const std::shared_ptr<CoreT>& core)
: core_(core)
, resolver_(core)
, rejection_(core)
{
core_->attach(this);
}
struct Continuable : public Request {
Continuable()
: resolveCount_(0)
, rejectCount_(0)
{ }
void resolve(const std::shared_ptr<Private::Core>& core) {
if (resolveCount_ >= 1)
throw Error("Resolve must not be called more than once");
doResolve(coreCast(core));
++resolveCount_;
}
void reject(const std::shared_ptr<Private::Core>& core) {
if (rejectCount_ >= 1)
throw Error("Reject must not be called more than once");
doReject(coreCast(core));
++rejectCount_;
}
std::shared_ptr<CoreT> coreCast(const std::shared_ptr<Private::Core>& core) const {
return std::static_pointer_cast<CoreT>(core);
}
virtual void doResolve(const std::shared_ptr<CoreT>& core) const = 0;
virtual void doReject(const std::shared_ptr<CoreT>& core) const = 0;
size_t resolveCount_;
size_t rejectCount_;
};
template<typename ResolveFunc, typename RejectFunc>
struct ThenContinuation : public Continuable {
ThenContinuation(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc)
: resolveFunc_(std::forward<ResolveFunc>(resolveFunc))
, rejectFunc_(std::forward<RejectFunc>(rejectFunc))
{
}
void doResolve(const std::shared_ptr<CoreT>& core) const {
resolveFunc_(core->value());
}
void doReject(const std::shared_ptr<CoreT>& core) const {
rejectFunc_(core->exc);
}
Promise<T> chain() {
throw Error("The request is not chainable");
}
ResolveFunc resolveFunc_;
RejectFunc rejectFunc_;
};
template<typename ResolveFunc, typename RejectFunc, typename Return>
struct ThenReturnContinuation : public Continuable {
ThenReturnContinuation(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc)
: resolveFunc_(std::forward<ResolveFunc>(resolveFunc))
, rejectFunc_(std::forward<RejectFunc>(rejectFunc))
{
}
void doResolve(const std::shared_ptr<CoreT>& core) const {
auto ret = resolveFunc_(core->value());
result = Some(std::move(ret));
}
void doReject(const std::shared_ptr<CoreT>& core) const {
rejectFunc_(core->exc);
}
Promise<Return> chain() {
typedef typename CoreT::template Rebind<Return>::Type CoreType;
auto core = std::make_shared<CoreType>();
optionally_do(result, [&core](Return&& result) {
void *mem = core->memory();
new (mem) Return(std::move(result));
core->state = State::Fulfilled;
});
return Promise<Return>(core);
}
ResolveFunc resolveFunc_;
RejectFunc rejectFunc_;
mutable Optional<Return> result;
};
template<typename ResolveFunc, typename RejectFunc>
struct ThenChainContinuation : public Continuable {
ThenChainContinuation(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc)
: resolveFunc_(std::forward<ResolveFunc>(resolveFunc))
, rejectFunc_(std::forward<RejectFunc>(rejectFunc))
{
}
void doResolve(const std::shared_ptr<CoreT>& core) const {
auto promise = resolveFunc_(core->value());
}
void doReject(const std::shared_ptr<CoreT>& core) const {
rejectFunc_(core->exc);
}
Promise<T> chain() {
auto core = std::make_shared<CoreT>();
return Promise<T>(core);
}
ResolveFunc resolveFunc_;
RejectFunc rejectFunc_;
};
template<typename ResolveFunc>
struct ContinuationFactory : public ContinuationFactory<decltype(&ResolveFunc::operator())> { };
template<typename R, typename Class, typename... Args>
struct ContinuationFactory<R (Class::*)(Args...) const> {
template<typename ResolveFunc, typename RejectFunc>
static Continuable *create(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) {
return new ThenReturnContinuation<ResolveFunc, RejectFunc, R>(
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc));
}
};
template<typename Class, typename... Args>
struct ContinuationFactory<void (Class::*)(Args ...) const> {
template<typename ResolveFunc, typename RejectFunc>
static Continuable *create(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) {
return new ThenContinuation<ResolveFunc, RejectFunc>(
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc));
}
};
template<typename U, typename Class, typename... Args>
struct ContinuationFactory<Promise<U> (Class::*)(Args...) const> {
template<typename ResolveFunc, typename RejectFunc>
static Continuable* create(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) {
return new ThenChainContinuation<ResolveFunc, RejectFunc>(
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc));
}
};
std::shared_ptr<CoreT> core_;
Resolver resolver_;
Rejection rejection_;
};
} // namespace Async
......@@ -361,12 +361,12 @@ Response::associatePeer(const std::shared_ptr<Tcp::Peer>& peer)
peer_ = peer;
}
ssize_t
Async::Promise<ssize_t>
Response::send(Code code) {
return send(code, "");
}
ssize_t
Async::Promise<ssize_t>
Response::send(Code code, const std::string& body, const Mime::MediaType& mime)
{
char buffer[Const::MaxBuffer];
......@@ -461,10 +461,6 @@ Handler::onInput(const char* buffer, size_t len, const std::shared_ptr<Tcp::Peer
}
}
void
Handler::onOutput() {
}
void
Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer) {
peer->putData(ParserData, std::make_shared<Private::Parser>());
......
......@@ -15,6 +15,7 @@
#include "http_defs.h"
#include "stream.h"
#include "mime.h"
#include "async.h"
namespace Net {
......@@ -96,8 +97,8 @@ public:
void setMime(const Mime::MediaType& mime);
ssize_t send(Code code);
ssize_t send(Code code, const std::string& body, const Mime::MediaType &mime = Mime::MediaType());
Async::Promise<ssize_t> send(Code code);
Async::Promise<ssize_t> send(Code code, const std::string& body, const Mime::MediaType &mime = Mime::MediaType());
private:
Response();
......@@ -203,7 +204,6 @@ namespace Private {
class Handler : public Net::Tcp::Handler {
public:
void onInput(const char* buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer);
void onOutput();
void onConnection(const std::shared_ptr<Tcp::Peer>& peer);
void onDisconnection(const std::shared_ptr<Tcp::Peer>& peer);
......
/* io.cc
Mathieu Stefani, 05 novembre 2015
I/O handling
*/
#include <thread>
#include "io.h"
#include "listener.h"
#include "peer.h"
#include "os.h"
namespace Net {
namespace Tcp {
using namespace Polling;
struct Message {
virtual ~Message() { }
enum class Type { Shutdown };
virtual Type type() const = 0;
};
struct ShutdownMessage : public Message {
Type type() const { return Type::Shutdown; }
};
template<typename To>
To *message_cast(const std::unique_ptr<Message>& from)
{
return static_cast<To *>(from.get());
}
IoWorker::IoWorker() {
}
IoWorker::~IoWorker() {
if (thread && thread->joinable()) {
thread->join();
}
}
void
IoWorker::start(const std::shared_ptr<Handler>& handler, Flags<Options> options) {
handler_ = handler;
options_ = options;
thread.reset(new std::thread([this]() {
this->run();
}));
if (pins.count() > 0) {
auto cpuset = pins.toPosix();
auto handle = thread->native_handle();
pthread_setaffinity_np(handle, sizeof (cpuset), &cpuset);
}
}
void
IoWorker::pin(const CpuSet& set) {
pins = set;
if (thread) {
auto cpuset = set.toPosix();
auto handle = thread->native_handle();
pthread_setaffinity_np(handle, sizeof (cpuset), &cpuset);
}
}
void
IoWorker::shutdown() {
mailbox.post(new ShutdownMessage());
}
std::shared_ptr<Peer>&
IoWorker::getPeer(Fd fd)
{
std::unique_lock<std::mutex> guard(peersMutex);
auto it = peers.find(fd);
if (it == std::end(peers))
{
throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
}
return it->second;
}
std::shared_ptr<Peer>&
IoWorker::getPeer(Polling::Tag tag)
{
return getPeer(tag.value());
}
void
IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) {
char buffer[Const::MaxBuffer];
memset(buffer, 0, sizeof buffer);
ssize_t totalBytes = 0;
int fd = peer->fd();
for (;;) {
ssize_t bytes;
bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
if (bytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (totalBytes > 0) {
handler_->onInput(buffer, totalBytes, peer);
}
} else {
if (errno == ECONNRESET) {
handler_->onDisconnection(peer);
close(fd);
}
else {
throw std::runtime_error(strerror(errno));
}
}
break;
}
else if (bytes == 0) {
handler_->onDisconnection(peer);
close(fd);
break;
}
else {
totalBytes += bytes;
if (totalBytes >= Const::MaxBuffer) {
std::cerr << "Too long packet" << std::endl;
break;
}
}
}
}
void
IoWorker::handleNewPeer(const std::shared_ptr<Peer>& peer)
{
int fd = peer->fd();
{
std::unique_lock<std::mutex> guard(peersMutex);
peers.insert(std::make_pair(fd, peer));
}
peer->io = this;
handler_->onConnection(peer);
poller.addFd(fd, NotifyOn::Read, Polling::Tag(fd), Polling::Mode::Edge);
}
void
IoWorker::run() {
if (pins.count() > 0) {
}
mailbox.bind(poller);
std::chrono::milliseconds timeout(-1);
for (;;) {
std::vector<Polling::Event> events;
int ready_fds;
switch(ready_fds = poller.poll(events, 1024, timeout)) {
case -1:
break;
case 0:
timeout = std::chrono::milliseconds(-1);
break;
default:
for (const auto& event: events) {
if (event.tag == mailbox.tag()) {
std::unique_ptr<Message> msg(mailbox.clear());
if (msg->type() == Message::Type::Shutdown) {
return;
}
} else {
auto& peer = getPeer(event.tag);
if (event.flags.hasFlag(NotifyOn::Read)) {
handleIncoming(peer);
}
else if (event.flags.hasFlag(NotifyOn::Write)) {
}
}
}
timeout = std::chrono::milliseconds(0);
break;
}
}
}
Async::Promise<ssize_t>
IoWorker::asyncWrite(Fd fd, const void* buf, size_t len) {
return Async::Promise<ssize_t>([=](Async::Resolver& resolve, Async::Rejection& reject) {
ssize_t bytes = ::send(fd, buf, len, 0);
if (bytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
toWrite.insert(
std::make_pair(fd,
OnHoldWrite(std::move(resolve), std::move(reject), buf, len)));
poller.addFdOneShot(fd, NotifyOn::Write, Polling::Tag(fd));
}
else {
reject(std::runtime_error("send"));
}
}
else {
resolve(bytes);
}
});
}
} // namespace Tcp
} // namespace Net
/* io.h
Mathieu Stefani, 05 novembre 2015
I/O handling
*/
#pragma once
#include "mailbox.h"
#include "flags.h"
#include "os.h"
#include "tcp.h"
#include "async.h"
#include <thread>
#include <mutex>
#include <memory>
#include <unordered_map>
namespace Net {
namespace Tcp {
class Peer;
class Message;
class Handler;
class IoWorker {
public:
friend class Peer;
PollableMailbox<Message> mailbox;
IoWorker();
~IoWorker();
void start(const std::shared_ptr<Handler> &handler, Flags<Options> options);
void handleNewPeer(const std::shared_ptr<Peer>& peer);
void pin(const CpuSet& set);
void shutdown();
private:
struct OnHoldWrite {
OnHoldWrite(Async::Resolver resolve, Async::Rejection reject,
const void *buf, size_t len)
: resolve(std::move(resolve))
, reject(std::move(reject))
, buf(buf)
, len(len)
{ }
Async::Resolver resolve;
Async::Rejection reject;
const void *buf;
size_t len;
};
Polling::Epoll poller;
std::unique_ptr<std::thread> thread;
mutable std::mutex peersMutex;
std::unordered_map<Fd, std::shared_ptr<Peer>> peers;
std::unordered_map<Fd, OnHoldWrite> toWrite;
std::shared_ptr<Handler> handler_;
Flags<Options> options_;
CpuSet pins;
std::shared_ptr<Peer>& getPeer(Fd fd);
std::shared_ptr<Peer>& getPeer(Polling::Tag tag);
Async::Promise<ssize_t> asyncWrite(Fd fd, const void *buf, size_t len);
void handleIncoming(const std::shared_ptr<Peer>& peer);
void run();
};
} // namespace Tcp
} // namespace Net
......@@ -3,7 +3,6 @@
*/
#include <thread>
#include <iostream>
#include <sys/socket.h>
#include <unistd.h>
......@@ -40,23 +39,6 @@ namespace {
using Polling::NotifyOn;
struct Message {
virtual ~Message() { }
enum class Type { Shutdown };
virtual Type type() const = 0;
};
struct ShutdownMessage : public Message {
Type type() const { return Type::Shutdown; }
};
template<typename To>
To *message_cast(const std::unique_ptr<Message>& from)
{
return static_cast<To *>(from.get());
}
void setSocketOptions(Fd fd, Flags<Options> options) {
if (options.hasFlag(Options::ReuseAddr)) {
......@@ -82,179 +64,6 @@ void setSocketOptions(Fd fd, Flags<Options> options) {
}
IoWorker::IoWorker() {
}
IoWorker::~IoWorker() {
if (thread && thread->joinable()) {
thread->join();
}
}
void
IoWorker::start(const std::shared_ptr<Handler>& handler, Flags<Options> options) {
handler_ = handler;
options_ = options;
thread.reset(new std::thread([this]() {
this->run();
}));
if (pins.count() > 0) {
auto cpuset = pins.toPosix();
auto handle = thread->native_handle();
pthread_setaffinity_np(handle, sizeof (cpuset), &cpuset);
}
}
void
IoWorker::pin(const CpuSet& set) {
pins = set;
if (thread) {
auto cpuset = set.toPosix();
auto handle = thread->native_handle();
pthread_setaffinity_np(handle, sizeof (cpuset), &cpuset);
}
}
std::shared_ptr<Peer>&
IoWorker::getPeer(Fd fd)
{
std::unique_lock<std::mutex> guard(peersMutex);
auto it = peers.find(fd);
if (it == std::end(peers))
{
throw std::runtime_error("No peer found for fd: " + to_string(fd));
}
return it->second;
}
std::shared_ptr<Peer>&
IoWorker::getPeer(Polling::Tag tag)
{
return getPeer(tag.value());
}
void
IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) {
char buffer[Const::MaxBuffer];
memset(buffer, 0, sizeof buffer);
ssize_t totalBytes = 0;
int fd = peer->fd();
for (;;) {
ssize_t bytes;
bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
if (bytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (totalBytes > 0) {
handler_->onInput(buffer, totalBytes, peer);
}
} else {
if (errno == ECONNRESET) {
handler_->onDisconnection(peer);
close(fd);
}
else {
throw std::runtime_error(strerror(errno));
}
}
break;
}
else if (bytes == 0) {
handler_->onDisconnection(peer);
close(fd);
break;
}
else {
totalBytes += bytes;
if (totalBytes >= Const::MaxBuffer) {
cerr << "Too long packet" << endl;
break;
}
}
}
}
void
IoWorker::handleNewPeer(const std::shared_ptr<Peer>& peer)
{
int fd = peer->fd();
{
std::unique_lock<std::mutex> guard(peersMutex);
peers.insert(std::make_pair(fd, peer));
}
handler_->onConnection(peer);
poller.addFd(fd, NotifyOn::Read, Polling::Tag(fd), Polling::Mode::Edge);
}
void
IoWorker::run() {
if (pins.count() > 0) {
}
mailbox.bind(poller);
std::chrono::milliseconds timeout(-1);
for (;;) {
std::vector<Polling::Event> events;
int ready_fds;
switch(ready_fds = poller.poll(events, 1024, timeout)) {
case -1:
break;
case 0:
timeout = std::chrono::milliseconds(-1);
break;
default:
for (const auto& event: events) {
if (event.tag == mailbox.tag()) {
std::unique_ptr<Message> msg(mailbox.clear());
if (msg->type() == Message::Type::Shutdown) {
return;
}
} else {
if (event.flags.hasFlag(NotifyOn::Read)) {
auto& peer = getPeer(event.tag);
handleIncoming(peer);
}
}
}
timeout = std::chrono::milliseconds(0);
break;
}
}
}
void
IoWorker::handleMailbox() {
}
Handler::Handler()
{ }
Handler::~Handler()
{ }
void
Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer) {
}
void
Handler::onDisconnection(const std::shared_ptr<Tcp::Peer>& peer) {
}
Listener::Listener()
: listen_fd(-1)
, backlog_(Const::MaxBacklog)
......@@ -267,7 +76,6 @@ Listener::Listener(const Address& address)
{
}
void
Listener::init(size_t workers, Flags<Options> options, int backlog)
{
......@@ -400,7 +208,7 @@ Listener::run() {
void
Listener::shutdown() {
for (auto &worker: ioGroup) {
worker->mailbox.post(new ShutdownMessage());
worker->shutdown();
}
}
......
......@@ -6,76 +6,26 @@
#pragma once
#include "tcp.h"
#include "net.h"
#include "mailbox.h"
#include "os.h"
#include "flags.h"
#include "io.h"
#include <vector>
#include <memory>
#include <thread>
#include <unordered_map>
#include <mutex>
namespace Net {
namespace Tcp {
class Peer;
class Message;
class Handler;
enum class Options : uint64_t {
None = 0,
NoDelay = 1,
Linger = NoDelay << 1,
FastOpen = Linger << 1,
QuickAck = FastOpen << 1,
ReuseAddr = QuickAck << 1,
ReverseLookup = ReuseAddr << 1,
InstallSignalHandler = ReverseLookup << 1
};
DECLARE_FLAGS_OPERATORS(Options)
void setSocketOptions(Fd fd, Flags<Options> options);
class IoWorker {
public:
PollableMailbox<Message> mailbox;
IoWorker();
~IoWorker();
void start(const std::shared_ptr<Handler> &handler, Flags<Options> options);
void handleNewPeer(const std::shared_ptr<Peer>& peer);
void pin(const CpuSet& set);
private:
Polling::Epoll poller;
std::unique_ptr<std::thread> thread;
mutable std::mutex peersMutex;
std::unordered_map<Fd, std::shared_ptr<Peer>> peers;
std::shared_ptr<Handler> handler_;
Flags<Options> options_;
CpuSet pins;
std::shared_ptr<Peer>& getPeer(Fd fd);
std::shared_ptr<Peer>& getPeer(Polling::Tag tag);
void handleIncoming(const std::shared_ptr<Peer>& peer);
void handleMailbox();
void run();
};
class Listener {
public:
friend class IoWorker;
friend class Peer;
Listener();
Listener(const Address& address);
......@@ -106,20 +56,6 @@ private:
void dispatchPeer(const std::shared_ptr<Peer>& peer);
};
class Handler {
public:
Handler();
~Handler();
virtual void onInput(const char *buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer) = 0;
virtual void onOutput() = 0;
virtual void onConnection(const std::shared_ptr<Tcp::Peer>& peer);
virtual void onDisconnection(const std::shared_ptr<Tcp::Peer>& peer);
};
} // namespace Tcp
} // namespace Net
......@@ -208,12 +208,17 @@ public:
return *constData();
}
T& unsafeGet() const {
return *data();
}
~Optional() {
if (!isEmpty()) {
data()->~T();
}
}
private:
T *const constData() const {
return const_cast<T *const>(reinterpret_cast<const T *const>(bytes));
......@@ -293,8 +298,22 @@ namespace details {
void static_checks() {
do_static_checks<T, Func>(std::is_bind_expression<Func>());
}
}
template<typename Func>
struct IsArgMovable : public IsArgMovable<decltype(&Func::operator())> { };
template<typename R, typename Class, typename Arg>
struct IsArgMovable<R (Class::*)(Arg) const> : public std::is_rvalue_reference<Arg> { };
template<typename Func, typename Arg>
typename std::conditional<
IsArgMovable<Func>::value,
Arg&&,
const Arg&
>::type tryMove(Arg& arg) {
return std::move(arg);
}
}
template<typename T, typename Func>
const Optional<T>&
......@@ -303,7 +322,7 @@ optionally_do(const Optional<T> &option, Func func) {
static_assert(std::is_same<typename types::callable_trait<Func>::ReturnType, void>::value,
"Use optionally_map if you want to return a value");
if (!option.isEmpty()) {
func(option.get());
func(details::tryMove<Func>(option.unsafeGet()));
}
return option;
......@@ -317,7 +336,7 @@ optionally_map(const Optional<T> &option, Func func)
{
details::static_checks<T, Func>();
if (!option.isEmpty()) {
return Some(func(option.get()));
return Some(func(details::tryMove<Func>(option.unsafeGet())));
}
return None();
......@@ -330,7 +349,7 @@ optionally_fmap(const Optional<T> &option, Func func)
{
details::static_checks<T, Func>();
if (!option.isEmpty()) {
const auto &ret = func(option.get());
const auto &ret = func(details::tryMove<Func>(option.unsafeGet()));
if (!ret.isEmpty()) {
return Some(ret.get());
}
......
......@@ -4,6 +4,8 @@
*/
#include "peer.h"
#include "async.h"
#include "io.h"
#include <iostream>
#include <stdexcept>
#include <sys/socket.h>
......@@ -75,9 +77,9 @@ Peer::tryGetData(std::string(name)) const {
return it->second;
}
ssize_t
Async::Promise<ssize_t>
Peer::send(const void* buf, size_t len) {
return ::send(fd_, buf, len, 0);
return io->asyncWrite(fd_, buf, len);
}
......
......@@ -8,6 +8,7 @@
#include "net.h"
#include "os.h"
#include "async.h"
#include <string>
#include <iostream>
#include <memory>
......@@ -17,8 +18,12 @@ namespace Net {
namespace Tcp {
class IoWorker;
class Peer {
public:
friend class IoWorker;
Peer();
Peer(const Address& addr);
......@@ -45,9 +50,10 @@ public:
return std::static_pointer_cast<T>(data);
}
ssize_t send(const void* buf, size_t len);
Async::Promise<ssize_t> send(const void* buf, size_t len);
private:
IoWorker* io;
Address addr;
std::string hostname_;
......
/* tcp.cc
Mathieu Stefani, 05 novembre 2015
TCP
*/
#include "tcp.h"
#include "peer.h"
namespace Net {
namespace Tcp {
Handler::Handler()
{ }
Handler::~Handler()
{ }
void
Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer) {
}
void
Handler::onDisconnection(const std::shared_ptr<Tcp::Peer>& peer) {
}
} // namespace Tcp
} // namespace Net
/* tcp.h
Mathieu Stefani, 05 novembre 2015
TCP
*/
#pragma once
#include "flags.h"
#include <memory>
namespace Net {
namespace Tcp {
class Peer;
enum class Options : uint64_t {
None = 0,
NoDelay = 1,
Linger = NoDelay << 1,
FastOpen = Linger << 1,
QuickAck = FastOpen << 1,
ReuseAddr = QuickAck << 1,
ReverseLookup = ReuseAddr << 1,
InstallSignalHandler = ReverseLookup << 1
};
DECLARE_FLAGS_OPERATORS(Options)
class Handler {
public:
Handler();
~Handler();
virtual void onInput(const char *buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer) = 0;
virtual void onConnection(const std::shared_ptr<Tcp::Peer>& peer);
virtual void onDisconnection(const std::shared_ptr<Tcp::Peer>& peer);
};
} // namespace Tcp
} // namespace Net
......@@ -5,3 +5,7 @@ add_test( mime_test run_mime_test )
add_executable( run_headers_test headers_test.cc )
target_link_libraries(run_headers_test gtest gtest_main net)
add_test( headers_test run_headers_test )
add_executable( run_async_test async_test.cc )
target_link_libraries(run_async_test gtest gtest_main net)
add_test( async_test run_async_test )
#include "gtest/gtest.h"
#include "async.h"
#include <thread>
Async::Promise<int> doAsync(int N)
{
Async::Promise<int> promise(
[=](Async::Resolver& resolve, Async::Rejection& reject) {
std::thread thr([=]() mutable {
std::this_thread::sleep_for(std::chrono::seconds(1));
resolve(N * 2);
});
thr.detach();
});
return promise;
}
TEST(async_test, basic_test) {
Async::Promise<int> p1(
[](Async::Resolver& resolv, Async::Rejection& reject) {
resolv(10);
});
ASSERT_TRUE(p1.isFulfilled());
int val { 0 };
p1.then([&](int v) { val = v; }, Async::NoExcept);
ASSERT_EQ(val, 10);
Async::Promise<int> p2 = doAsync(10);
p2.then([](int result) { ASSERT_EQ(result, 20); },
Async::NoExcept);
std::this_thread::sleep_for(std::chrono::seconds(2));
Async::Promise<int> p3(
[](Async::Resolver& resolv, Async::Rejection& reject) {
reject(std::runtime_error("Because I decided"));
});
ASSERT_TRUE(p3.isRejected());
p3.then([](int) { ASSERT_TRUE(false); }, [](std::exception_ptr eptr) {
ASSERT_THROW(std::rethrow_exception(eptr), std::runtime_error);
});
}
TEST(async_test, chain_test) {
Async::Promise<int> p1(
[](Async::Resolver& resolve, Async::Rejection& reject) {
resolve(10);
});
p1
.then([](int result) { return result * 2; }, Async::NoExcept)
->chain()
.then([](int result) { std::cout << "Result = " << result << std::endl; },
Async::NoExcept);
Async::Promise<int> p2(
[](Async::Resolver& resolve, Async::Rejection& reject) {
resolve(10);
});
#if 0
p2
.then([](int result) { return result * 2.0; }, Async::IgnoreException)
->chain()
.then([](double result) { std::cout << "Result = " << result << std::endl; },
Async::IgnoreException);
#endif
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment