X-Git-Url: http://git.archive.openwrt.org/?p=project%2Flibubox.git;a=blobdiff_plain;f=uloop.c;h=8517366a55f19154b25a67dd21ce1e08f09e1589;hp=ea160a06a0f3d9127e021130fb4227e04ce3d1b5;hb=6eff829d788b36939325557066f58aafd6a05321;hpb=b8d9b382e39823850331edc2a92379173daf1be3 diff --git a/uloop.c b/uloop.c index ea160a0..8517366 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 @@ -58,241 +58,81 @@ static struct list_head processes = LIST_HEAD_INIT(processes); 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 = {}; - - if (poll_fd >= 0) - return 0; - - poll_fd = kqueue(); - if (poll_fd < 0) - return -1; - - EV_SET(&ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); - kevent(poll_fd, &ev, 1, NULL, 0, &timeout); +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); - return 0; -} - - -static uint16_t get_flags(unsigned int flags, unsigned int mask) -{ - uint16_t kflags = 0; - - if (!(flags & mask)) - return EV_DELETE; - - kflags = EV_ADD; - if (flags & ULOOP_EDGE_TRIGGER) - kflags |= EV_CLEAR; - - return kflags; -} +#ifdef USE_KQUEUE +#include "uloop-kqueue.c" +#endif -static struct kevent events[ULOOP_MAX_EVENTS]; +#ifdef USE_EPOLL +#include "uloop-epoll.c" +#endif -static int register_kevent(struct uloop_fd *fd, unsigned int flags) +static void waker_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; - - changed = flags ^ fd->flags; - if (changed & ULOOP_EDGE_TRIGGER) - changed |= flags; - - if (changed & ULOOP_READ) { - kflags = get_flags(flags, ULOOP_READ); - EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); - } - - 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; + char buf[4]; - return 0; + while (read(fd->fd, buf, 4) > 0) + ; } -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 = waker_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; - - 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; - - cur->fd = u; - if (!u) - continue; - - if (events[n].flags & EV_ERROR) { - u->error = true; - if (!(u->flags & ULOOP_ERROR_CB)) - uloop_fd_delete(u); - } + int fds[2]; - if(events[n].filter == EVFILT_READ) - ev |= ULOOP_READ; - else if (events[n].filter == EVFILT_WRITE) - ev |= ULOOP_WRITE; - - if (events[n].flags & EV_EOF) - u->eof = true; - else if (!ev) - cur->fd = NULL; - - cur->events = ev; - if (u->flags & ULOOP_EDGE_DEFER) { - u->flags &= ~ULOOP_EDGE_DEFER; - u->flags |= ULOOP_EDGE_TRIGGER; - register_kevent(u, u->flags); - } - } - return nfds; -} - -#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 - -int uloop_init(void) -{ - if (poll_fd >= 0) + if (waker_pipe >= 0) return 0; - poll_fd = epoll_create(32); - if (poll_fd < 0) + if (pipe(fds) < 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; + waker_init_fd(fds[0]); + waker_init_fd(fds[1]); + waker_pipe = fds[1]; - ev.data.fd = fd->fd; - ev.data.ptr = fd; - fd->flags = flags; + waker_fd.fd = fds[0]; + waker_fd.cb = waker_consume; + uloop_fd_add(&waker_fd, ULOOP_READ); - return epoll_ctl(poll_fd, op, fd->fd, &ev); + return 0; } -static struct epoll_event events[ULOOP_MAX_EVENTS]; +static void uloop_setup_signals(bool add); -static int __uloop_fd_delete(struct uloop_fd *sock) -{ - sock->flags = 0; - return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); -} - -static int uloop_fetch_events(int timeout) +int uloop_init(void) { - int n, nfds; - - 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; - - cur->fd = u; - if (!u) - continue; - - if (events[n].events & (EPOLLERR|EPOLLHUP)) { - u->error = true; - if (!(u->flags & ULOOP_ERROR_CB)) - uloop_fd_delete(u); - } - - if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { - cur->fd = NULL; - continue; - } - - if(events[n].events & EPOLLRDHUP) - u->eof = true; - - if(events[n].events & EPOLLIN) - ev |= ULOOP_READ; - - if(events[n].events & EPOLLOUT) - ev |= ULOOP_WRITE; + if (uloop_init_pollfd() < 0) + return -1; - cur->events = ev; + if (waker_init() < 0) { + uloop_done(); + return -1; } - return nfds; -} + uloop_setup_signals(true); -#endif + return 0; +} static bool uloop_fd_stack_event(struct uloop_fd *fd, int events) { @@ -531,6 +371,9 @@ static void uloop_handle_processes(void) while (1) { pid = waitpid(-1, &ret, WNOHANG); + if (pid < 0 && errno == EINTR) + continue; + if (pid <= 0) return; @@ -548,14 +391,28 @@ static void uloop_handle_processes(void) } +static void uloop_signal_wake(void) +{ + do { + if (write(waker_pipe, "w", 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(); } static void uloop_sigchld(int signo) { do_sigchld = true; + uloop_signal_wake(); } static void uloop_install_handler(int signum, void (*handler)(int), struct sigaction* old, bool add) @@ -610,7 +467,9 @@ static void uloop_setup_signals(bool add) uloop_install_handler(SIGINT, uloop_handle_sigint, &old_sigint, add); uloop_install_handler(SIGTERM, uloop_handle_sigint, &old_sigterm, add); - uloop_install_handler(SIGCHLD, uloop_sigchld, &old_sigchld, add); + + if (uloop_handle_sigchld) + uloop_install_handler(SIGCHLD, uloop_sigchld, &old_sigchld, add); uloop_ignore_signal(SIGPIPE, add); } @@ -663,20 +522,21 @@ static void uloop_clear_processes(void) uloop_process_delete(p); } -void uloop_run(void) +bool uloop_cancelling(void) { - static int recursive_calls = 0; + return uloop_run_depth > 0 && uloop_cancelled; +} + +int uloop_run_timeout(int timeout) +{ + int next_time = 0; struct timeval tv; - /* - * Handlers are only updated for the first call to uloop_run() (and restored - * when this call is done). - */ - if (!recursive_calls++) - uloop_setup_signals(true); + uloop_run_depth++; + uloop_status = 0; uloop_cancelled = false; - while(!uloop_cancelled) + while (!uloop_cancelled) { uloop_gettime(&tv); uloop_process_timeouts(&tv); @@ -688,20 +548,33 @@ void uloop_run(void) break; uloop_gettime(&tv); - uloop_run_events(uloop_get_next_timeout(&tv)); + + next_time = uloop_get_next_timeout(&tv); + if (timeout >= 0 && timeout < next_time) + next_time = timeout; + uloop_run_events(next_time); } - if (!--recursive_calls) - uloop_setup_signals(false); + --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();