diff options
Diffstat (limited to 'src/fdevent_linux_rtsig.c')
-rw-r--r-- | src/fdevent_linux_rtsig.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/src/fdevent_linux_rtsig.c b/src/fdevent_linux_rtsig.c new file mode 100644 index 00000000..8bedaf0d --- /dev/null +++ b/src/fdevent_linux_rtsig.c @@ -0,0 +1,260 @@ +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <limits.h> + +#define __USE_GNU +#include <fcntl.h> + +#include "fdevent.h" +#include "settings.h" +#include "buffer.h" + +#ifdef USE_LINUX_SIGIO +static void fdevent_linux_rtsig_free(fdevents *ev) { + free(ev->pollfds); + if (ev->unused.ptr) free(ev->unused.ptr); + + bitset_free(ev->sigbset); +} + + +static int fdevent_linux_rtsig_event_del(fdevents *ev, int fde_ndx, int fd) { + if (fde_ndx < 0) return -1; + + if ((size_t)fde_ndx >= ev->used) { + fprintf(stderr, "%s.%d: del! out of range %d %u\n", __FILE__, __LINE__, fde_ndx, ev->used); + SEGFAULT(); + } + + if (ev->pollfds[fde_ndx].fd == fd) { + size_t k = fde_ndx; + + ev->pollfds[k].fd = -1; + + bitset_clear_bit(ev->sigbset, fd); + + if (ev->unused.size == 0) { + ev->unused.size = 16; + ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); + } else if (ev->unused.size == ev->unused.used) { + ev->unused.size += 16; + ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); + } + + ev->unused.ptr[ev->unused.used++] = k; + } else { + fprintf(stderr, "%s.%d: del! %d %d\n", __FILE__, __LINE__, ev->pollfds[fde_ndx].fd, fd); + + SEGFAULT(); + } + + return -1; +} + +#if 0 +static int fdevent_linux_rtsig_event_compress(fdevents *ev) { + size_t j; + + if (ev->used == 0) return 0; + if (ev->unused.used != 0) return 0; + + for (j = ev->used - 1; j + 1 > 0; j--) { + if (ev->pollfds[j].fd == -1) ev->used--; + } + + + return 0; +} +#endif + +static int fdevent_linux_rtsig_event_add(fdevents *ev, int fde_ndx, int fd, int events) { + /* known index */ + if (fde_ndx != -1) { + if (ev->pollfds[fde_ndx].fd == fd) { + ev->pollfds[fde_ndx].events = events; + + return fde_ndx; + } + fprintf(stderr, "%s.%d: add: (%d, %d)\n", __FILE__, __LINE__, fde_ndx, ev->pollfds[fde_ndx].fd); + SEGFAULT(); + } + + if (ev->unused.used > 0) { + int k = ev->unused.ptr[--ev->unused.used]; + + ev->pollfds[k].fd = fd; + ev->pollfds[k].events = events; + + bitset_set_bit(ev->sigbset, fd); + + return k; + } else { + if (ev->size == 0) { + ev->size = 16; + ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); + } else if (ev->size == ev->used) { + ev->size += 16; + ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); + } + + ev->pollfds[ev->used].fd = fd; + ev->pollfds[ev->used].events = events; + + bitset_set_bit(ev->sigbset, fd); + + return ev->used++; + } +} + +static int fdevent_linux_rtsig_poll(fdevents *ev, int timeout_ms) { + struct timespec ts; + int r; + +#if 0 + fdevent_linux_rtsig_event_compress(ev); +#endif + + ev->in_sigio = 1; + + ts.tv_sec = timeout_ms / 1000; + ts.tv_nsec = (timeout_ms % 1000) * 1000000; + r = sigtimedwait(&(ev->sigset), &(ev->siginfo), &(ts)); + + if (r == -1) { + if (errno == EAGAIN) return 0; + return r; + } else if (r == SIGIO) { + struct sigaction act; + + /* flush the signal queue */ + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(ev->signum, &act, NULL); + + /* re-enable the signal queue */ + act.sa_handler = SIG_DFL; + sigaction(ev->signum, &act, NULL); + + ev->in_sigio = 0; + r = poll(ev->pollfds, ev->used, timeout_ms); + + return r; + } else if (r == ev->signum) { +# if 0 + fprintf(stderr, "event: %d %02lx\n", ev->siginfo.si_fd, ev->siginfo.si_band); +# endif + return bitset_test_bit(ev->sigbset, ev->siginfo.si_fd); + } else { + /* ? */ + return -1; + } +} + +static int fdevent_linux_rtsig_event_get_revent(fdevents *ev, size_t ndx) { + if (ev->in_sigio == 1) { +# if 0 + if (ev->siginfo.si_band == POLLERR) { + fprintf(stderr, "event: %d %02lx %02x %s\n", ev->siginfo.si_fd, ev->siginfo.si_band, errno, strerror(errno)); + } +# endif + if (ndx != 0) { + fprintf(stderr, "+\n"); + return 0; + } + + return ev->siginfo.si_band & 0x3f; + } else { + if (ndx >= ev->used) { + fprintf(stderr, "%s.%d: event: %u %u\n", __FILE__, __LINE__, ndx, ev->used); + return 0; + } + return ev->pollfds[ndx].revents; + } +} + +static int fdevent_linux_rtsig_event_get_fd(fdevents *ev, size_t ndx) { + if (ev->in_sigio == 1) { + return ev->siginfo.si_fd; + } else { + return ev->pollfds[ndx].fd; + } +} + +static int fdevent_linux_rtsig_fcntl_set(fdevents *ev, int fd) { + static pid_t pid = 0; + + if (pid == 0) pid = getpid(); + + if (-1 == fcntl(fd, F_SETSIG, ev->signum)) return -1; + + if (-1 == fcntl(fd, F_SETOWN, (int) pid)) return -1; + + return fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK | O_RDWR); +} + + +static int fdevent_linux_rtsig_event_next_fdndx(fdevents *ev, int ndx) { + if (ev->in_sigio == 1) { + if (ndx < 0) return 0; + return -1; + } else { + size_t i; + + i = (ndx < 0) ? 0 : ndx + 1; + for (; i < ev->used; i++) { + if (ev->pollfds[i].revents) break; + } + + return i; + } +} + +int fdevent_linux_rtsig_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_LINUX_RTSIG; +#define SET(x) \ + ev->x = fdevent_linux_rtsig_##x; + + SET(free); + SET(poll); + + SET(event_del); + SET(event_add); + + SET(event_next_fdndx); + SET(fcntl_set); + SET(event_get_fd); + SET(event_get_revent); + + ev->signum = SIGRTMIN + 1; + + sigemptyset(&(ev->sigset)); + sigaddset(&(ev->sigset), ev->signum); + sigaddset(&(ev->sigset), SIGIO); + if (-1 == sigprocmask(SIG_BLOCK, &(ev->sigset), NULL)) { + fprintf(stderr, "%s.%d: sigprocmask failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", + __FILE__, __LINE__, strerror(errno)); + + return -1; + } + + ev->in_sigio = 1; + + ev->sigbset = bitset_init(ev->maxfds); + + return 0; +} +#else +int fdevent_linux_rtsig_init(fdevents *ev) { + UNUSED(ev); + + fprintf(stderr, "%s.%d: linux-rtsig not supported, try to set server.event-handler = \"poll\" or \"select\"\n", + __FILE__, __LINE__); + return -1; +} +#endif |