/**
* Copyright (c) 2018 Cornell University.
*
* Author: Ted Yin <tederminant@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _SALTICIDAE_EVENT_H
#define _SALTICIDAE_EVENT_H
#ifdef __cplusplus
#include <condition_variable>
#include <unistd.h>
#include <uv.h>
#include "salticidae/type.h"
#include "salticidae/queue.h"
#include "salticidae/util.h"
#include "salticidae/ref.h"
namespace salticidae {
static void _on_uv_handle_close(uv_handle_t *h) { if (h) delete h; }
struct _event_context_deleter {
constexpr _event_context_deleter() = default;
static void _on_uv_walk(uv_handle_t *handle, void *) {
if (!uv_is_closing(handle))
uv_close(handle, _on_uv_handle_close);
}
void operator()(uv_loop_t *ptr) {
if (ptr != nullptr)
{
uv_walk(ptr, _on_uv_walk, nullptr);
uv_run(ptr, UV_RUN_DEFAULT);
if (uv_loop_close(ptr))
SALTICIDAE_LOG_WARN("failed to close libuv loop");
delete ptr;
}
}
};
using _event_context_ot = ArcObj<uv_loop_t, _event_context_deleter>;
class EventContext: public _event_context_ot {
public:
EventContext(): _event_context_ot(new uv_loop_t()) {
if (uv_loop_init(get()) < 0)
{
delete obj;
obj = nullptr;
throw SalticidaeError(SALTI_ERROR_LIBUV_INIT);
}
}
EventContext(uv_loop_t *eb): _event_context_ot(eb) {}
EventContext(const EventContext &) = default;
EventContext(EventContext &&) = default;
EventContext &operator=(const EventContext &) = default;
EventContext &operator=(EventContext &&) = default;
void dispatch() const {
// TODO: improve this loop
uv_run(get(), UV_RUN_DEFAULT);
}
void stop() const { uv_stop(get()); }
};
class FdEvent {
public:
using callback_t = std::function<void(int fd, int events)>;
static const int READ = UV_READABLE;
static const int WRITE = UV_WRITABLE;
static const int ERROR = 1 << 30;
protected:
EventContext ec;
int fd;
uv_poll_t *ev_fd;
callback_t callback;
static inline void fd_then(uv_poll_t *h, int status, int events) {
if (status != 0)
events |= ERROR;
auto event = static_cast<FdEvent *>(h->data);
event->callback(event->fd, events);
}
public:
FdEvent(): ec(nullptr), ev_fd(nullptr) {}
FdEvent(const EventContext &ec, int fd, callback_t callback):
ec(ec), fd(fd), ev_fd(new uv_poll_t()),
callback(std::move(callback)) {
if (uv_poll_init(ec.get(), ev_fd, fd) < 0)
throw SalticidaeError(SALTI_ERROR_LIBUV_INIT);
ev_fd->data = this;
}
FdEvent(const FdEvent &) = delete;
FdEvent(FdEvent &&other):
ec(std::move(other.ec)), fd(other.fd), ev_fd(other.ev_fd),
callback(std::move(other.callback)) {
other.ev_fd = nullptr;
if (ev_fd != nullptr)
ev_fd->data = this;
}
void swap(FdEvent &other) {
std::swap(ec, other.ec);
std::swap(fd, other.fd);
std::swap(ev_fd, other.ev_fd);
std::swap(callback, other.callback);
if (ev_fd != nullptr)
ev_fd->data = this;
if (other.ev_fd != nullptr)
other.ev_fd->data = &other;
}
FdEvent &operator=(FdEvent &&other) {
if (this != &other)
{
FdEvent tmp(std::move(other));
tmp.swap(*this);
}
return *this;
}
~FdEvent() { clear(); }
void clear() {
if (ev_fd != nullptr)
{
uv_poll_stop(ev_fd);
uv_close((uv_handle_t *)ev_fd, _on_uv_handle_close);
ev_fd = nullptr;
}
callback = nullptr;
}
void set_callback(callback_t _callback) {
callback = _callback;
}
void add(int events) {
assert(ev_fd != nullptr);
if (uv_poll_start(ev_fd, events, FdEvent::fd_then)