X-Git-Url: http://git.archive.openwrt.org/?p=project%2Flibubox.git;a=blobdiff_plain;f=uloop.c;h=2d7b2c93406a498b359d28042ca1408dbe039271;hp=f60eb41055053d4e582d1347a4cdd594016727e4;hb=17f4e41ecb80f70c14493b4518e6eabec9faff7b;hpb=9abfbad95fc5bec86840ad5121370ddd419db3de diff --git a/uloop.c b/uloop.c index f60eb41..2d7b2c9 100644 --- a/uloop.c +++ b/uloop.c @@ -22,15 +22,6 @@ #include #include -#include "uloop.h" - -#ifdef USE_KQUEUE -#include -#endif -#ifdef USE_EPOLL -#include -#endif - #include #include #include @@ -38,22 +29,37 @@ #include #include #include -#include #include -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#include "uloop.h" +#include "utils.h" + +#ifdef USE_KQUEUE +#include #endif +#ifdef USE_EPOLL +#include +#endif +#include + #define ULOOP_MAX_EVENTS 10 -static struct uloop_timeout *first_timeout; +static struct list_head timeouts = LIST_HEAD_INIT(timeouts); +static struct list_head processes = LIST_HEAD_INIT(processes); + static int poll_fd = -1; bool uloop_cancelled = false; +bool uloop_handle_sigchld = true; +static bool do_sigchld = false; +static int cur_fd, cur_nfds; #ifdef USE_KQUEUE int uloop_init(void) { + struct timespec timeout = { 0, 0 }; + struct kevent ev = {}; + if (poll_fd >= 0) return 0; @@ -61,6 +67,9 @@ int uloop_init(void) 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); + return 0; } @@ -79,58 +88,80 @@ static uint16_t get_flags(unsigned int flags, unsigned int mask) return kflags; } -static int register_poll(struct uloop_fd *fd, unsigned int flags) +static struct kevent events[ULOOP_MAX_EVENTS]; + +static int register_kevent(struct uloop_fd *fd, unsigned int flags) { struct timespec timeout = { 0, 0 }; struct kevent ev[2]; - unsigned int changed; int nev = 0; + unsigned int fl = 0; + uint16_t kflags; - changed = fd->kqflags ^ flags; - if (changed & ULOOP_EDGE_TRIGGER) - changed |= flags; + if (flags & ULOOP_EDGE_DEFER) + flags &= ~ULOOP_EDGE_TRIGGER; - if (changed & ULOOP_READ) { - uint16_t kflags = get_flags(flags, ULOOP_READ); - EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); - } + kflags = get_flags(flags, ULOOP_READ); + EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); - if (changed & ULOOP_WRITE) { - uint16_t kflags = get_flags(flags, ULOOP_WRITE); - EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); - } + kflags = get_flags(flags, ULOOP_WRITE); + EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); + + if (!flags) + fl |= EV_DELETE; - if (nev && (kevent(poll_fd, ev, nev, NULL, 0, &timeout) == -1)) + if (kevent(poll_fd, ev, nev, NULL, fl, &timeout) == -1) return -1; - fd->kqflags = flags; return 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; + + fd->flags = flags; + return register_kevent(fd, flags); +} + int uloop_fd_delete(struct uloop_fd *sock) { + int i; + + for (i = cur_fd + 1; i < cur_nfds; i++) { + if (events[i].udata != sock) + continue; + + events[i].udata = NULL; + } + sock->registered = false; return register_poll(sock, 0); } static void uloop_run_events(int timeout) { - struct kevent events[ULOOP_MAX_EVENTS]; struct timespec ts; int nfds, n; - if (timeout > 0) { + 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); + nfds = kevent(poll_fd, NULL, 0, events, ARRAY_SIZE(events), timeout >= 0 ? &ts : NULL); for(n = 0; n < nfds; ++n) { struct uloop_fd *u = events[n].udata; unsigned int ev = 0; - if(events[n].flags & EV_ERROR) { + if (!u) + continue; + + if (events[n].flags & EV_ERROR) { u->error = true; uloop_fd_delete(u); } @@ -140,14 +171,22 @@ static void uloop_run_events(int timeout) else if (events[n].filter == EVFILT_WRITE) ev |= ULOOP_WRITE; - if(events[n].flags & EV_EOF) + if (events[n].flags & EV_EOF) u->eof = true; else if (!ev) continue; - if(u->cb) + if (u->cb) { + cur_fd = n; + cur_nfds = nfds; u->cb(u, ev); + if (u->flags & ULOOP_EDGE_DEFER) { + u->flags &= ~ULOOP_EDGE_DEFER; + register_kevent(u, u->flags); + } + } } + cur_nfds = 0; } #endif @@ -196,16 +235,25 @@ static int register_poll(struct uloop_fd *fd, unsigned int flags) return epoll_ctl(poll_fd, op, fd->fd, &ev); } +static struct epoll_event events[ULOOP_MAX_EVENTS]; + int uloop_fd_delete(struct uloop_fd *sock) { + int i; + + for (i = cur_fd + 1; i < cur_nfds; i++) { + if (events[i].data.ptr != sock) + continue; + + events[i].data.ptr = NULL; + } sock->registered = false; return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); } static void uloop_run_events(int timeout) { - struct epoll_event events[ULOOP_MAX_EVENTS]; - int nfds, n; + int n, nfds; nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); for(n = 0; n < nfds; ++n) @@ -213,12 +261,15 @@ static void uloop_run_events(int timeout) struct uloop_fd *u = events[n].data.ptr; unsigned int ev = 0; - if(events[n].events & EPOLLERR) { + if (!u) + continue; + + if(events[n].events & (EPOLLERR|EPOLLHUP)) { u->error = true; uloop_fd_delete(u); } - if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR))) + if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) continue; if(events[n].events & EPOLLRDHUP) @@ -230,9 +281,13 @@ static void uloop_run_events(int timeout) if(events[n].events & EPOLLOUT) ev |= ULOOP_WRITE; - if(u->cb) + if(u->cb) { + cur_fd = n; + cur_nfds = nfds; u->cb(u, ev); + } } + cur_nfds = 0; } #endif @@ -261,33 +316,27 @@ out: static int tv_diff(struct timeval *t1, struct timeval *t2) { - if (t1->tv_sec != t2->tv_sec) - return (t1->tv_sec - t2->tv_sec) * 1000; - else - return (t1->tv_usec - t2->tv_usec) / 1000; + return + (t1->tv_sec - t2->tv_sec) * 1000 + + (t1->tv_usec - t2->tv_usec) / 1000; } int uloop_timeout_add(struct uloop_timeout *timeout) { - struct uloop_timeout **head = &first_timeout; - struct uloop_timeout *prev = NULL; + struct uloop_timeout *tmp; + struct list_head *h = &timeouts; if (timeout->pending) return -1; - while (*head) { - if (tv_diff(&(*head)->time, &timeout->time) > 0) + list_for_each_entry(tmp, &timeouts, list) { + if (tv_diff(&tmp->time, &timeout->time) > 0) { + h = &tmp->list; break; - - prev = *head; - head = &(*head)->next; + } } - timeout->prev = prev; - timeout->next = *head; - if (timeout->next) - timeout->next->prev = timeout; - *head = timeout; + list_add_tail(&timeout->list, h); timeout->pending = true; return 0; @@ -303,11 +352,11 @@ int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) gettimeofday(&timeout->time, NULL); time->tv_sec += msecs / 1000; - time->tv_usec += msecs % 1000; + time->tv_usec += (msecs % 1000) * 1000; if (time->tv_usec > 1000000) { time->tv_sec++; - time->tv_usec %= 100000; + time->tv_usec %= 1000000; } return uloop_timeout_add(timeout); @@ -318,41 +367,106 @@ int uloop_timeout_cancel(struct uloop_timeout *timeout) if (!timeout->pending) return -1; - if (timeout->prev) - timeout->prev->next = timeout->next; - else - first_timeout = timeout->next; + list_del(&timeout->list); + timeout->pending = false; - if (timeout->next) - timeout->next->prev = timeout->prev; + return 0; +} - timeout->pending = false; +int uloop_process_add(struct uloop_process *p) +{ + struct uloop_process *tmp; + struct list_head *h = &processes; + + if (p->pending) + return -1; + + list_for_each_entry(tmp, &processes, list) { + if (tmp->pid > p->pid) { + h = &tmp->list; + break; + } + } + + list_add_tail(&p->list, h); + p->pending = true; return 0; } +int uloop_process_delete(struct uloop_process *p) +{ + if (!p->pending) + return -1; + + list_del(&p->list); + p->pending = false; + + return 0; +} + +static void uloop_handle_processes(void) +{ + struct uloop_process *p, *tmp; + pid_t pid; + int ret; + + do_sigchld = false; + + while (1) { + pid = waitpid(-1, &ret, WNOHANG); + if (pid <= 0) + return; + + list_for_each_entry_safe(p, tmp, &processes, list) { + if (p->pid < pid) + continue; + + if (p->pid > pid) + break; + + uloop_process_delete(p); + p->cb(p, ret); + } + } + +} + static void uloop_handle_sigint(int signo) { uloop_cancelled = true; } +static void uloop_sigchld(int signo) +{ + do_sigchld = true; +} + static void uloop_setup_signals(void) { struct sigaction s; + memset(&s, 0, sizeof(struct sigaction)); s.sa_handler = uloop_handle_sigint; s.sa_flags = 0; sigaction(SIGINT, &s, NULL); + + if (uloop_handle_sigchld) { + s.sa_handler = uloop_sigchld; + sigaction(SIGCHLD, &s, NULL); + } } static int uloop_get_next_timeout(struct timeval *tv) { + struct uloop_timeout *timeout; int diff; - if (!first_timeout) + if (list_empty(&timeouts)) return -1; - diff = tv_diff(&first_timeout->time, tv); + timeout = list_first_entry(&timeouts, struct uloop_timeout, list); + diff = tv_diff(&timeout->time, tv); if (diff < 0) return 0; @@ -361,19 +475,36 @@ static int uloop_get_next_timeout(struct timeval *tv) static void uloop_process_timeouts(struct timeval *tv) { - struct uloop_timeout *timeout; + struct uloop_timeout *t; + + while (!list_empty(&timeouts)) { + t = list_first_entry(&timeouts, struct uloop_timeout, list); - while (first_timeout) { - if (tv_diff(&first_timeout->time, tv) > 0) + if (tv_diff(&t->time, tv) > 0) break; - timeout = first_timeout; - uloop_timeout_cancel(timeout); - if (timeout->cb) - timeout->cb(timeout); + uloop_timeout_cancel(t); + if (t->cb) + t->cb(t); } } +static void uloop_clear_timeouts(void) +{ + struct uloop_timeout *t, *tmp; + + list_for_each_entry_safe(t, tmp, &timeouts, list) + uloop_timeout_cancel(t); +} + +static void uloop_clear_processes(void) +{ + struct uloop_process *p, *tmp; + + list_for_each_entry_safe(p, tmp, &processes, list) + uloop_process_delete(p); +} + void uloop_run(void) { struct timeval tv; @@ -385,6 +516,9 @@ void uloop_run(void) uloop_process_timeouts(&tv); if (uloop_cancelled) break; + + if (do_sigchld) + uloop_handle_processes(); uloop_run_events(uloop_get_next_timeout(&tv)); } } @@ -396,4 +530,7 @@ void uloop_done(void) close(poll_fd); poll_fd = -1; + + uloop_clear_timeouts(); + uloop_clear_processes(); }