commit - 57ba4f1bd9f8bbbff1e3c9fd7a0c145e39c42152
commit + f436aa54ddf3e9824f825a23aeb42cbc147d7fcc
blob - /dev/null
blob + 8ead398358280dd3bbcbccb9d677bf8fdad17a96 (mode 644)
--- /dev/null
+++ ev.c
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ev.h"
+
+struct evcb {
+ void (*cb)(int, int, void *);
+ void *udata;
+};
+
+struct evtimer {
+ unsigned int id;
+ struct timeval tv;
+ struct evcb cb;
+};
+
+struct evbase {
+ size_t len;
+
+ struct pollfd *pfds;
+ size_t pfdlen;
+
+ struct evcb *cbs;
+ size_t cblen;
+
+ int sigpipe[2];
+ struct evcb sigcb;
+
+ unsigned int tid;
+ struct evtimer *timers;
+ size_t ntimers;
+ size_t timerscap;
+};
+
+static struct evbase *base;
+static int ev_stop;
+
+static int
+ev_resize(size_t len)
+{
+ void *t;
+ size_t i;
+
+ t = recallocarray(base->pfds, base->pfdlen, len, sizeof(*base->pfds));
+ if (t == NULL)
+ return -1;
+ base->pfds = t;
+ base->pfdlen = len;
+
+ for (i = base->len; i < len; ++i)
+ base->pfds[i].fd = -1;
+
+ t = recallocarray(base->cbs, base->cblen, len, sizeof(*base->cbs));
+ if (t == NULL)
+ return -1;
+ base->cbs = t;
+ base->cblen = len;
+
+ base->len = len;
+ return 0;
+}
+
+int
+ev_init(void)
+{
+ if (base != NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((base = calloc(1, sizeof(*base))) == NULL)
+ return -1;
+
+ base->sigpipe[0] = -1;
+ base->sigpipe[1] = -1;
+
+ if (ev_resize(16) == -1) {
+ free(base->pfds);
+ free(base->cbs);
+ free(base);
+ base = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ev_add(int fd, int ev, void (*cb)(int, int, void *), void *udata)
+{
+ if (fd < 0 || (size_t)fd >= base->len) {
+ if (ev_resize(fd + 1) == -1)
+ return -1;
+ }
+
+ base->pfds[fd].fd = fd;
+ base->pfds[fd].events = ev;
+
+ base->cbs[fd].cb = cb;
+ base->cbs[fd].udata = udata;
+
+ return 0;
+}
+
+static void
+ev_sigcatch(int signo)
+{
+ unsigned char s;
+ int err;
+
+ err = errno;
+
+ /*
+ * We should be able to write up to PIPE_BUF bytes without
+ * blocking.
+ */
+ s = signo;
+ (void) write(base->sigpipe[1], &s, sizeof(s));
+
+ errno = err;
+}
+
+static void
+ev_sigdispatch(int fd, int ev, void *data)
+{
+ unsigned char signo;
+
+ if (read(fd, &signo, sizeof(signo)) != sizeof(signo))
+ return;
+
+ base->sigcb.cb(signo, 0, base->sigcb.udata);
+}
+
+int
+ev_signal(int sig, void (*cb)(int, int, void *), void *udata)
+{
+ int flags;
+
+ if (base->sigpipe[0] == -1) {
+ /* pipe2(2) is not available everywhere... sigh */
+ if (pipe(base->sigpipe) == -1)
+ return -1;
+
+ if ((flags = fcntl(base->sigpipe[1], F_GETFL)) == -1 ||
+ fcntl(base->sigpipe[1], F_SETFL, flags | O_NONBLOCK) == -1)
+ return -1;
+
+ if (ev_add(base->sigpipe[0], POLLIN, ev_sigdispatch, NULL)
+ == -1)
+ return -1;
+ }
+
+ base->sigcb.cb = cb;
+ base->sigcb.udata = udata;
+
+ signal(sig, ev_sigcatch);
+ return 0;
+}
+
+static inline void
+bubbleup(size_t i)
+{
+ struct evtimer tmp;
+ size_t p;
+
+ for (;;) {
+ if (i == 0)
+ return;
+
+ p = (i - 1) / 2;
+ if (timercmp(&base->timers[p].tv, &base->timers[i].tv, <))
+ return;
+
+ /* swap */
+ memcpy(&tmp, &base->timers[p], sizeof(tmp));
+ memcpy(&base->timers[p], &base->timers[i], sizeof(tmp));
+ memcpy(&base->timers[i], &tmp, sizeof(tmp));
+ i = p;
+ }
+}
+
+unsigned int
+ev_timer(const struct timeval *tv, void (*cb)(int, int, void*), void *udata)
+{
+ struct evtimer *evt;
+ void *t;
+ size_t newcap;
+ unsigned int nextid;
+
+ if (tv == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (base->ntimers == base->timerscap) {
+ newcap = base->timerscap + 8;
+ t = recallocarray(base->timers, base->timerscap, newcap,
+ sizeof(*base->timers));
+ if (t == NULL)
+ return 0;
+ base->timers = t;
+ base->timerscap = newcap;
+ }
+
+ if ((nextid = ++base->tid) == 0)
+ nextid = ++base->tid;
+
+ evt = &base->timers[base->ntimers];
+ evt->id = nextid;
+ memcpy(&evt->tv, tv, sizeof(*tv));
+ evt->cb.cb = cb;
+ evt->cb.udata = udata;
+
+ bubbleup(base->ntimers);
+ base->ntimers++;
+
+ return (nextid);
+}
+
+int
+ev_timer_pending(unsigned int id)
+{
+ size_t i;
+
+ for (i = 0; i < base->ntimers; ++i) {
+ if (base->timers[i].id == id)
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+bubbledown(size_t i)
+{
+ struct timeval tmp;
+ size_t l, r, s;
+
+ for (;;) {
+ l = 2 * i + 1;
+ r = 2 * i + 2;
+
+ /* base case: there are no children */
+ if (l > base->ntimers)
+ return;
+
+ /* find the smaller child */
+ s = r;
+ if (r > base->ntimers ||
+ timercmp(&base->timers[l].tv, &base->timers[r].tv, <))
+ s = l;
+
+ /* other base case: it's at the right place */
+ if (timercmp(&base->timers[i].tv, &base->timers[s].tv, <))
+ return;
+
+ /* swap */
+ memcpy(&tmp, &base->timers[s], sizeof(tmp));
+ memcpy(&base->timers[s], &base->timers[i], sizeof(tmp));
+ memcpy(&base->timers[i], &tmp, sizeof(tmp));
+
+ i = s;
+ }
+}
+
+static inline void
+cancel_timer(size_t i)
+{
+ /* special case: it's the last one */
+ if (i == base->ntimers - 1) {
+ base->ntimers--;
+ memset(&base->timers[base->ntimers], 0, sizeof(*base->timers));
+ return;
+ }
+
+ memcpy(&base->timers[i], &base->timers[base->ntimers - 1],
+ sizeof(*base->timers));
+ base->ntimers--;
+
+ bubbledown(i);
+}
+
+int
+ev_timer_cancel(unsigned int id)
+{
+ size_t i;
+
+ for (i = 0; i < base->ntimers; ++i) {
+ if (base->timers[i].id == id)
+ break;
+ }
+
+ if (i == base->ntimers)
+ return -1;
+
+ cancel_timer(i);
+ return (0);
+}
+
+int
+ev_del(int fd)
+{
+ if (fd < 0 || (size_t)fd >= base->len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ base->pfds[fd].fd = -1;
+ base->pfds[fd].events = 0;
+
+ base->cbs[fd].cb = NULL;
+ base->cbs[fd].udata = NULL;
+
+ return 0;
+}
+
+int
+ev_loop(void)
+{
+ struct timespec elapsed, beg, end, min, *wait;
+ struct timeval tv, sub;
+ int n;
+ size_t i;
+
+ while (!ev_stop) {
+ wait = NULL;
+ if (base->ntimers) {
+ TIMEVAL_TO_TIMESPEC(&base->timers[0].tv, &min);
+ wait = &min;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &beg);
+ if ((n = ppoll(base->pfds, base->len, wait, NULL)) == -1) {
+ if (errno != EINTR)
+ return -1;
+ }
+
+ if (n == 0)
+ memcpy(&elapsed, &min, sizeof(min));
+ else {
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ timespecsub(&end, &beg, &elapsed);
+ }
+
+ TIMESPEC_TO_TIMEVAL(&tv, &elapsed);
+
+ for (i = 0; i < base->ntimers && !ev_stop; /* nop */) {
+ timersub(&base->timers[i].tv, &tv, &sub);
+ if (sub.tv_sec <= 0) {
+ base->timers[i].cb.cb(-1, POLLHUP,
+ base->timers[i].cb.udata);
+ cancel_timer(i);
+ continue;
+ }
+
+ memcpy(&base->timers[i].tv, &sub, sizeof(sub));
+ i++;
+ }
+
+ for (i = 0; i < base->len && n > 0 && !ev_stop; ++i) {
+ if (base->pfds[i].fd == -1)
+ continue;
+ if (base->pfds[i].revents & (POLLIN|POLLOUT|POLLHUP)) {
+ n--;
+ base->cbs[i].cb(base->pfds[i].fd,
+ base->pfds[i].revents,
+ base->cbs[i].udata);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+ev_break(void)
+{
+ ev_stop = 1;
+}
blob - /dev/null
blob + 9697eeb7c227c45cc70b4228a7d91eecc015aa91 (mode 644)
--- /dev/null
+++ ev.h
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+struct timeval;
+
+int ev_init(void);
+int ev_add(int, int, void(*)(int, int, void *), void *);
+int ev_signal(int, void(*)(int, int, void * ), void *);
+unsigned int ev_timer(const struct timeval *, void(*)(int, int, void *),
+ void *);
+int ev_timer_pending(unsigned int);
+int ev_timer_cancel(unsigned int);
+int ev_del(int);
+int ev_loop(void);
+void ev_break(void);