]> jfr.im git - solanum.git/commitdiff
Fix kqueue sometimes dropping updates.
authorJilles Tjoelker <redacted>
Mon, 12 May 2008 16:54:20 +0000 (18:54 +0200)
committerJilles Tjoelker <redacted>
Mon, 12 May 2008 16:54:20 +0000 (18:54 +0200)
(ircd wouldn't read or write anymore to certain clients)
This happens because kqueue.c will often try to add
already closed file descriptors to the kqueue. The kernel
tries to report bad file descriptors in the eventlist; if
the eventlist has no space, processing of the changelist
is silently halted.
The fix:
1. allocate two kqlst things, one for what kqlst currently does
and one as output buffer
this ensures the kevent(2) call in rb_select_kqueue() never
drops updates
2. replace the kevent(2) call in kq_update_events() by a loop
that processes the updates one at a time
that doesn't happen much, and it's the only way to be sure
without also getting events out of the queue we cannot process
at that time
libratbox r25354 (jilles)

libratbox/src/kqueue.c

index 72763c0ea6500472ae1a223e8e04d9bc2db0708c..f047c59bf94f1f2465ae0606ff7ec16ec8f96214 100644 (file)
@@ -34,8 +34,6 @@
 
 #include <sys/event.h>
 
-#define KE_LENGTH      128
-
 /* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
 
 #ifndef EV_SET
@@ -59,6 +57,7 @@ static int kq;
 static struct timespec zero_timespec;
 
 static struct kevent *kqlst;   /* kevent buffer */
+static struct kevent *kqout;   /* kevent output buffer */
 static int kqmax;              /* max structs to buffer */
 static int kqoff;              /* offset into the buffer */
 
@@ -108,14 +107,26 @@ kq_update_events(rb_fde_t * F, short filter, PF * handler)
 
                if(++kqoff == kqmax)
                {
-                       int ret;
-
-                       ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
-                       /* jdc -- someone needs to do error checking... */
-                       if(ret == -1)
+                       int ret, i;
+
+                       /* Add them one at a time, because there may be
+                        * already closed fds in it. The kernel will try
+                        * to report invalid fds in the output; if there
+                        * is no space, it silently stops processing the
+                        * array at that point. We cannot give output space
+                        * because that would also return events we cannot
+                        * process at this point.
+                        */
+                       for (i = 0; i < kqoff; i++)
                        {
-                               rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno));
-                               return;
+                               ret = kevent(kq, kqlst + i, 1, NULL, 0, &zero_timespec);
+                               /* jdc -- someone needs to do error checking... */
+                               if(ret == -1)
+                               {
+                                       rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno));
+                                       kqoff = 0;
+                                       return;
+                               }
                        }
                        kqoff = 0;
                }
@@ -144,6 +155,7 @@ rb_init_netio_kqueue(void)
        }
        kqmax = getdtablesize();
        kqlst = rb_malloc(sizeof(struct kevent) * kqmax);
+       kqout = rb_malloc(sizeof(struct kevent) * kqmax);
        rb_open(kq, RB_FD_UNKNOWN, "kqueue fd");
        zero_timespec.tv_sec = 0;
        zero_timespec.tv_nsec = 0;
@@ -195,7 +207,6 @@ int
 rb_select_kqueue(long delay)
 {
        int num, i;
-       static struct kevent ke[KE_LENGTH];
        struct timespec poll_time;
        struct timespec *pt;
        rb_fde_t *F;
@@ -212,7 +223,7 @@ rb_select_kqueue(long delay)
 
        for (;;)
        {
-               num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, pt);
+               num = kevent(kq, kqlst, kqoff, kqout, kqmax, pt);
                kqoff = 0;
 
                if(num >= 0)
@@ -237,18 +248,18 @@ rb_select_kqueue(long delay)
        {
                PF *hdl = NULL;
 
-               if(ke[i].flags & EV_ERROR)
+               if(kqout[i].flags & EV_ERROR)
                {
-                       errno = ke[i].data;
+                       errno = kqout[i].data;
                        /* XXX error == bad! -- adrian */
                        continue;       /* XXX! */
                }
 
-               switch (ke[i].filter)
+               switch (kqout[i].filter)
                {
 
                case EVFILT_READ:
-                       F = ke[i].udata;
+                       F = kqout[i].udata;
                        if((hdl = F->read_handler) != NULL)
                        {
                                F->read_handler = NULL;
@@ -258,7 +269,7 @@ rb_select_kqueue(long delay)
                        break;
 
                case EVFILT_WRITE:
-                       F = ke[i].udata;
+                       F = kqout[i].udata;
                        if((hdl = F->write_handler) != NULL)
                        {
                                F->write_handler = NULL;
@@ -267,7 +278,7 @@ rb_select_kqueue(long delay)
                        break;
 #if defined(EVFILT_TIMER)
                case EVFILT_TIMER:
-                       rb_run_event(ke[i].udata);
+                       rb_run_event(kqout[i].udata);
                        break;
 #endif
                default: