You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.6 KiB
95 lines
2.6 KiB
// Copyright (c) Vitaliy Filippov, 2019+
|
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
|
|
|
#include <sys/epoll.h>
|
|
#include <sys/poll.h>
|
|
#include <unistd.h>
|
|
#include <stdexcept>
|
|
|
|
#include "epoll_manager.h"
|
|
|
|
#define MAX_EPOLL_EVENTS 64
|
|
|
|
epoll_manager_t::epoll_manager_t(ring_loop_t *ringloop)
|
|
{
|
|
this->ringloop = ringloop;
|
|
|
|
epoll_fd = epoll_create(1);
|
|
if (epoll_fd < 0)
|
|
{
|
|
throw std::runtime_error(std::string("epoll_create: ") + strerror(errno));
|
|
}
|
|
|
|
tfd = new timerfd_manager_t([this](int fd, bool wr, std::function<void(int, int)> handler) { set_fd_handler(fd, wr, handler); });
|
|
|
|
handle_epoll_events();
|
|
}
|
|
|
|
epoll_manager_t::~epoll_manager_t()
|
|
{
|
|
if (tfd)
|
|
{
|
|
delete tfd;
|
|
tfd = NULL;
|
|
}
|
|
close(epoll_fd);
|
|
}
|
|
|
|
void epoll_manager_t::set_fd_handler(int fd, bool wr, std::function<void(int, int)> handler)
|
|
{
|
|
if (handler != NULL)
|
|
{
|
|
bool exists = epoll_handlers.find(fd) != epoll_handlers.end();
|
|
epoll_event ev;
|
|
ev.data.fd = fd;
|
|
ev.events = (wr ? EPOLLOUT : 0) | EPOLLIN | EPOLLRDHUP | EPOLLET;
|
|
if (epoll_ctl(epoll_fd, exists ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev) < 0)
|
|
{
|
|
throw std::runtime_error(std::string("epoll_ctl: ") + strerror(errno));
|
|
}
|
|
epoll_handlers[fd] = handler;
|
|
}
|
|
else
|
|
{
|
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0 && errno != ENOENT)
|
|
{
|
|
throw std::runtime_error(std::string("epoll_ctl: ") + strerror(errno));
|
|
}
|
|
epoll_handlers.erase(fd);
|
|
}
|
|
}
|
|
|
|
void epoll_manager_t::handle_epoll_events()
|
|
{
|
|
io_uring_sqe *sqe = ringloop->get_sqe();
|
|
if (!sqe)
|
|
{
|
|
throw std::runtime_error("can't get SQE, will fall out of sync with EPOLLET");
|
|
}
|
|
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
|
my_uring_prep_poll_add(sqe, epoll_fd, POLLIN);
|
|
data->callback = [this](ring_data_t *data)
|
|
{
|
|
if (data->res < 0)
|
|
{
|
|
throw std::runtime_error(std::string("epoll failed: ") + strerror(-data->res));
|
|
}
|
|
handle_epoll_events();
|
|
};
|
|
ringloop->submit();
|
|
int nfds;
|
|
epoll_event events[MAX_EPOLL_EVENTS];
|
|
do
|
|
{
|
|
nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, 0);
|
|
for (int i = 0; i < nfds; i++)
|
|
{
|
|
auto cb_it = epoll_handlers.find(events[i].data.fd);
|
|
if (cb_it != epoll_handlers.end())
|
|
{
|
|
auto & cb = cb_it->second;
|
|
cb(events[i].data.fd, events[i].events);
|
|
}
|
|
}
|
|
} while (nfds == MAX_EPOLL_EVENTS);
|
|
}
|
|
|