/************************************************************************
*
* s_bsd_kqueue.c - code implementing a kqueue IO loop
* By Adrian Chadd <adrian@creative.net.au>
*
* Based upon:
*
* IRC - Internet Relay Chat, server/s_bsd.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: s_bsd_kqueue.c,v 1.3 2003/06/14 13:55:52 tr-ircd Exp $
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "fd.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "listener.h"
#include "numeric.h"
#include "packet.h"
#include "resnew.h"
#include "s_auth.h"
#ifdef USE_KQUEUE
#define KE_LENGTH 128
/* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
#ifndef EV_SET
#define EV_SET(kevp, a, b, c, d, e, f) do { \
(kevp)->ident = (a); \
(kevp)->filter = (b); \
(kevp)->flags = (c); \
(kevp)->fflags = (d); \
(kevp)->data = (e); \
(kevp)->udata = (f); \
} while(0)
#endif
static void kq_update_events(int, fdlist_t, short, PF *);
static int kq;
static int hkq;
static struct timespec zero_timespec;
static struct kevent *kqlst; /* kevent buffer */
static int kqmax; /* max structs to buffer */
static int kqoff; /* offset into the buffer */
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* Private functions */
void kq_update_events(int fd, fdlist_t list, short filter, PF * handler)
{
PF *cur_handler;
int kep_flags;
switch (filter) {
case EVFILT_READ:
cur_handler = fd_table[fd].read_handler;
break;
case EVFILT_WRITE:
cur_handler = fd_table[fd].write_handler;
break;
default:
/* XXX bad! -- adrian */
return;
break;
}
if ((cur_handler == NULL && handler != NULL)
|| (cur_handler != NULL && handler == NULL)) {
struct kevent *kep;
kep = kqlst + kqoff;
if (handler != NULL) {
if (filter == EVFILT_WRITE)
kep_flags = (EV_ADD | EV_ONESHOT);
else
kep_flags = EV_ADD;
} else {
kep_flags = EV_DELETE;
}
EV_SET(kep, (uintptr_t) fd, filter, kep_flags, 0, 0, 0);
if (kqoff == kqmax) {
int ret;
int q;
if (list == FDLIST_IRCD)
q = kq;
else if (list == FDLIST_HTTPD)
q = hkq;
else
q = kq;
ret = kevent(q, kqlst, kqoff, NULL, 0, &zero_timespec);
/* jdc -- someone needs to do error checking... */
if (ret == -1) {
perror("kq_update_events(): kevent()");
return;
}
kqoff = 0;
} else {
kqoff++;
}
}
}
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* Public functions */
/*
* init_netio
*
* This is a needed exported function which will be called to initialise
* the network loop code.
*/
void init_netio(void)
{
LogSys.kqueue_netio =
logevent_register("kqueue netio", LOG_ALWAYS, LOG_ERRORLOG, LOG_FATAL, "Could not open kqueue");
kq = kqueue();
if (kq < 0) {
logevent_call(LogSys.kqueue_netio);
exit(115); /* Whee! */
}
hkq = kqueue();
if (hkq < 0) {
logevent_call(LogSys.kqueue_netio);
exit(115); /* Whee! */
}
kqmax = getdtablesize();
kqlst = (struct kevent *) MyMalloc(sizeof(*kqlst) * kqmax);
zero_timespec.tv_sec = 0;
zero_timespec.tv_nsec = 0;
}
/*
* comm_setselect
*
* This is a needed exported function which will be called to register
* and deregister interest in a pending IO state for a given FD.
*/
void
comm_setselect(int fd, fdlist_t list, unsigned int type,
PF * handler, void *client_data, time_t timeout)
{
fde_t *F = &fd_table[fd];
assert(fd >= 0);
assert(F->flags.open);
/* Update the list, even though we're not using it .. */
F->list = list;
if (type & COMM_SELECT_READ) {
kq_update_events(fd, list, EVFILT_READ, handler);
F->read_handler = handler;
F->read_data = client_data;
}
if (type & COMM_SELECT_WRITE) {
kq_update_events(fd, list, EVFILT_WRITE, handler);
F->write_handler = handler;
F->write_data = client_data;
}
if (timeout)
F->timeout = timeofday + (timeout / 1000);
}
/*
* Check all connections for new connections and input data that is to be
* processed. Also check for connections with data queued and whether we can
* write it out.
*/
/*
* comm_select
*
* Called to do the new-style IO, courtesy of of squid (like most of this
* new IO code). This routine handles the stuff we've hidden in
* comm_setselect and fd_table[] and calls callbacks for IO ready
* events.
*/
int comm_select(unsigned long delay, int *callbacks, fdlist_t list)
{
int num, i;
static struct kevent ke[KE_LENGTH];
struct timespec poll_time;
int q;
if (list == FDLIST_IRCD)
q = kq;
else if (list == FDLIST_HTTPD)
q = hkq;
else
q = kq;
do {
/*
* remember we are doing NANOseconds here, not micro/milli. God knows
* why jlemon used a timespec, but hey, he wrote the interface, not I
* -- Adrian
*/
poll_time.tv_sec = 0;
poll_time.tv_nsec = delay * 1000000;
for (;;) {
num = kevent(q, kqlst, kqoff, ke, KE_LENGTH, &poll_time);
kqoff = 0;
if (num >= 0)
break;
if (ignoreErrno(errno))
break;
recheck_clock(NULL);
return COMM_ERROR;
/* NOTREACHED */
}
recheck_clock(NULL);
if (num == 0)
continue;
*callbacks += num;
for (i = 0; i < num; i++) {
int fd = (int) ke[i].ident;
PF *hdl = NULL;
fde_t *F = &fd_table[fd];
if (ke[i].flags & EV_ERROR) {
errno = ke[i].data;
/* XXX error == bad! -- adrian */
continue; /* XXX! */
}
switch (ke[i].filter) {
case EVFILT_READ:
if ((hdl = F->read_handler) != NULL) {
F->read_handler = NULL;
hdl(fd, F->read_data);
}
case EVFILT_WRITE:
if ((hdl = F->write_handler) != NULL) {
F->write_handler = NULL;
hdl(fd, F->write_data);
}
default:
/* Bad! -- adrian */
break;
}
}
return COMM_OK;
} while (0); /* XXX should rip this out! -- adrian */
/* XXX Get here, we broke! */
return 0;
}
#endif /* USE_KQUEUE */
syntax highlighted by Code2HTML, v. 0.9.1