mirror of
https://github.com/libevent/libevent.git
synced 2025-01-09 00:56:20 +08:00
Real time signal support from Taral <taral@taral.net>
svn:r75
This commit is contained in:
parent
ee8cc84e9f
commit
2bf53326c0
410
rtsig.c
Normal file
410
rtsig.c
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user