X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=uloop.c;h=da6f690cfad63f100f38510d087e81350cc1ea2b;hb=a2fce001819ea5144652ecb9f828f892a1648fb8;hp=37465203cc478f413abde4a833ee3f20ed069d6d;hpb=cd5238b5003ab8b4bbceedbf89f59542f2629b45;p=project%2Flibubox.git diff --git a/uloop.c b/uloop.c index 3746520..da6f690 100644 --- a/uloop.c +++ b/uloop.c @@ -1,7 +1,7 @@ /* * uloop - event loop implementation * - * Copyright (C) 2010-2013 Felix Fietkau + * Copyright (C) 2010-2016 Felix Fietkau * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,6 +26,7 @@ #include #include #include +#include #include "uloop.h" #include "utils.h" @@ -35,6 +36,7 @@ #endif #ifdef USE_EPOLL #include +#include #endif #include @@ -43,246 +45,150 @@ struct uloop_fd_event { unsigned int events; }; +struct uloop_fd_stack { + struct uloop_fd_stack *next; + struct uloop_fd *fd; + unsigned int events; +}; + +static struct uloop_fd_stack *fd_stack = NULL; + #define ULOOP_MAX_EVENTS 10 static struct list_head timeouts = LIST_HEAD_INIT(timeouts); static struct list_head processes = LIST_HEAD_INIT(processes); +static struct list_head signals = LIST_HEAD_INIT(signals); static int poll_fd = -1; bool uloop_cancelled = false; bool uloop_handle_sigchld = true; +static int uloop_status = 0; static bool do_sigchld = false; static struct uloop_fd_event cur_fds[ULOOP_MAX_EVENTS]; static int cur_fd, cur_nfds; +static int uloop_run_depth = 0; -#ifdef USE_KQUEUE - -int uloop_init(void) -{ - struct timespec timeout = { 0, 0 }; - struct kevent ev = {}; +uloop_fd_handler uloop_fd_set_cb = NULL; - if (poll_fd >= 0) - return 0; +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); - poll_fd = kqueue(); - if (poll_fd < 0) - return -1; +#ifdef USE_KQUEUE +#include "uloop-kqueue.c" +#endif - EV_SET(&ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); - kevent(poll_fd, &ev, 1, NULL, 0, &timeout); +#ifdef USE_EPOLL +#include "uloop-epoll.c" +#endif - return 0; +static void set_signo(uint64_t *signums, int signo) +{ + if (signo >= 1 && signo <= 64) + *signums |= (1u << (signo - 1)); } - -static uint16_t get_flags(unsigned int flags, unsigned int mask) +static bool get_signo(uint64_t signums, int signo) { - uint16_t kflags = 0; - - if (!(flags & mask)) - return EV_DELETE; - - kflags = EV_ADD; - if (flags & ULOOP_EDGE_TRIGGER) - kflags |= EV_CLEAR; - - return kflags; + return (signo >= 1) && (signo <= 64) && (signums & (1u << (signo - 1))); } -static struct kevent events[ULOOP_MAX_EVENTS]; - -static int register_kevent(struct uloop_fd *fd, unsigned int flags) +static void signal_consume(struct uloop_fd *fd, unsigned int events) { - struct timespec timeout = { 0, 0 }; - struct kevent ev[2]; - int nev = 0; - unsigned int fl = 0; - unsigned int changed; - uint16_t kflags; - - if (flags & ULOOP_EDGE_DEFER) - flags &= ~ULOOP_EDGE_TRIGGER; + struct uloop_signal *usig, *usig_next; + uint64_t signums = 0; + uint8_t buf[32]; + ssize_t nsigs; - changed = flags ^ fd->flags; - if (changed & ULOOP_EDGE_TRIGGER) - changed |= flags; + do { + nsigs = read(fd->fd, buf, sizeof(buf)); - if (changed & ULOOP_READ) { - kflags = get_flags(flags, ULOOP_READ); - EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); + for (ssize_t i = 0; i < nsigs; i++) + set_signo(&signums, buf[i]); } + while (nsigs > 0); - if (changed & ULOOP_WRITE) { - kflags = get_flags(flags, ULOOP_WRITE); - EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); - } - - if (!flags) - fl |= EV_DELETE; - - fd->flags = flags; - if (kevent(poll_fd, ev, nev, NULL, fl, &timeout) == -1) - return -1; - - return 0; + list_for_each_entry_safe(usig, usig_next, &signals, list) + if (get_signo(signums, usig->signo)) + usig->cb(usig); } -static int register_poll(struct uloop_fd *fd, unsigned int flags) -{ - if (flags & ULOOP_EDGE_TRIGGER) - flags |= ULOOP_EDGE_DEFER; - else - flags &= ~ULOOP_EDGE_DEFER; - - return register_kevent(fd, flags); -} +static int waker_pipe = -1; +static struct uloop_fd waker_fd = { + .fd = -1, + .cb = signal_consume, +}; -static int __uloop_fd_delete(struct uloop_fd *fd) +static void waker_init_fd(int fd) { - return register_poll(fd, 0); + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); } -static int uloop_fetch_events(int timeout) +static int waker_init(void) { - struct timespec ts; - int nfds, n; + int fds[2]; - if (timeout >= 0) { - ts.tv_sec = timeout / 1000; - ts.tv_nsec = (timeout % 1000) * 1000000; - } - - nfds = kevent(poll_fd, NULL, 0, events, ARRAY_SIZE(events), timeout >= 0 ? &ts : NULL); - for (n = 0; n < nfds; n++) { - struct uloop_fd_event *cur = &cur_fds[n]; - struct uloop_fd *u = events[n].udata; - unsigned int ev = 0; + if (waker_pipe >= 0) + return 0; - cur->fd = u; - if (!u) - continue; + if (pipe(fds) < 0) + return -1; - if (events[n].flags & EV_ERROR) { - u->error = true; - uloop_fd_delete(u); - } + waker_init_fd(fds[0]); + waker_init_fd(fds[1]); + waker_pipe = fds[1]; - if(events[n].filter == EVFILT_READ) - ev |= ULOOP_READ; - else if (events[n].filter == EVFILT_WRITE) - ev |= ULOOP_WRITE; + waker_fd.fd = fds[0]; + waker_fd.cb = signal_consume; + uloop_fd_add(&waker_fd, ULOOP_READ); - if (events[n].flags & EV_EOF) - u->eof = true; - else if (!ev) - cur->fd = NULL; - - if (u->flags & ULOOP_EDGE_DEFER) { - u->flags &= ~ULOOP_EDGE_DEFER; - u->flags |= ULOOP_EDGE_TRIGGER; - register_kevent(u, u->flags); - } - } - return nfds; + return 0; } -#endif - -#ifdef USE_EPOLL - -/** - * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 - */ -#ifndef EPOLLRDHUP -#define EPOLLRDHUP 0x2000 -#endif +static void uloop_setup_signals(bool add); int uloop_init(void) { - if (poll_fd >= 0) - return 0; - - poll_fd = epoll_create(32); - if (poll_fd < 0) + if (uloop_init_pollfd() < 0) return -1; - fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); - return 0; -} - -static int register_poll(struct uloop_fd *fd, unsigned int flags) -{ - struct epoll_event ev; - int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; - - memset(&ev, 0, sizeof(struct epoll_event)); - - if (flags & ULOOP_READ) - ev.events |= EPOLLIN | EPOLLRDHUP; - - if (flags & ULOOP_WRITE) - ev.events |= EPOLLOUT; - - if (flags & ULOOP_EDGE_TRIGGER) - ev.events |= EPOLLET; - - ev.data.fd = fd->fd; - ev.data.ptr = fd; - - return epoll_ctl(poll_fd, op, fd->fd, &ev); -} + if (waker_init() < 0) { + uloop_done(); + return -1; + } -static struct epoll_event events[ULOOP_MAX_EVENTS]; + uloop_setup_signals(true); -static int __uloop_fd_delete(struct uloop_fd *sock) -{ - return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); + return 0; } -static int uloop_fetch_events(int timeout) +static bool uloop_fd_stack_event(struct uloop_fd *fd, int events) { - int n, nfds; + struct uloop_fd_stack *cur; - nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); - for (n = 0; n < nfds; ++n) { - struct uloop_fd_event *cur = &cur_fds[n]; - struct uloop_fd *u = events[n].data.ptr; - unsigned int ev = 0; + /* + * Do not buffer events for level-triggered fds, they will keep firing. + * Caller needs to take care of recursion issues. + */ + if (!(fd->flags & ULOOP_EDGE_TRIGGER)) + return false; - cur->fd = u; - if (!u) + for (cur = fd_stack; cur; cur = cur->next) { + if (cur->fd != fd) continue; - if (events[n].events & (EPOLLERR|EPOLLHUP)) { - u->error = true; - uloop_fd_delete(u); - } - - if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { + if (events < 0) cur->fd = NULL; - continue; - } - - if(events[n].events & EPOLLRDHUP) - u->eof = true; + else + cur->events |= events | ULOOP_EVENT_BUFFERED; - if(events[n].events & EPOLLIN) - ev |= ULOOP_READ; - - if(events[n].events & EPOLLOUT) - ev |= ULOOP_WRITE; - - cur->events = ev; + return true; } - return nfds; + return false; } -#endif - -static void uloop_run_events(int timeout) +static void uloop_run_events(int64_t timeout) { struct uloop_fd_event *cur; struct uloop_fd *fd; @@ -295,17 +201,33 @@ static void uloop_run_events(int timeout) } while (cur_nfds > 0) { + struct uloop_fd_stack stack_cur; + unsigned int events; + cur = &cur_fds[cur_fd++]; cur_nfds--; fd = cur->fd; + events = cur->events; if (!fd) continue; if (!fd->cb) continue; - fd->cb(fd, cur->events); + if (uloop_fd_stack_event(fd, cur->events)) + continue; + + stack_cur.next = fd_stack; + stack_cur.fd = fd; + fd_stack = &stack_cur; + do { + stack_cur.events = 0; + fd->cb(fd, events); + events = stack_cur.events & ULOOP_EVENT_MASK; + } while (stack_cur.fd && events); + fd_stack = stack_cur.next; + return; } } @@ -328,8 +250,13 @@ int uloop_fd_add(struct uloop_fd *sock, unsigned int flags) if (ret < 0) goto out; + if (uloop_fd_set_cb) + uloop_fd_set_cb(sock, flags); + + sock->flags = flags; sock->registered = true; sock->eof = false; + sock->error = false; out: return ret; @@ -337,22 +264,31 @@ out: int uloop_fd_delete(struct uloop_fd *fd) { + int ret; int i; - if (!fd->registered) - return 0; - for (i = 0; i < cur_nfds; i++) { if (cur_fds[cur_fd + i].fd != fd) continue; cur_fds[cur_fd + i].fd = NULL; } + + if (!fd->registered) + return 0; + + if (uloop_fd_set_cb) + uloop_fd_set_cb(fd, 0); + fd->registered = false; - return __uloop_fd_delete(fd); + uloop_fd_stack_event(fd, -1); + ret = __uloop_fd_delete(fd); + fd->flags = 0; + + return ret; } -static int tv_diff(struct timeval *t1, struct timeval *t2) +static int64_t tv_diff(struct timeval *t1, struct timeval *t2) { return (t1->tv_sec - t2->tv_sec) * 1000 + @@ -396,14 +332,14 @@ int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) if (timeout->pending) uloop_timeout_cancel(timeout); - uloop_gettime(&timeout->time); + uloop_gettime(time); time->tv_sec += msecs / 1000; time->tv_usec += (msecs % 1000) * 1000; if (time->tv_usec > 1000000) { time->tv_sec++; - time->tv_usec %= 1000000; + time->tv_usec -= 1000000; } return uloop_timeout_add(timeout); @@ -421,6 +357,26 @@ int uloop_timeout_cancel(struct uloop_timeout *timeout) } int uloop_timeout_remaining(struct uloop_timeout *timeout) +{ + int64_t td; + struct timeval now; + + if (!timeout->pending) + return -1; + + uloop_gettime(&now); + + td = tv_diff(&timeout->time, &now); + + if (td > INT_MAX) + return INT_MAX; + else if (td < INT_MIN) + return INT_MIN; + else + return (int)td; +} + +int64_t uloop_timeout_remaining64(struct uloop_timeout *timeout) { struct timeval now; @@ -474,6 +430,9 @@ static void uloop_handle_processes(void) while (1) { pid = waitpid(-1, &ret, WNOHANG); + if (pid < 0 && errno == EINTR) + continue; + if (pid <= 0) return; @@ -491,55 +450,182 @@ static void uloop_handle_processes(void) } +int uloop_interval_set(struct uloop_interval *timer, unsigned int msecs) +{ + return timer_register(timer, msecs); +} + +int uloop_interval_cancel(struct uloop_interval *timer) +{ + return timer_remove(timer); +} + +int64_t uloop_interval_remaining(struct uloop_interval *timer) +{ + return timer_next(timer); +} + +static void uloop_signal_wake(int signo) +{ + uint8_t sigbyte = signo; + + if (signo == SIGCHLD) + do_sigchld = true; + + do { + if (write(waker_pipe, &sigbyte, 1) < 0) { + if (errno == EINTR) + continue; + } + break; + } while (1); +} + static void uloop_handle_sigint(int signo) { + uloop_status = signo; uloop_cancelled = true; + uloop_signal_wake(signo); } -static void uloop_sigchld(int signo) +static void uloop_install_handler(int signum, void (*handler)(int), struct sigaction* old, bool add) { - do_sigchld = true; + struct sigaction s; + struct sigaction *act; + + act = NULL; + sigaction(signum, NULL, &s); + + if (add) { + if (s.sa_handler == SIG_DFL) { /* Do not override existing custom signal handlers */ + memcpy(old, &s, sizeof(struct sigaction)); + s.sa_handler = handler; + s.sa_flags = 0; + act = &s; + } + } + else if (s.sa_handler == handler) { /* Do not restore if someone modified our handler */ + act = old; + } + + if (act != NULL) + sigaction(signum, act, NULL); } -static void uloop_setup_signals(void) +static void uloop_ignore_signal(int signum, bool ignore) { struct sigaction s; + void *new_handler = NULL; + + sigaction(signum, NULL, &s); - memset(&s, 0, sizeof(struct sigaction)); - s.sa_handler = uloop_handle_sigint; - s.sa_flags = 0; - sigaction(SIGINT, &s, NULL); + if (ignore) { + if (s.sa_handler == SIG_DFL) /* Ignore only if there isn't any custom handler */ + new_handler = SIG_IGN; + } else { + if (s.sa_handler == SIG_IGN) /* Restore only if noone modified our SIG_IGN */ + new_handler = SIG_DFL; + } - if (uloop_handle_sigchld) { - s.sa_handler = uloop_sigchld; - sigaction(SIGCHLD, &s, NULL); + if (new_handler) { + s.sa_handler = new_handler; + s.sa_flags = 0; + sigaction(signum, &s, NULL); } } -static int uloop_get_next_timeout(struct timeval *tv) +static void uloop_setup_signals(bool add) +{ + static struct sigaction old_sigint, old_sigchld, old_sigterm; + + uloop_install_handler(SIGINT, uloop_handle_sigint, &old_sigint, add); + uloop_install_handler(SIGTERM, uloop_handle_sigint, &old_sigterm, add); + + if (uloop_handle_sigchld) + uloop_install_handler(SIGCHLD, uloop_signal_wake, &old_sigchld, add); + + uloop_ignore_signal(SIGPIPE, add); +} + +int uloop_signal_add(struct uloop_signal *s) +{ + struct list_head *h = &signals; + struct uloop_signal *tmp; + struct sigaction sa; + + if (s->pending) + return -1; + + list_for_each_entry(tmp, &signals, list) { + if (tmp->signo > s->signo) { + h = &tmp->list; + break; + } + } + + list_add_tail(&s->list, h); + s->pending = true; + + sigaction(s->signo, NULL, &s->orig); + + if (s->orig.sa_handler != uloop_signal_wake) { + sa.sa_handler = uloop_signal_wake; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(s->signo, &sa, NULL); + } + + return 0; +} + +int uloop_signal_delete(struct uloop_signal *s) +{ + if (!s->pending) + return -1; + + list_del(&s->list); + s->pending = false; + + if (s->orig.sa_handler != uloop_signal_wake) + sigaction(s->signo, &s->orig, NULL); + + return 0; +} + +int uloop_get_next_timeout(void) { struct uloop_timeout *timeout; - int diff; + struct timeval tv; + int64_t diff; if (list_empty(&timeouts)) return -1; + uloop_gettime(&tv); + timeout = list_first_entry(&timeouts, struct uloop_timeout, list); - diff = tv_diff(&timeout->time, tv); + diff = tv_diff(&timeout->time, &tv); if (diff < 0) return 0; + if (diff > INT_MAX) + return INT_MAX; return diff; } -static void uloop_process_timeouts(struct timeval *tv) +static void uloop_process_timeouts(void) { struct uloop_timeout *t; + struct timeval tv; + + if (list_empty(&timeouts)) + return; + uloop_gettime(&tv); while (!list_empty(&timeouts)) { t = list_first_entry(&timeouts, struct uloop_timeout, list); - if (tv_diff(&t->time, tv) > 0) + if (tv_diff(&t->time, &tv) > 0) break; uloop_timeout_cancel(t); @@ -564,31 +650,54 @@ static void uloop_clear_processes(void) uloop_process_delete(p); } -void uloop_run(void) +bool uloop_cancelling(void) { - struct timeval tv; + return uloop_run_depth > 0 && uloop_cancelled; +} - uloop_setup_signals(); - while(!uloop_cancelled) - { - uloop_gettime(&tv); - uloop_process_timeouts(&tv); - if (uloop_cancelled) - break; +int uloop_run_timeout(int timeout) +{ + int next_time = 0; + + uloop_run_depth++; + + uloop_status = 0; + uloop_cancelled = false; + do { + uloop_process_timeouts(); if (do_sigchld) uloop_handle_processes(); - uloop_run_events(uloop_get_next_timeout(&tv)); - } + + if (uloop_cancelled) + break; + + next_time = uloop_get_next_timeout(); + if (timeout >= 0 && (next_time < 0 || timeout < next_time)) + next_time = timeout; + uloop_run_events(next_time); + } while (!uloop_cancelled && timeout < 0); + + --uloop_run_depth; + + return uloop_status; } void uloop_done(void) { - if (poll_fd < 0) - return; + uloop_setup_signals(false); - close(poll_fd); - poll_fd = -1; + if (poll_fd >= 0) { + close(poll_fd); + poll_fd = -1; + } + + if (waker_pipe >= 0) { + uloop_fd_delete(&waker_fd); + close(waker_pipe); + close(waker_fd.fd); + waker_pipe = -1; + } uloop_clear_timeouts(); uloop_clear_processes();