1
1
openmpi/opal/event/rtsig.c
Brian Barrett 3a7edebc71 * Use the internal (non-locking) functions when in code that will already
be holding the lock.  Fixes ticket #251.

This commit was SVN r11172.
2006-08-14 16:09:17 +00:00

438 строки
8.5 KiB
C

#include "opal_config.h"
/* Enable F_SETSIG and F_SETOWN */
#define _GNU_SOURCE
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#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
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#define EVLIST_X_NORT 0x1000 /* Skip RT signals (internal) */
#include "event.h"
#include "log.h"
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_i(ev);
event_active_i(ev, flags, 1);
}
static void *rtsig_init(void);
static int rtsig_add(void *, struct event *);
static int rtsig_del(void *, struct event *);
static int rtsig_recalc(struct event_base *, void *, int);
static int rtsig_dispatch(struct event_base *, void *, struct timeval *);
struct opal_eventop rtsigops = {
"rtsig",
rtsig_init,
rtsig_add,
rtsig_del,
rtsig_recalc,
rtsig_dispatch
};
static 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);
}
static 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);
}
static 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);
}
static int
rtsig_recalc(struct event_base *base, void *arg, int max)
{
return (0);
}
static int
rtsig_dispatch(struct event_base *base, 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) {
#ifndef HAVE_WORKING_RTSIG
poll_all:
#endif
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, &base->eventqueue, ev_next)
if (!(ev->ev_flags & EVLIST_X_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(&base->eventqueue);
flags && ev != TAILQ_END(&base->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 + 999) / 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_i(ev);
res--;
} else {
i++;
}
event_active_i(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);
if (op->poll == NULL || op->toev == NULL)
event_err(1, "%s: malloc");
}
return (0);
}