Fix kqueue sometimes dropping updates.

(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)
This commit is contained in:
Jilles Tjoelker 2008-05-12 18:54:20 +02:00
parent edd8f6417d
commit 3c95b6e72f

View 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;
int ret, i;
ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
/* jdc -- someone needs to do error checking... */
if(ret == -1)
/* 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: