blob: dc73e913f9b14e0ccb97a6d0a043d0d502c08080 [file] [log] [blame]
/*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "poller.h"
#include <assert.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include "util.h"
/* Based on https://github.com/elly/elib/blob/master/reactor.c */
struct poller {
int epoll_fd;
int num_polled;
};
struct polled {
struct poller *poller;
int fd;
void *priv;
void (*read)(struct polled *polled);
void (*write)(struct polled *polled);
void (*close)(struct polled *polled);
};
struct poller *poller_new(void)
{
int epoll_fd = epoll_create1(0);
if (epoll_fd < 0)
return NULL;
struct poller *poller = xmalloc(sizeof(*poller));
poller->epoll_fd = epoll_fd;
poller->num_polled = 0;
return poller;
}
void poller_free(struct poller *poller)
{
assert(poller);
assert(poller->num_polled == 0);
close(poller->epoll_fd);
xfree(poller);
}
int poller_fd(struct poller *poller)
{
assert(poller);
return poller->epoll_fd;
}
struct polled *poller_add(struct poller *poller, int fd)
{
assert(poller);
struct polled *polled = xmalloc(sizeof(*polled));
struct epoll_event event;
polled->poller = poller;
polled->fd = fd;
polled->priv = NULL;
polled->read = NULL;
polled->write = NULL;
polled->close = NULL;
event.events = 0;
event.data.ptr = polled;
if (epoll_ctl(poller->epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0) {
xfree(polled);
return NULL;
}
poller->num_polled++;
return polled;
}
static int poller_update(struct poller *poller, struct polled *polled)
{
assert(poller);
assert(polled);
assert(polled->poller == poller);
struct epoll_event event;
event.events = 0;
event.data.ptr = polled;
if (polled->read)
event.events |= EPOLLIN;
if (polled->write)
event.events |= EPOLLOUT;
if (polled->close)
event.events |= EPOLLRDHUP;
return epoll_ctl(poller->epoll_fd, EPOLL_CTL_MOD, polled->fd, &event);
}
static int poller_remove(struct poller *poller, struct polled *polled)
{
assert(poller);
assert(polled);
assert(polled->poller == poller);
int result = epoll_ctl(poller->epoll_fd, EPOLL_CTL_DEL, polled->fd, NULL);
if (result)
return result;
xfree(polled);
poller->num_polled--;
return 0;
}
int poller_poll(struct poller *poller)
{
struct epoll_event events[16];
struct polled *polled;
int n, i;
n = epoll_wait(poller->epoll_fd,
events, sizeof(events) / sizeof(events[0]),
0);
if (n < 0)
return n;
for (i = 0; i < n; i++) {
polled = events[i].data.ptr;
if (events[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) {
if (polled->close)
(polled->close)(polled);
poller_remove(poller, polled);
} else if ((events[i].events & EPOLLIN) && polled->read) {
(polled->read)(polled);
} else if ((events[i].events & EPOLLOUT) && polled->write) {
(polled->write)(polled);
}
}
return 0;
}
void *polled_priv(struct polled *polled)
{
assert(polled);
return polled->priv;
}
void polled_set_priv(struct polled *polled, void *priv)
{
assert(polled);
polled->priv = priv;
}
void polled_set_read(struct polled *polled,
void (*read)(struct polled *polled))
{
assert(polled);
polled->read = read;
}
void polled_set_write(struct polled *polled,
void (*write)(struct polled *polled))
{
assert(polled);
polled->write = write;
}
void polled_set_close(struct polled *polled,
void (*close)(struct polled *polled))
{
assert(polled);
polled->close = close;
}
int polled_update(struct polled *polled)
{
assert(polled);
return poller_update(polled->poller, polled);
}
int polled_delete(struct polled *polled)
{
assert(polled);
return poller_remove(polled->poller, polled);
}