285 lines
6.9 KiB
C
285 lines
6.9 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "linked_list.h"
|
|
#include "poller.h"
|
|
|
|
struct event_source_fd {
|
|
struct linked_list link; // poller::fds
|
|
const struct event_source_fd_impl *impl;
|
|
event_source_fd_func_t func;
|
|
|
|
int fd;
|
|
uint32_t mask;
|
|
void *data;
|
|
|
|
struct poller *poller;
|
|
bool killed;
|
|
ssize_t pollfd_idx;
|
|
};
|
|
|
|
struct event_source_signal {
|
|
struct linked_list link; // poller::signals
|
|
const struct event_source_signal_impl *impl;
|
|
event_source_signal_func_t func;
|
|
|
|
int signal;
|
|
void *data;
|
|
|
|
struct poller *poller;
|
|
bool raised;
|
|
bool killed;
|
|
};
|
|
|
|
/* Used for signal handling */
|
|
struct poller *global_poller = NULL;
|
|
|
|
void poller_init(struct poller *poller) {
|
|
assert(global_poller == NULL);
|
|
|
|
linked_list_init(&poller->fds);
|
|
linked_list_init(&poller->signals);
|
|
poller->pollfds = NULL;
|
|
poller->pollfds_len = 0;
|
|
poller->fd_event_sources = 0;
|
|
global_poller = poller;
|
|
}
|
|
|
|
int poller_finish(struct poller *poller) {
|
|
while (!linked_list_empty(&poller->fds)) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)poller->fds.next;
|
|
free(bpfd);
|
|
}
|
|
while (!linked_list_empty(&poller->signals)) {
|
|
struct event_source_signal *bps = (struct event_source_signal *)poller->signals.next;
|
|
|
|
struct sigaction sa;
|
|
sa.sa_handler = SIG_DFL;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sigaction(bps->signal, &sa, NULL);
|
|
|
|
free(bps);
|
|
}
|
|
free(poller->pollfds);
|
|
return 0;
|
|
}
|
|
|
|
static int event_mask_to_poll_mask(uint32_t event_mask) {
|
|
int poll_mask = 0;
|
|
if (event_mask & EVENT_READABLE) {
|
|
poll_mask |= POLLIN;
|
|
}
|
|
if (event_mask & EVENT_WRITABLE) {
|
|
poll_mask |= POLLOUT;
|
|
}
|
|
return poll_mask;
|
|
}
|
|
|
|
static uint32_t poll_mask_to_event_mask(int poll_mask) {
|
|
uint32_t event_mask = 0;
|
|
if (poll_mask & POLLIN) {
|
|
event_mask |= EVENT_READABLE;
|
|
}
|
|
if (poll_mask & POLLOUT) {
|
|
event_mask |= EVENT_WRITABLE;
|
|
}
|
|
if (poll_mask & POLLERR) {
|
|
event_mask |= EVENT_ERROR;
|
|
}
|
|
if (poll_mask & POLLHUP) {
|
|
event_mask |= EVENT_HANGUP;
|
|
}
|
|
return event_mask;
|
|
}
|
|
|
|
static int regenerate_pollfds(struct poller *poller) {
|
|
if (!poller->pollfds_dirty) {
|
|
return 0;
|
|
}
|
|
|
|
if (poller->fd_event_sources > poller->pollfds_len) {
|
|
struct pollfd *fds = calloc(poller->fd_event_sources, sizeof(struct pollfd));
|
|
if (fds == NULL) {
|
|
return -1;
|
|
}
|
|
free(poller->pollfds);
|
|
poller->pollfds = fds;
|
|
poller->pollfds_len = poller->fd_event_sources;
|
|
}
|
|
|
|
ssize_t idx = 0;
|
|
for (struct linked_list *elem = poller->fds.next; elem != &poller->fds; elem = elem->next) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)elem;
|
|
bpfd->pollfd_idx = idx++;
|
|
poller->pollfds[bpfd->pollfd_idx] = (struct pollfd){
|
|
.fd = bpfd->fd,
|
|
.events = event_mask_to_poll_mask(bpfd->mask),
|
|
};
|
|
}
|
|
|
|
poller->pollfds_dirty = false;
|
|
return 0;
|
|
}
|
|
|
|
struct event_source_fd *poller_add_fd(struct poller *poller, int fd, uint32_t mask,
|
|
event_source_fd_func_t func, void *data) {
|
|
struct event_source_fd *bpfd = calloc(1, sizeof(struct event_source_fd));
|
|
if (bpfd == NULL) {
|
|
return NULL;
|
|
}
|
|
bpfd->fd = fd;
|
|
bpfd->mask = mask;
|
|
bpfd->data = data;
|
|
bpfd->func = func;
|
|
bpfd->poller = poller;
|
|
bpfd->pollfd_idx = -1;
|
|
poller->fd_event_sources += 1;
|
|
poller->pollfds_dirty = true;
|
|
linked_list_insert(&poller->fds, &bpfd->link);
|
|
return (struct event_source_fd *)bpfd;
|
|
}
|
|
|
|
int event_source_fd_destroy(struct event_source_fd *event_source) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
|
struct poller *poller = bpfd->poller;
|
|
poller->fd_event_sources -= 1;
|
|
poller->pollfds_dirty = true;
|
|
bpfd->killed = true;
|
|
return 0;
|
|
}
|
|
|
|
int event_source_fd_update(struct event_source_fd *event_source, uint32_t mask) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)event_source;
|
|
struct poller *poller = bpfd->poller;
|
|
event_source->mask = mask;
|
|
poller->pollfds_dirty = true;
|
|
return 0;
|
|
}
|
|
|
|
static void signal_handler(int sig) {
|
|
if (global_poller == NULL) {
|
|
return;
|
|
}
|
|
for (struct linked_list *elem = global_poller->signals.next;
|
|
elem != &global_poller->signals; elem = elem->next) {
|
|
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
|
if (bps->signal == sig) {
|
|
bps->raised = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct event_source_signal *poller_add_signal(struct poller *poller, int signal,
|
|
event_source_signal_func_t func, void *data) {
|
|
|
|
struct event_source_signal *bps = calloc(1, sizeof(struct event_source_signal));
|
|
if (bps == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
bps->signal = signal;
|
|
bps->data = data;
|
|
bps->func = func;
|
|
bps->poller = poller;
|
|
|
|
struct sigaction sa;
|
|
sa.sa_handler = &signal_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sigaction(signal, &sa, NULL);
|
|
|
|
linked_list_insert(&poller->signals, &bps->link);
|
|
|
|
return (struct event_source_signal *)bps;
|
|
}
|
|
|
|
int event_source_signal_destroy(struct event_source_signal *event_source) {
|
|
struct event_source_signal *bps = (struct event_source_signal *)event_source;
|
|
struct poller *poller = bps->poller;
|
|
|
|
int refcnt = 0;
|
|
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
|
elem = elem->next) {
|
|
struct event_source_signal *b = (struct event_source_signal *)elem;
|
|
if (b->signal == bps->signal && !b->killed) {
|
|
refcnt++;
|
|
}
|
|
}
|
|
|
|
if (refcnt == 0) {
|
|
struct sigaction sa;
|
|
sa.sa_handler = SIG_DFL;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sigaction(bps->signal, &sa, NULL);
|
|
}
|
|
|
|
bps->killed = true;
|
|
return 0;
|
|
}
|
|
|
|
int poller_poll(struct poller *poller) {
|
|
if (regenerate_pollfds(poller) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
if (poll(poller->pollfds, poller->fd_event_sources, -1) == -1 && errno != EINTR) {
|
|
return -1;
|
|
}
|
|
|
|
for (struct linked_list *elem = poller->fds.next; elem != &poller->fds; elem = elem->next) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)elem;
|
|
if (bpfd->pollfd_idx == -1 || bpfd->killed) {
|
|
continue;
|
|
}
|
|
short revents = poller->pollfds[bpfd->pollfd_idx].revents;
|
|
if (revents == 0) {
|
|
continue;
|
|
}
|
|
bpfd->func(poller->pollfds[bpfd->pollfd_idx].fd, poll_mask_to_event_mask(revents),
|
|
bpfd->data);
|
|
}
|
|
|
|
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
|
elem = elem->next) {
|
|
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
|
if (!bps->raised || bps->killed) {
|
|
continue;
|
|
}
|
|
bps->func(bps->signal, bps->data);
|
|
bps->raised = false;
|
|
}
|
|
|
|
for (struct linked_list *elem = poller->fds.next; elem != &poller->fds; elem = elem->next) {
|
|
struct event_source_fd *bpfd = (struct event_source_fd *)elem;
|
|
if (!bpfd->killed) {
|
|
continue;
|
|
}
|
|
|
|
elem = elem->prev;
|
|
linked_list_remove(&bpfd->link);
|
|
free(bpfd);
|
|
}
|
|
|
|
for (struct linked_list *elem = poller->signals.next; elem != &poller->signals;
|
|
elem = elem->next) {
|
|
struct event_source_signal *bps = (struct event_source_signal *)elem;
|
|
if (!bps->killed) {
|
|
continue;
|
|
}
|
|
|
|
elem = elem->prev;
|
|
linked_list_remove(&bps->link);
|
|
free(bps);
|
|
}
|
|
|
|
return 0;
|
|
}
|