blobmsg: add blobmsg_realloc_string_buffer()
[project/libubox.git] / uloop.c
diff --git a/uloop.c b/uloop.c
index 88e9c81..f0ccb0c 100644 (file)
--- a/uloop.c
+++ b/uloop.c
@@ -1,24 +1,20 @@
 /*
- *   Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
- *   Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- *   Copyright (C) 2010 Steven Barth <steven@midlink.org>
+ * uloop - event loop implementation
  *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
+ * Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
  *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-
 #include <sys/time.h>
 #include <sys/types.h>
 
@@ -57,6 +53,9 @@ static int cur_fd, cur_nfds;
 
 int uloop_init(void)
 {
+       struct timespec timeout = { 0, 0 };
+       struct kevent ev = {};
+
        if (poll_fd >= 0)
                return 0;
 
@@ -64,6 +63,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;
 }
 
@@ -84,38 +86,52 @@ static uint16_t get_flags(unsigned int flags, unsigned int mask)
 
 static struct kevent events[ULOOP_MAX_EVENTS];
 
-static int register_poll(struct uloop_fd *fd, unsigned int flags)
+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;
+       unsigned int changed;
+       uint16_t kflags;
 
-       changed = fd->kqflags ^ flags;
+       if (flags & ULOOP_EDGE_DEFER)
+               flags &= ~ULOOP_EDGE_TRIGGER;
+
+       changed = flags ^ fd->flags;
        if (changed & ULOOP_EDGE_TRIGGER)
                changed |= flags;
 
        if (changed & ULOOP_READ) {
-               uint16_t kflags = get_flags(flags, ULOOP_READ);
+               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);
+               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, fl, &timeout) == -1))
+       fd->flags = flags;
+       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;
+
+       return register_kevent(fd, flags);
+}
+
 int uloop_fd_delete(struct uloop_fd *sock)
 {
        int i;
@@ -136,12 +152,12 @@ static void uloop_run_events(int timeout)
        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;
@@ -169,6 +185,10 @@ static void uloop_run_events(int timeout)
                        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;
@@ -226,6 +246,9 @@ int uloop_fd_delete(struct uloop_fd *sock)
 {
        int i;
 
+       if (!sock->registered)
+               return 0;
+
        for (i = cur_fd + 1; i < cur_nfds; i++) {
                if (events[i].data.ptr != sock)
                        continue;
@@ -282,6 +305,9 @@ int uloop_fd_add(struct uloop_fd *sock, unsigned int flags)
        unsigned int fl;
        int ret;
 
+       if (!(flags & (ULOOP_READ | ULOOP_WRITE)))
+               return uloop_fd_delete(sock);
+
        if (!sock->registered && !(flags & ULOOP_BLOCKING)) {
                fl = fcntl(sock->fd, F_GETFL, 0);
                fl |= O_NONBLOCK;
@@ -327,6 +353,15 @@ int uloop_timeout_add(struct uloop_timeout *timeout)
        return 0;
 }
 
+static void uloop_gettime(struct timeval *tv)
+{
+       struct timespec ts;
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+       tv->tv_sec = ts.tv_sec;
+       tv->tv_usec = ts.tv_nsec / 1000;
+}
+
 int uloop_timeout_set(struct uloop_timeout *timeout, int msecs)
 {
        struct timeval *time = &timeout->time;
@@ -334,7 +369,7 @@ int uloop_timeout_set(struct uloop_timeout *timeout, int msecs)
        if (timeout->pending)
                uloop_timeout_cancel(timeout);
 
-       gettimeofday(&timeout->time, NULL);
+       uloop_gettime(&timeout->time);
 
        time->tv_sec += msecs / 1000;
        time->tv_usec += (msecs % 1000) * 1000;
@@ -358,6 +393,18 @@ int uloop_timeout_cancel(struct uloop_timeout *timeout)
        return 0;
 }
 
+int uloop_timeout_remaining(struct uloop_timeout *timeout)
+{
+       struct timeval now;
+
+       if (!timeout->pending)
+               return -1;
+
+       uloop_gettime(&now);
+
+       return tv_diff(&timeout->time, &now);
+}
+
 int uloop_process_add(struct uloop_process *p)
 {
        struct uloop_process *tmp;
@@ -497,7 +544,7 @@ void uloop_run(void)
        uloop_setup_signals();
        while(!uloop_cancelled)
        {
-               gettimeofday(&tv, NULL);
+               uloop_gettime(&tv);
                uloop_process_timeouts(&tv);
                if (uloop_cancelled)
                        break;