]> granicus.if.org Git - libevent/commitdiff
Real time signal support from Taral <taral@taral.net>
authorNiels Provos <provos@gmail.com>
Thu, 25 Sep 2003 02:53:39 +0000 (02:53 +0000)
committerNiels Provos <provos@gmail.com>
Thu, 25 Sep 2003 02:53:39 +0000 (02:53 +0000)
svn:r75

rtsig.c [new file with mode: 0644]

diff --git a/rtsig.c b/rtsig.c
new file mode 100644 (file)
index 0000000..2674d40
--- /dev/null
+++ b/rtsig.c
@@ -0,0 +1,410 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Enable F_SETSIG and F_SETOWN */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#ifndef HAVE_WORKING_RTSIG
+#include <sys/stat.h>
+#endif
+#include <unistd.h>
+
+#define EVLIST_X_NORT  0x1000  /* Skip RT signals (internal) */
+
+#include "event.h"
+extern struct event_list eventqueue;
+extern struct event_list signalqueue;
+
+struct rtsigop {
+    sigset_t sigs;
+    struct pollfd *poll;
+    struct event **toev;
+    int cur, max, total;
+#ifndef HAVE_WORKING_RTSIG
+    int pollmode;
+#endif
+};
+
+#define INIT_MAX 16
+
+static int
+poll_add(struct rtsigop *op, struct event *ev)
+{
+    struct pollfd *pfd;
+
+    if (op->poll == NULL) return 0;
+
+    if (op->cur == op->max) {
+        void *p;
+
+        p = realloc(op->poll, sizeof(*op->poll) * (op->max << 1));
+        if (!p) {
+            errno = ENOMEM;
+            return -1;
+        }
+        op->poll = p;
+        p = realloc(op->toev, sizeof(*op->toev) * (op->max << 1));
+        if (!p) {
+            op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
+            errno = ENOMEM;
+            return -1;
+        }
+        op->toev = p;
+        op->max <<= 1;
+    }
+
+    pfd = &op->poll[op->cur];
+    pfd->fd = ev->ev_fd;
+    pfd->events = 0;
+    if (ev->ev_events & EV_READ) pfd->events |= POLLIN;
+    if (ev->ev_events & EV_WRITE) pfd->events |= POLLOUT;
+    pfd->revents = 0;
+
+    op->toev[op->cur] = ev;
+    op->cur++;
+
+    return 0;
+}
+
+static void
+poll_free(struct rtsigop *op, int n)
+{
+    if (op->poll == NULL) return;
+
+    op->cur--;
+    if (n < op->cur) {
+        memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll));
+        op->toev[n] = op->toev[op->cur];
+    }
+    if (op->max > INIT_MAX && op->cur < op->max >> 1) {
+        op->max >>= 1;
+        op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
+        op->toev = realloc(op->toev, sizeof(*op->toev) * op->max);
+    }
+}
+
+static void
+poll_remove(struct rtsigop *op, struct event *ev)
+{
+    int i;
+
+    for (i = 0; i < op->cur; i++) {
+        if (op->toev[i] == ev) {
+            poll_free(op, i);
+            break;
+        }
+    }
+}
+
+static void
+activate(struct event *ev, int flags)
+{
+    if (!(ev->ev_events & EV_PERSIST)) event_del(ev);
+    event_active(ev, flags, 1);
+}
+
+void *rtsig_init(void);
+int rtsig_add(void *, struct event *);
+int rtsig_del(void *, struct event *);
+int rtsig_recalc(void *, int);
+int rtsig_dispatch(void *, struct timeval *);
+
+struct eventop rtsigops = {
+    "rtsig",
+    rtsig_init,
+    rtsig_add,
+    rtsig_del,
+    rtsig_recalc,
+    rtsig_dispatch
+};
+
+void *rtsig_init(void)
+{
+    struct rtsigop *op;
+
+    if (getenv("EVENT_NORTSIG"))
+        return NULL;
+
+    op = malloc(sizeof(*op));
+    if (op == NULL) return NULL;
+
+    memset(op, 0, sizeof(*op));
+
+    op->max = INIT_MAX;
+    op->poll = malloc(sizeof(*op->poll) * op->max);
+    if (op->poll == NULL) {
+        free(op);
+        return NULL;
+    }
+    op->toev = malloc(sizeof(*op->toev) * op->max);
+    if (op->toev == NULL) {
+        free(op->poll);
+        free(op);
+        return NULL;
+    }
+
+    sigemptyset(&op->sigs);
+    sigaddset(&op->sigs, SIGIO);
+    sigaddset(&op->sigs, SIGRTMIN);
+    sigprocmask(SIG_BLOCK, &op->sigs, NULL);
+    return op;
+}
+
+int rtsig_add(void *arg, struct event *ev)
+{
+    struct rtsigop *op = (struct rtsigop *) arg;
+    int flags, i;
+#ifndef HAVE_WORKING_RTSIG
+    struct stat st;
+#endif
+
+    if (ev->ev_events & EV_SIGNAL) {
+        sigaddset(&op->sigs, EVENT_SIGNAL(ev));
+        return sigprocmask(SIG_BLOCK, &op->sigs, NULL);
+    }
+
+    if (!(ev->ev_events & (EV_READ | EV_WRITE))) return 0;
+
+#ifndef HAVE_WORKING_RTSIG
+    if (fstat(ev->ev_fd, &st) == -1) return -1;
+    if (S_ISFIFO(st.st_mode)) {
+        ev->ev_flags |= EVLIST_X_NORT;
+        op->pollmode++;
+    }
+#endif
+
+    flags = fcntl(ev->ev_fd, F_GETFL);
+    if (flags == -1) return -1;
+
+    if (!(flags & O_ASYNC)) {
+        if (fcntl(ev->ev_fd, F_SETSIG, SIGRTMIN) == -1
+            || fcntl(ev->ev_fd, F_SETOWN, (int) getpid()) == -1)
+                return -1;
+
+        if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC)) return -1;
+    }
+
+#ifdef O_ONESIGFD
+    fcntl(ev->ev_fd, F_SETAUXFL, O_ONESIGFD);
+#endif
+
+    op->total++;
+    if (poll_add(op, ev) == -1) goto err;
+
+    return 0;
+
+err:
+    i = errno;
+    fcntl(ev->ev_fd, F_SETFL, flags);
+    errno = i;
+    return -1;
+}
+
+int rtsig_del(void *arg, struct event *ev)
+{
+    struct rtsigop *op = (struct rtsigop *) arg;
+
+    if (ev->ev_events & EV_SIGNAL) {
+        sigset_t sigs;
+
+        sigdelset(&op->sigs, EVENT_SIGNAL(ev));
+
+        sigemptyset(&sigs);
+        sigaddset(&sigs, EVENT_SIGNAL(ev));
+        return sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+    }
+
+    if (!(ev->ev_events & (EV_READ | EV_WRITE))) return 0;
+
+#ifndef HAVE_WORKING_RTSIG
+    if (ev->ev_flags & EVLIST_X_NORT) op->pollmode--;
+#endif
+    poll_remove(op, ev);
+    op->total--;
+
+    return 0;
+}
+
+int rtsig_recalc(void *arg, int max)
+{
+    return 0;
+}
+
+int rtsig_dispatch(void *arg, struct timeval *tv)
+{
+    struct rtsigop *op = (struct rtsigop *) arg;
+    struct timespec ts;
+    int res, i;
+
+    if (op->poll == NULL) goto retry_poll;
+#ifndef HAVE_WORKING_RTSIG
+    if (op->pollmode) goto poll_all;
+#endif
+
+    if (op->cur) {
+        ts.tv_sec = ts.tv_nsec = 0;
+    } else {
+        ts.tv_sec = tv->tv_sec;
+        ts.tv_nsec = tv->tv_usec * 1000;
+    }
+
+    for (;;) {
+        siginfo_t info;
+        struct event *ev;
+        int signum;
+
+        signum = sigtimedwait(&op->sigs, &info, &ts);
+
+        if (signum == -1) {
+            if (errno == EAGAIN) break;
+            return errno == EINTR ? 0 : -1;
+        }
+
+        ts.tv_sec = ts.tv_nsec = 0;
+
+        if (signum == SIGIO) {
+poll_all:
+            free(op->poll);
+            free(op->toev);
+retry_poll:
+            op->cur = 0;
+            op->max = op->total;
+            op->poll = malloc(sizeof(*op->poll) * op->total);
+            if (op->poll == NULL) return -1;
+            op->toev = malloc(sizeof(*op->toev) * op->total);
+            if (op->toev == NULL) {
+                free(op->poll);
+                op->poll = NULL;
+                return -1;
+            }
+
+            TAILQ_FOREACH(ev, &eventqueue, ev_next)
+                if (!(ev->ev_flags & EVENT_NORT))
+                    poll_add(op, ev);
+
+            break;
+        }
+
+        if (signum == SIGRTMIN) {
+            int flags, i, sigok = 0;
+
+            if (info.si_band <= 0) { /* SI_SIGIO */
+                flags = EV_READ | EV_WRITE;
+            } else {
+                flags = 0;
+                if (info.si_band & POLLIN) flags |= EV_READ;
+                if (info.si_band & POLLOUT) flags |= EV_WRITE;
+                if (!flags) continue;
+            }
+
+            for (i = 0; flags && i < op->cur; i++) {
+                ev = op->toev[i];
+
+                if (ev->ev_fd == info.si_fd) {
+                    flags &= ~ev->ev_events;
+                    sigok = 1;
+                }
+            }
+
+            for (ev = TAILQ_FIRST(&eventqueue);
+                 flags && ev != TAILQ_END(&eventqueue);
+                 ev = TAILQ_NEXT(ev, ev_next))
+            {
+                if (ev->ev_fd == info.si_fd) {
+                    if (flags & ev->ev_events) {
+                        i = poll_add(op, ev);
+                        if (i == -1) return -1;
+                        flags &= ~ev->ev_events;
+                    }
+                    sigok = 1;
+                }
+            }
+
+            if (!sigok) {
+                flags = fcntl(info.si_fd, F_GETFL);
+                if (flags == -1) return -1;
+                fcntl(info.si_fd, F_SETFL, flags & ~O_ASYNC);
+            }
+        } else {
+            TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
+                if (EVENT_SIGNAL(ev) == signum)
+                    activate(ev, EV_SIGNAL);
+            }
+        }
+    }
+
+    if (!op->cur) return 0;
+
+    res = poll(op->poll, op->cur, tv->tv_sec * 1000 + tv->tv_usec / 1000);
+    if (res < 0) return -1;
+
+    i = 0;
+#ifdef HAVE_WORKING_RTSIG
+    while (i < res) {
+#else
+    while (i < op->cur) {
+#endif
+        if (op->poll[i].revents) {
+            int flags = 0;
+            struct event *ev = op->toev[i];
+
+            if (op->poll[i].revents & POLLIN) flags |= EV_READ;
+            if (op->poll[i].revents & POLLOUT) flags |= EV_WRITE;
+
+            if (!(ev->ev_events & EV_PERSIST)) {
+                event_del(ev);
+                res--;
+            } else {
+                i++;
+            }
+            event_active(ev, flags, 1);
+        } else {
+#ifndef HAVE_WORKING_RTSIG
+            if (op->toev[i]->ev_flags & EVLIST_X_NORT) {
+                i++;
+                res++;
+                continue;
+            }
+#endif
+            for (;;) {
+                op->cur--;
+                if (i == op->cur) break;
+                if (op->poll[op->cur].revents) {
+                    memcpy(&op->poll[i], &op->poll[op->cur], sizeof(*op->poll));
+                    op->toev[i] = op->toev[op->cur];
+                    break;
+                }
+            }
+        }
+    }
+#ifdef HAVE_WORKING_RTSIG
+    op->cur = res;
+#endif
+
+    if (!op->cur) {
+        op->max = INIT_MAX;
+        free(op->poll);
+        free(op->toev);
+        /* We just freed it, we shouldn't have a problem getting it back. */
+        op->poll = malloc(sizeof(*op->poll) * op->max);
+        op->toev = malloc(sizeof(*op->toev) * op->max);
+    }
+
+    return 0;
+}