From 98686f18e98acbb992a360d9fbbe5fabd4b4e4a8 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Mon, 12 May 2008 18:54:20 +0200 Subject: [PATCH] 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) --- libratbox/src/kqueue.c | 43 ++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/libratbox/src/kqueue.c b/libratbox/src/kqueue.c index 72763c0..f047c59 100644 --- a/libratbox/src/kqueue.c +++ b/libratbox/src/kqueue.c @@ -34,8 +34,6 @@ #include -#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: