mio_kqueue.h.patch   [plain text]


--- /tmp/jabberd-2.2.14/mio/mio_kqueue.h	2011-05-31 15:11:38.000000000 -0700
+++ ./jabberd2/mio/mio_kqueue.h	2011-06-22 20:13:00.000000000 -0700
@@ -1,160 +1,151 @@
-/*
- * jabberd - Jabber Open Source Server
- * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
- *                    Ryan Eatmon, Robert Norris, Christof Meerwald
- *
- * 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.
- *
- * 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, MA02111-1307USA
- */
-
-/*
- * MIO backend for kqueue
- * Written by Jiang Hong <jh@6.cn>
- *
- */
-
-#error kqueue is currently unfinished and will eat your CPU alive. If you intend to use it, you need to fix it first.
+/* MIO backend for kqueue() */
 
+#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_EVENT_H
 #include <sys/event.h>
-
-#define KE_INC_SIZE 64
-#define KE_FLAG_READ (1 << 1)
-#define KE_FLAG_WRITE (1 << 2)
-
-#define KE_ENSURE(x) ((x) && (x) != -1)
-
-// _mio_set_events() borrows ideas from squid-2.7 comm_kqueue.c
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 
 #define MIO_FUNCS \
-    static int _mio_poll(mio_t m, int t) \
+ \
+  mio_priv_t dbjdebug; \
+    static mio_fd_t \
+    _mio_alloc_fd(mio_t m, int fd) \
     { \
-		struct timespec timeout; \
-		int r; \
-		timeout.tv_sec = t / 1000; \
-		timeout.tv_nsec = (t % 1000) * 1000000; \
-		r = kevent(MIO(m)->kqueue_fd, MIO(m)->kqlst, MIO(m)->kqoff, MIO(m)->ke, MIO(m)->kqmax, &timeout); \
-		MIO(m)->kqoff = 0; \
-		if (r >= MIO(m)->kqmax) { \
-			MIO(m)->kqmax = MIO(m)->kqmax + KE_INC_SIZE; \
-			MIO(m)->kqlst = realloc(MIO(m)->kqlst, sizeof(struct kevent) * MIO(m)->kqmax); \
-			MIO(m)->ke = realloc(MIO(m)->ke, sizeof(struct kevent) * MIO(m)->kqmax); \
+        struct kevent events[2]; \
+        mio_priv_fd_t priv_fd; \
+        dbjdebug = m; \
+        priv_fd = malloc(sizeof(*priv_fd)); \
+        memset(priv_fd, 0, sizeof(*priv_fd)); \
+        priv_fd->mio_fd.fd = fd; \
+        EV_SET(&events[0], fd, EVFILT_READ, EV_ADD|EV_DISABLE, 0, 0, priv_fd); \
+        EV_SET(&events[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE, 0, 0, priv_fd); \
+        if (kevent(MIO(m)->kq, events, sizeof(events)/sizeof(events[0]), NULL, 0, NULL) == -1) { \
+            mio_debug(ZONE,"error creating kevents on fd %d (%d)", fd, errno); \
 		} \
-		return r; \
+        return (mio_fd_t)priv_fd; \
     } \
+     \
 	static void \
-	_mio_set_events(void* m, int fd, int need_read, int need_write, void* data) \
+    _mio_free_fd(mio_t m, mio_fd_t mfd) \
 	{ \
-	    struct kevent *kep; \
-		int st_new_r = (need_read == 1) ? KE_FLAG_READ : ((need_read == 0) ? 0 : MIO(m)->kqueue_state[fd] & KE_FLAG_READ); \
-		int st_new_w = (need_write == 1) ? KE_FLAG_WRITE : ((need_write == 0) ? 0 : MIO(m)->kqueue_state[fd] & KE_FLAG_WRITE); \
-	    int st_new = st_new_r | st_new_w; \
-	    int st_change; \
-	 \
-	    st_change = MIO(m)->kqueue_state[fd] ^ st_new; \
-	    if (!st_change) return; \
-	 \
-	    if (MIO(m)->kqoff >= MIO(m)->kqmax - 2) { \
-			MIO(m)->kqmax = MIO(m)->kqmax + KE_INC_SIZE; \
-			MIO(m)->kqlst = realloc(MIO(m)->kqlst, sizeof(struct kevent) * MIO(m)->kqmax); \
-			MIO(m)->ke = realloc(MIO(m)->ke, sizeof(struct kevent) * MIO(m)->kqmax); \
-	    } \
-	    kep = MIO(m)->kqlst + MIO(m)->kqoff; \
-	 \
-	    if (st_change & KE_FLAG_READ) { \
-	    	EV_SET(kep, (uintptr_t) fd, EVFILT_READ, KE_ENSURE(need_read) ? EV_ADD : EV_DELETE, 0, 0, data); \
-		    MIO(m)->kqoff++; \
-			kep++; \
+      int i; \
+      /* Unfortunately, the mio_impl.h api is a bit broken in that it \
+       * assumes that we can defer free until the end of the current iteration. \
+       * Unfortunately, with kqueue, a given fd may appear in the iteration loop \
+       * more than once, so we need to both defer free and also clear out any \
+       * other instances of the current fd in the return data.  Fortunately, the \
+       * amount of data we ask for in each call to kevent is small and constant. \
+       */ \
+      for (i = 0; i < MIO(m)->nevents; i++) {  \
+        if (MIO(m)->events[i].udata == mfd) { \
+          MIO(m)->events[i].udata = &MIO(m)->dummy; \
 	    } \
-	    if (st_change & KE_FLAG_WRITE) { \
-		    EV_SET(kep, (uintptr_t) fd, EVFILT_WRITE, KE_ENSURE(need_write) ? EV_ADD : EV_DELETE, 0, 0, data); \
-		    MIO(m)->kqoff++; \
-			kep++; \
 	    } \
-	    MIO(m)->kqueue_state[fd] = st_new; \
+      memset(mfd, 0x5a, sizeof(mio_priv_fd_t)); /* debugging only */ \
+      free(mfd); \
 	} \
-    static mio_fd_t _mio_alloc_fd(mio_t m, int fd)                      \
-    {                                                                   \
-		struct kevent event; \
-        mio_priv_fd_t priv_fd = malloc(sizeof (struct mio_priv_fd_st)); \
-        memset(priv_fd, 0, sizeof (struct mio_priv_fd_st));             \
                                                                         \
-        priv_fd->mio_fd.fd = fd;                                        \
-        priv_fd->events = 0;                                            \
-		_mio_set_events(m, fd, 1, 1, priv_fd); \
-        return (mio_fd_t)priv_fd;                                       \
+    static int \
+    _mio_check(mio_t m, int timeout) \
+    { \
+      struct timespec ts; \
+      int ret; \
+      ts.tv_nsec = 0; \
+      ts.tv_sec = timeout; \
+      ret = kevent(MIO(m)->kq, NULL, 0, MIO(m)->events, sizeof(MIO(m)->events)/sizeof(MIO(m)->events[0]), &ts); \
+      if (ret >= 0) \
+        MIO(m)->nevents = ret; \
+      return ret; \
     }
 	
-
-#define MIO_FD_VARS \
-    uint32_t events;
+#define MIO_FD_VARS
 
 #define MIO_VARS \
-    int defer_free; \
-    int kqueue_fd; \
-	unsigned char *kqueue_state; \
-    struct kevent* kqlst; \
-	struct kevent* ke; \
-	int kqmax; \
-	int kqoff;
+    int kq; \
+    int nevents; \
+    struct kevent events[32]; \
+    struct mio_priv_fd_st dummy;
 
 #define MIO_INIT_VARS(m) \
     do {                                                                \
-        MIO(m)->defer_free = 0;                                         \
-        if ((MIO(m)->kqueue_fd = kqueue()) < 0)               			\
-        {                                                               \
-            mio_debug(ZONE,"unable to initialize kqueue mio");			\
-            free(m);                                                    \
+        MIO(m)->nevents = 0;						\
+        MIO(m)->dummy.type = type_CLOSED; \
+        if ((MIO(m)->kq = kqueue()) == -1) {                            \
+             mio_debug(ZONE,"internal error creating kqueue (%d)", errno); \
             return NULL;                                                \
         }                                                               \
-		MIO(m)->kqmax = KE_INC_SIZE; \
-		MIO(m)->kqlst = malloc(sizeof(struct kevent) * MIO(m)->kqmax); \
-		MIO(m)->ke = malloc(sizeof(struct kevent) * MIO(m)->kqmax); \
-		MIO(m)->kqueue_state = calloc(65536, sizeof(char)); \
-    } while(0)
-#define MIO_FREE_VARS(m) \
-    do { \
-        close(MIO(m)->kqueue_fd); \
-		free(MIO(m)->kqueue_state); \
-		free(MIO(m)->kqlst); \
-		free(MIO(m)->ke); \
     } while(0)
 
+#define MIO_FREE_VARS(m) close(MIO(m)->kq)
+
 #define MIO_ALLOC_FD(m, rfd) _mio_alloc_fd(m,rfd)
-#define MIO_FREE_FD(m, mfd)     free(mfd)
-#define MIO_REMOVE_FD(m, mfd) _mio_set_events(m, mfd->mio_fd.fd, 0, 0, NULL)
-#define MIO_CHECK(m, t)         _mio_poll(m, t)
+#define MIO_CAN_FREE(m)         (MIO(m)->nevents == 0)
+#define MIO_FREE_FD(m, mfd)     _mio_free_fd(m, mfd)
 
-#define MIO_SET_READ(m, mfd) _mio_set_events(m, mfd->mio_fd.fd, 1, 0, NULL)
-#define MIO_SET_WRITE(m, mfd) _mio_set_events(m, mfd->mio_fd.fd, 0, 1, NULL)
-#define MIO_UNSET_READ(m, mfd) _mio_set_events(m, mfd->mio_fd.fd, 0, -1, NULL)
-#define MIO_UNSET_WRITE(m, mfd) _mio_set_events(m, mfd->mio_fd.fd, -1, 0, NULL)
+#define MIO_REMOVE_FD(m, mfd) \
+    do { \
+        struct kevent events[2]; \
+        EV_SET(&events[0], mfd->mio_fd.fd, EVFILT_READ, EV_DELETE, 0, 0, mfd); \
+        EV_SET(&events[1], mfd->mio_fd.fd, EVFILT_WRITE, EV_DELETE, 0, 0, mfd); \
+        if (kevent(MIO(m)->kq, events, sizeof(events)/sizeof(events[0]), NULL, 0, NULL) == -1) { \
+           mio_debug(ZONE,"error deleting kevents on fd %d (%d)", mfd->mio_fd.fd, errno); \
+        } \
+    } while (0)
 
-#define MIO_CAN_READ(m,iter) \
-    (MIO(m)->ke[iter].filter == EVFILT_READ)
+/*
+ * This could be tweaked to be more efficient and only apply filter changes
+ * once every loop, but that can be done if testing shows it to be helpful
+ */
+#define MIO_SET_READ(m, mfd)    \
+    do { \
+        struct kevent changelist; \
+        EV_SET(&changelist, mfd->mio_fd.fd, EVFILT_READ, EV_ENABLE, 0, 0, mfd); \
+        if (kevent(MIO(m)->kq, &changelist, 1, NULL, 0, NULL) == -1) { \
+           mio_debug(ZONE,"error setting kevent EVFILT_READ on fd %d (%d)", mfd->mio_fd.fd, errno); \
+        } \
+    } while (0)
 
-#define MIO_CAN_WRITE(m,iter) \
-    (MIO(m)->ke[iter].filter == EVFILT_WRITE)
+#define MIO_SET_WRITE(m, mfd) \
+    do { \
+        struct kevent changelist; \
+        EV_SET(&changelist, mfd->mio_fd.fd, EVFILT_WRITE, EV_ENABLE, 0, 0, mfd); \
+        if (kevent(MIO(m)->kq, &changelist, 1, NULL, 0, NULL) == -1) { \
+           mio_debug(ZONE,"error setting kevent EVFILT_WRITE on fd %d (%d)", mfd->mio_fd.fd, errno); \
+        } \
+    } while (0)
 
-#define MIO_CAN_FREE(m)         (!MIO(m)->defer_free)
+#define MIO_UNSET_READ(m, mfd)    \
+    do { \
+        struct kevent changelist; \
+        EV_SET(&changelist, mfd->mio_fd.fd, EVFILT_READ, EV_DISABLE, 0, 0, mfd); \
+        if (kevent(MIO(m)->kq, &changelist, 1, NULL, 0, NULL) == -1) { \
+           mio_debug(ZONE,"error setting kevent EVFILT_READ on fd %d (%d)", mfd->mio_fd.fd, errno); \
+        } \
+    } while (0)
+
+#define MIO_UNSET_WRITE(m, mfd) \
+    do { \
+        struct kevent changelist; \
+        EV_SET(&changelist, mfd->mio_fd.fd, EVFILT_WRITE, EV_DISABLE, 0, 0, mfd); \
+        if (kevent(MIO(m)->kq, &changelist, 1, NULL, 0, NULL) == -1) { \
+           mio_debug(ZONE,"error setting kevent EVFILT_WRITE on fd %d (%d)", mfd->mio_fd.fd, errno); \
+        } \
+    } while (0)
 
 #define MIO_INIT_ITERATOR(iter) \
     int iter;
 
 #define MIO_ITERATE_RESULTS(m, retval, iter) \
-    for(MIO(m)->defer_free = 1, iter = 0; (iter < retval) || ((MIO(m)->defer_free = 0)); iter++)
+    for(iter = 0; (iter < retval) || ((MIO(m)->nevents = 0)); iter++)
 
-#define MIO_ITERATOR_FD(m, iter) \
-    (MIO(m)->ke[iter].udata)
+#define MIO_CAN_READ(m, iter)  (MIO((m))->events[(iter)].filter == EVFILT_READ)
+#define MIO_CAN_WRITE(m, iter) (MIO((m))->events[(iter)].filter == EVFILT_WRITE)
+
+#define MIO_ITERATOR_FD(m, iter) ((mio_fd_t)(MIO(m)->events[(iter)].udata))
+
+#define MIO_CHECK(m, t) _mio_check(m, t)
\ No newline at end of file