ircd/poll.cpp
#include "poll.hpp"
#include <unistd.h>
#include <assert.h>
#define TICKLE_INTERVAL (SOCK_TIMEOUT/3) // tickle via timeout every this seconds (for PINGs)
#define EVENTS_MAX 64
Poll* Poll::inst = NULL;
Poll::Poll(): fds(0), activity(NOW) {
assert(!inst);
efd = epoll_create(1);
assert(efd != -1);
}
Poll::~Poll() {
close(efd);
assert(inst == this);
inst = NULL;
if (fds) {
LOG("poll: still having active fds!");
}
assert((fds == 0) == timeouts.empty());
}
bool Poll::ctl(int op, int cfd, event_t events) {
events |= EVENT_CLOSE; // explicit default
struct epoll_event ev = {}; // make valgrind happy
ev.data.fd = cfd;
ev.events = events;
if (epoll_ctl(efd, op, cfd, &ev) == -1) {
LOG_ERRNO("EPOLL_CTL_(%d)", op);
return false;
}
return true;
}
bool Poll::add(int cfd, poll_handler_t handler, event_t events) {
assert(!handlers[cfd]);
if (ctl(EPOLL_CTL_ADD, cfd, events)) {
handlers[cfd] = handler;
timeouts.push(cfd, NOW);
++fds;
return true;
}
return false;
}
bool Poll::mod(int cfd, poll_handler_t handler, event_t events) {
assert(handlers[cfd]);
handlers[cfd] = handler;
return ctl(EPOLL_CTL_MOD, cfd, events);
}
bool Poll::del(int cfd) {
assert(handlers[cfd]);
if (ctl(EPOLL_CTL_DEL, cfd, EVENT_NONE)) {
handlers[cfd] = NULL;
timeouts.pop(cfd);
--fds;
return true;
}
return false;
}
bool Poll::wait(int tout_ms) {
struct epoll_event events[EVENTS_MAX];
int n = epoll_wait(efd, events, EVENTS_MAX, tout_ms);
if (n == -1) {
if (errno == EINTR) {
return true;
} else {
LOG_ERRNO("epoll_wait()");
return false;
}
}
update_now();
if (n > 0) {
activity = NOW;
}
for (int i=0; i<n; i++) {
struct epoll_event* event = &events[i];
int fd = event->data.fd;
// reset fd (including handler mask) as recently active
timeouts.pop(fd);
timeouts.push(fd, NOW);
// call handler - this might delete the fd
handlers[fd](fd, event->events);
}
int fd;
while ((fd = timeouts.pop(NOW-TICKLE_INTERVAL)) != -1) {
timeouts.push(fd, NOW);
handlers[fd](fd, EVENT_TOUT);
}
return true;
}