aboutsummaryrefslogtreecommitdiff
path: root/seatd/poll/basic_poller.c
diff options
context:
space:
mode:
Diffstat (limited to 'seatd/poll/basic_poller.c')
-rw-r--r--seatd/poll/basic_poller.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/seatd/poll/basic_poller.c b/seatd/poll/basic_poller.c
new file mode 100644
index 0000000..a388d8e
--- /dev/null
+++ b/seatd/poll/basic_poller.c
@@ -0,0 +1,386 @@
+#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 "list.h"
+#include "poller.h"
+
+struct basic_poller *global_poller = NULL;
+
+const struct poll_impl basic_poller_impl;
+const struct event_source_fd_impl basic_poller_fd_impl;
+const struct event_source_signal_impl basic_poller_signal_impl;
+
+struct basic_poller {
+ struct poller base;
+ struct list signals;
+ struct list new_signals;
+ struct list fds;
+ struct list new_fds;
+
+ struct pollfd *pollfds;
+ size_t pollfds_len;
+ bool dirty;
+ bool inpoll;
+};
+
+struct basic_poller_fd {
+ struct event_source_fd base;
+ struct basic_poller *poller;
+ bool killed;
+};
+
+struct basic_poller_signal {
+ struct event_source_signal base;
+ struct basic_poller *poller;
+ bool raised;
+ bool killed;
+};
+
+static struct basic_poller *basic_poller_from_poller(struct poller *base) {
+ assert(base->impl == &basic_poller_impl);
+ return (struct basic_poller *)base;
+}
+
+static struct poller *basic_poller_create(void) {
+ if (global_poller != NULL) {
+ errno = EEXIST;
+ return NULL;
+ }
+
+ struct basic_poller *poller = calloc(1, sizeof(struct basic_poller));
+ if (poller == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ list_init(&poller->fds);
+ list_init(&poller->new_fds);
+ list_init(&poller->signals);
+ list_init(&poller->new_signals);
+ poller->base.impl = &basic_poller_impl;
+ global_poller = poller;
+ return (struct poller *)poller;
+}
+
+static int destroy(struct poller *base) {
+ struct basic_poller *poller = basic_poller_from_poller(base);
+ for (size_t idx = 0; idx < poller->fds.length; idx++) {
+ struct basic_poller_fd *bpfd = poller->fds.items[idx];
+ free(bpfd);
+ }
+ list_free(&poller->fds);
+ for (size_t idx = 0; idx < poller->new_fds.length; idx++) {
+ struct basic_poller_fd *bpfd = poller->new_fds.items[idx];
+ free(bpfd);
+ }
+ list_free(&poller->new_fds);
+ for (size_t idx = 0; idx < poller->signals.length; idx++) {
+ struct basic_poller_signal *bps = poller->signals.items[idx];
+
+ struct sigaction sa;
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(bps->base.signal, &sa, NULL);
+
+ free(bps);
+ }
+ list_free(&poller->signals);
+ for (size_t idx = 0; idx < poller->new_signals.length; idx++) {
+ struct basic_poller_signal *bps = poller->new_signals.items[idx];
+
+ struct sigaction sa;
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(bps->base.signal, &sa, NULL);
+
+ free(bps);
+ }
+ list_free(&poller->new_signals);
+ 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 basic_poller *poller) {
+ if (poller->pollfds_len != poller->fds.length) {
+ struct pollfd *fds = calloc(poller->fds.length, sizeof(struct pollfd));
+ if (fds == NULL) {
+ return -1;
+ }
+ free(poller->pollfds);
+ poller->pollfds = fds;
+ poller->pollfds_len = poller->fds.length;
+ }
+
+ for (size_t idx = 0; idx < poller->fds.length; idx++) {
+ struct basic_poller_fd *bpfd = poller->fds.items[idx];
+ poller->pollfds[idx] = (struct pollfd){
+ .fd = bpfd->base.fd,
+ .events = event_mask_to_poll_mask(bpfd->base.mask),
+ };
+ }
+
+ return 0;
+}
+
+static struct event_source_fd *add_fd(struct poller *base, int fd, uint32_t mask,
+ event_source_fd_func_t func, void *data) {
+ struct basic_poller *poller = basic_poller_from_poller(base);
+
+ struct basic_poller_fd *bpfd = calloc(1, sizeof(struct basic_poller_fd));
+ if (bpfd == NULL) {
+ return NULL;
+ }
+ bpfd->base.impl = &basic_poller_fd_impl;
+ bpfd->base.fd = fd;
+ bpfd->base.mask = mask;
+ bpfd->base.data = data;
+ bpfd->base.func = func;
+ bpfd->poller = poller;
+ poller->dirty = true;
+ if (poller->inpoll) {
+ list_add(&poller->new_fds, bpfd);
+ } else {
+ list_add(&poller->fds, bpfd);
+ regenerate_pollfds(poller);
+ }
+ return (struct event_source_fd *)bpfd;
+}
+
+static int fd_destroy(struct event_source_fd *event_source) {
+ struct basic_poller_fd *bpfd = (struct basic_poller_fd *)event_source;
+ struct basic_poller *poller = bpfd->poller;
+ int idx = list_find(&poller->fds, event_source);
+ if (idx == -1) {
+ return -1;
+ }
+ poller->dirty = true;
+ if (poller->inpoll) {
+ bpfd->killed = true;
+ } else {
+ list_del(&poller->fds, idx);
+ free(bpfd);
+ regenerate_pollfds(poller);
+ }
+ return 0;
+}
+
+static int fd_update(struct event_source_fd *event_source, uint32_t mask) {
+ struct basic_poller_fd *bpfd = (struct basic_poller_fd *)event_source;
+ struct basic_poller *poller = bpfd->poller;
+ event_source->mask = mask;
+
+ poller->dirty = true;
+ if (!poller->inpoll) {
+ regenerate_pollfds(poller);
+ }
+ return 0;
+}
+
+static void signal_handler(int sig) {
+ if (global_poller == NULL) {
+ return;
+ }
+
+ for (size_t idx = 0; idx < global_poller->signals.length; idx++) {
+ struct basic_poller_signal *bps = global_poller->signals.items[idx];
+ if (bps->base.signal == sig) {
+ bps->raised = true;
+ }
+ }
+}
+
+static struct event_source_signal *add_signal(struct poller *base, int signal,
+ event_source_signal_func_t func, void *data) {
+ struct basic_poller *poller = basic_poller_from_poller(base);
+
+ struct basic_poller_signal *bps = calloc(1, sizeof(struct basic_poller_signal));
+ if (bps == NULL) {
+ return NULL;
+ }
+
+ int refcnt = 0;
+ for (size_t idx = 0; idx < poller->signals.length; idx++) {
+ struct basic_poller_signal *bps = poller->signals.items[idx];
+ if (bps->base.signal == signal) {
+ refcnt++;
+ }
+ }
+
+ bps->base.impl = &basic_poller_signal_impl;
+ bps->base.signal = signal;
+ bps->base.data = data;
+ bps->base.func = func;
+ bps->poller = poller;
+
+ if (refcnt == 0) {
+ struct sigaction sa;
+ sa.sa_handler = &signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(signal, &sa, NULL);
+ }
+
+ if (poller->inpoll) {
+ list_add(&poller->new_signals, bps);
+ } else {
+ list_add(&poller->signals, bps);
+ }
+
+ return (struct event_source_signal *)bps;
+}
+
+static int signal_destroy(struct event_source_signal *event_source) {
+ struct basic_poller_signal *bps = (struct basic_poller_signal *)event_source;
+ struct basic_poller *poller = bps->poller;
+
+ int idx = list_find(&poller->signals, event_source);
+ if (idx == -1) {
+ return -1;
+ }
+
+ int refcnt = 0;
+ for (size_t idx = 0; idx < poller->signals.length; idx++) {
+ struct basic_poller_signal *b = poller->signals.items[idx];
+ if (b->base.signal == bps->base.signal) {
+ refcnt++;
+ }
+ }
+
+ if (refcnt == 0) {
+ struct sigaction sa;
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(bps->base.signal, &sa, NULL);
+ }
+
+ if (poller->inpoll) {
+ bps->killed = true;
+ } else {
+ list_del(&poller->signals, idx);
+ free(bps);
+ }
+ return 0;
+}
+
+static int basic_poller_poll(struct poller *base) {
+ struct basic_poller *poller = basic_poller_from_poller(base);
+
+ if (poll(poller->pollfds, poller->fds.length, -1) == -1 && errno != EINTR) {
+ return -1;
+ }
+
+ poller->inpoll = true;
+
+ for (size_t idx = 0; idx < poller->fds.length; idx++) {
+ short revents = poller->pollfds[idx].revents;
+ if (revents == 0) {
+ continue;
+ }
+ struct basic_poller_fd *bpfd = poller->fds.items[idx];
+ bpfd->base.func(poller->pollfds[idx].fd, poll_mask_to_event_mask(revents),
+ bpfd->base.data);
+ }
+
+ for (size_t idx = 0; idx < poller->signals.length; idx++) {
+ struct basic_poller_signal *bps = poller->signals.items[idx];
+ if (!bps->raised) {
+ continue;
+ }
+ bps->base.func(bps->base.signal, bps->base.data);
+ bps->raised = false;
+ }
+
+ poller->inpoll = false;
+
+ for (size_t idx = 0; idx < poller->fds.length; idx++) {
+ struct basic_poller_fd *bpfd = poller->fds.items[idx];
+ if (!bpfd->killed) {
+ continue;
+ }
+
+ list_del(&poller->fds, idx);
+ free(bpfd);
+ idx--;
+ }
+
+ for (size_t idx = 0; idx < poller->signals.length; idx++) {
+ struct basic_poller_signal *bps = poller->signals.items[idx];
+ if (!bps->killed) {
+ continue;
+ }
+
+ list_del(&poller->signals, idx);
+ free(bps);
+ idx--;
+ }
+
+ if (poller->new_fds.length > 0) {
+ list_concat(&poller->fds, &poller->new_fds);
+ list_truncate(&poller->new_fds);
+ }
+
+ if (poller->new_signals.length > 0) {
+ list_concat(&poller->signals, &poller->new_signals);
+ list_truncate(&poller->new_signals);
+ }
+
+ if (poller->dirty) {
+ regenerate_pollfds(poller);
+ poller->dirty = false;
+ }
+
+ return 0;
+}
+
+const struct event_source_fd_impl basic_poller_fd_impl = {
+ .update = fd_update,
+ .destroy = fd_destroy,
+};
+
+const struct event_source_signal_impl basic_poller_signal_impl = {
+ .destroy = signal_destroy,
+};
+
+const struct poll_impl basic_poller_impl = {
+ .create = basic_poller_create,
+ .destroy = destroy,
+ .add_fd = add_fd,
+ .add_signal = add_signal,
+ .poll = basic_poller_poll,
+};