From 2bf53326c06f0ddee544e64c9281192253767aac Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Thu, 25 Sep 2003 02:53:39 +0000 Subject: [PATCH] Real time signal support from Taral svn:r75 --- rtsig.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 rtsig.c diff --git a/rtsig.c b/rtsig.c new file mode 100644 index 00000000..2674d401 --- /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 +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HAVE_WORKING_RTSIG +#include +#endif +#include + +#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; +} -- 2.40.0