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:
parent
edd8f6417d
commit
3c95b6e72f
1 changed files with 27 additions and 16 deletions
|
@ -34,8 +34,6 @@
|
||||||
|
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
|
|
||||||
#define KE_LENGTH 128
|
|
||||||
|
|
||||||
/* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
|
/* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
|
||||||
|
|
||||||
#ifndef EV_SET
|
#ifndef EV_SET
|
||||||
|
@ -59,6 +57,7 @@ static int kq;
|
||||||
static struct timespec zero_timespec;
|
static struct timespec zero_timespec;
|
||||||
|
|
||||||
static struct kevent *kqlst; /* kevent buffer */
|
static struct kevent *kqlst; /* kevent buffer */
|
||||||
|
static struct kevent *kqout; /* kevent output buffer */
|
||||||
static int kqmax; /* max structs to buffer */
|
static int kqmax; /* max structs to buffer */
|
||||||
static int kqoff; /* offset into the buffer */
|
static int kqoff; /* offset into the buffer */
|
||||||
|
|
||||||
|
@ -108,15 +107,27 @@ kq_update_events(rb_fde_t * F, short filter, PF * handler)
|
||||||
|
|
||||||
if(++kqoff == kqmax)
|
if(++kqoff == kqmax)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, i;
|
||||||
|
|
||||||
ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
|
/* 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++)
|
||||||
|
{
|
||||||
|
ret = kevent(kq, kqlst + i, 1, NULL, 0, &zero_timespec);
|
||||||
/* jdc -- someone needs to do error checking... */
|
/* jdc -- someone needs to do error checking... */
|
||||||
if(ret == -1)
|
if(ret == -1)
|
||||||
{
|
{
|
||||||
rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno));
|
rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno));
|
||||||
|
kqoff = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
kqoff = 0;
|
kqoff = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +155,7 @@ rb_init_netio_kqueue(void)
|
||||||
}
|
}
|
||||||
kqmax = getdtablesize();
|
kqmax = getdtablesize();
|
||||||
kqlst = rb_malloc(sizeof(struct kevent) * kqmax);
|
kqlst = rb_malloc(sizeof(struct kevent) * kqmax);
|
||||||
|
kqout = rb_malloc(sizeof(struct kevent) * kqmax);
|
||||||
rb_open(kq, RB_FD_UNKNOWN, "kqueue fd");
|
rb_open(kq, RB_FD_UNKNOWN, "kqueue fd");
|
||||||
zero_timespec.tv_sec = 0;
|
zero_timespec.tv_sec = 0;
|
||||||
zero_timespec.tv_nsec = 0;
|
zero_timespec.tv_nsec = 0;
|
||||||
|
@ -195,7 +207,6 @@ int
|
||||||
rb_select_kqueue(long delay)
|
rb_select_kqueue(long delay)
|
||||||
{
|
{
|
||||||
int num, i;
|
int num, i;
|
||||||
static struct kevent ke[KE_LENGTH];
|
|
||||||
struct timespec poll_time;
|
struct timespec poll_time;
|
||||||
struct timespec *pt;
|
struct timespec *pt;
|
||||||
rb_fde_t *F;
|
rb_fde_t *F;
|
||||||
|
@ -212,7 +223,7 @@ rb_select_kqueue(long delay)
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, pt);
|
num = kevent(kq, kqlst, kqoff, kqout, kqmax, pt);
|
||||||
kqoff = 0;
|
kqoff = 0;
|
||||||
|
|
||||||
if(num >= 0)
|
if(num >= 0)
|
||||||
|
@ -237,18 +248,18 @@ rb_select_kqueue(long delay)
|
||||||
{
|
{
|
||||||
PF *hdl = NULL;
|
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 */
|
/* XXX error == bad! -- adrian */
|
||||||
continue; /* XXX! */
|
continue; /* XXX! */
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ke[i].filter)
|
switch (kqout[i].filter)
|
||||||
{
|
{
|
||||||
|
|
||||||
case EVFILT_READ:
|
case EVFILT_READ:
|
||||||
F = ke[i].udata;
|
F = kqout[i].udata;
|
||||||
if((hdl = F->read_handler) != NULL)
|
if((hdl = F->read_handler) != NULL)
|
||||||
{
|
{
|
||||||
F->read_handler = NULL;
|
F->read_handler = NULL;
|
||||||
|
@ -258,7 +269,7 @@ rb_select_kqueue(long delay)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EVFILT_WRITE:
|
case EVFILT_WRITE:
|
||||||
F = ke[i].udata;
|
F = kqout[i].udata;
|
||||||
if((hdl = F->write_handler) != NULL)
|
if((hdl = F->write_handler) != NULL)
|
||||||
{
|
{
|
||||||
F->write_handler = NULL;
|
F->write_handler = NULL;
|
||||||
|
@ -267,7 +278,7 @@ rb_select_kqueue(long delay)
|
||||||
break;
|
break;
|
||||||
#if defined(EVFILT_TIMER)
|
#if defined(EVFILT_TIMER)
|
||||||
case EVFILT_TIMER:
|
case EVFILT_TIMER:
|
||||||
rb_run_event(ke[i].udata);
|
rb_run_event(kqout[i].udata);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue