Hi,

I have rewritten fdpoll-kqueue.c from scratch to reduce the amount of memory used, and to dramatically reduce the CPU used compared to the previous implementation. If you had a high CPU usage with Cherokee in *BSD using kqueue, this is for you.

I've tested it in NetBSD 2.0.2. If anyone wants to try it in FreeBSD and provide feedback will be appreciated.

Enjoy.

Rodrigo
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/* Cherokee
 *
 * Authors:
 *      Alvaro Lopez Ortega <[EMAIL PROTECTED]>
 *      Rodrigo Fernandez-Vizarra <[EMAIL PROTECTED]>
 *
 * Copyright (C) 2001, 2002, 2003, 2004, 2005 Alvaro Lopez Ortega
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include "common-internal.h"
#include "fdpoll-protected.h"

#include <stdio.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>

#define KQUEUE_READ_EVENT  0x1
#define KQUEUE_WRITE_EVENT 0x2


/***********************************************************************/
/* kqueue, kevent: kernel event notification mechanism                 */
/*                                                                     */
/* #include <sys/event.h>                                              */
/* #include <sys/time.h>                                               */
/*                                                                     */
/* int                                                                 */
/* kqueue(void);                                                       */
/*                                                                     */
/* int                                                                 */
/*  kevent(int kq, const struct kevent *changelist, size_t nchanges,   */
/*         struct kevent *eventlist, size_t nevents,                   */
/*         const struct timespec *timeout);                            */
/*                                                                     */
/* EV_SET(&kev, ident, filter, flags, fflags, data, udata);            */
/*                                                                     */
/***********************************************************************/


typedef struct {
	struct cherokee_fdpoll poll;

	int                    kqueue;
	struct kevent          *changelist;
	int                    *fdevents;
	int                    *idx_fd_chlist;
	size_t                 nchanges;
} cherokee_fdpoll_kqueue_t;



static ret_t 
_free (cherokee_fdpoll_kqueue_t *fdp)
{
	close( fdp->kqueue );
	free( fdp->changelist );
	free( fdp->fdevents );
	free( fdp->idx_fd_chlist );
	
	free( fdp );
        return ret_ok;	   
}


static ret_t
_add_change(cherokee_fdpoll_kqueue_t *fdp, int fd, int rw, int change )
{
	int index;
	struct kevent *event;

	index = fdp->idx_fd_chlist[fd];
	if ( index == -1 ) {
		/* fd not in changelist 
		 */
		index = fdp->nchanges;
		if ( index > FDPOLL(fdp)->nfiles) {
			PRINT_ERROR ("ERROR: Maximum number of fd exeeded\n");
			return ret_error;
		}
		fdp->idx_fd_chlist[fd] = index;
		fdp->nchanges++;

	}
	
	event = &fdp->changelist[index];

	memset(event, 0, sizeof(struct kevent));
	event->ident = fd;
	event->flags = change;
	switch (rw) {
	case 0:
		event->filter = EVFILT_READ;
		break;
	case 1:
		event->filter = EVFILT_WRITE;
		break;
	default:
		SHOULDNT_HAPPEN;
        }
	return ret_ok;
}

static ret_t
_add (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw)
{
	int re;

	re = _add_change( fdp, fd, rw, EV_ADD|EV_ENABLE );
	if ( re == ret_ok) {
		FDPOLL(fdp)->npollfds++;
	}

	return re;
}

static ret_t
_del (cherokee_fdpoll_kqueue_t *fdp, int fd)
{
	int re;

	re = _add_change( fdp, fd, 0, EV_DELETE|EV_DISABLE );
	if ( re == ret_ok) {
		FDPOLL(fdp)->npollfds--;
	}

	return re;
}

static int
_watch (cherokee_fdpoll_kqueue_t *fdp, int timeout_msecs)
{
	struct timespec  timeout;
	int              i, re, fd;
	int              n_events;


	timeout.tv_sec  = timeout_msecs/1000L;
	timeout.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L;

	/* Get the events of the file descriptors with
	 * activity
	 */
	memset(fdp->idx_fd_chlist, -1, sizeof(int)*FDPOLL(fdp)->system_nfiles);

	n_events = kevent(fdp->kqueue, 
			  fdp->changelist, 
			  fdp->nchanges,
			  fdp->changelist,
			  FDPOLL(fdp)->nfiles,
			  &timeout);
	fdp->nchanges=0;
	if ( n_events < 0 ) {
		PRINT_ERROR ("ERROR: kevent: %s\n", strerror(errno));
		return 0;
	} else if ( n_events > 0 ) {
		memset(fdp->fdevents, 0, FDPOLL(fdp)->system_nfiles*sizeof(int));
		for ( i = 0; i < n_events; ++i ) {
			if ( fdp->changelist[i].filter == EVFILT_READ ) {
				fdp->fdevents[fdp->changelist[i].ident] = KQUEUE_READ_EVENT;
			} else if (fdp->changelist[i].filter == EVFILT_WRITE) {
				fdp->fdevents[fdp->changelist[i].ident] = KQUEUE_WRITE_EVENT;
			} else {
				SHOULDNT_HAPPEN;
			}
		}
	}
	
	return n_events;
}


static int
_check (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw)
{
	uint32_t events;

	/* Sanity check: is it a wrong fd?
	 */
	if ( fd < 0 ) return -1;

	events = fdp->fdevents[fd];

	switch (rw) {
	case 0:
		events &= KQUEUE_READ_EVENT;
		break;
	case 1:
		events &= KQUEUE_WRITE_EVENT;
		break;
	default:
		SHOULDNT_HAPPEN;
	}


	return events;
}


static ret_t
_reset (cherokee_fdpoll_kqueue_t *fdp, int fd)
{
	return ret_ok;
}


static void
_set_mode (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw)
{

 	if ( ( rw && (fdp->fdevents[fd] == KQUEUE_READ_EVENT ) ) ||
 	     ( (!rw) && (fdp->fdevents[fd] == KQUEUE_WRITE_EVENT) ) ) {
		/* If transitioning from r -> w or from w -> r 
		 * clear any active event on the fd as we are
		 * no longer interested on it.
		 */
		int n_events;
		struct kevent changelist[1];
		struct timespec timeout;
		
		memset(&changelist[0], 0, sizeof(struct kevent));
		changelist[0].ident = fd;
		if ( fdp->fdevents[fd] == KQUEUE_READ_EVENT ) {
			changelist[0].filter = EVFILT_READ;
		} else {
			/* fdp->fdevents[fd] == KQUEUE_WRITE_EVENT
			 */
			changelist[0].filter = EVFILT_WRITE;
		}
		changelist[0].flags = EV_DELETE;

		memset(&timeout, 0, sizeof(struct timespec));
		/* Update the kqueue fd list without sleeping (zeroed timeout)
		 */
		n_events = kevent(fdp->kqueue,
				  &changelist[0],
				  1,
				  NULL,
				  0,
				  &timeout);
		/* Clear previous events if any
		 */
		fdp->fdevents[fd] = 0; 
		if ( n_events < 0 ) {
			PRINT_ERROR("ERROR: fd %d: kevent %s\n", fd, 
				    strerror(errno));
		}
	}


	_add_change( fdp, fd, rw, EV_ADD );
}

ret_t 
fdpoll_kqueue_new (cherokee_fdpoll_t **fdp, int sys_limit, int limit)
{
	cherokee_fdpoll_t *nfd;
	CHEROKEE_NEW_STRUCT (n, fdpoll_kqueue);

	nfd = FDPOLL(n);

	/* Init base class properties
	 */
	nfd->type          = cherokee_poll_kqueue;
	nfd->nfiles        = limit;
	nfd->system_nfiles = sys_limit;
	nfd->npollfds      = 0;

	/* Init base class virtual methods
	 */
	nfd->free          = (fdpoll_func_free_t) _free;
	nfd->add           = (fdpoll_func_add_t) _add;
	nfd->del           = (fdpoll_func_del_t) _del;
	nfd->reset         = (fdpoll_func_reset_t) _reset;
	nfd->set_mode      = (fdpoll_func_set_mode_t) _set_mode;
	nfd->check         = (fdpoll_func_check_t) _check;
	nfd->watch         = (fdpoll_func_watch_t) _watch;	

	/* Init kqueue specific variables
	 */
	n->nchanges        = 0;
	n->changelist      = ( struct kevent *)malloc(sizeof(struct kevent)*
						      nfd->nfiles);
	n->fdevents        = (int *)malloc(sizeof(int) * nfd->system_nfiles);
	n->idx_fd_chlist   = (int *)malloc(sizeof(int) * nfd->system_nfiles);

	if ( (!n->fdevents) ||(!n->changelist) || (!n->idx_fd_chlist) ) {
		_free( n );
		return ret_nomem;
	}

	memset(n->fdevents, 0, sizeof(int)*nfd->system_nfiles);
	memset(n->idx_fd_chlist, 0, sizeof(int)*nfd->system_nfiles);

	if ( (n->kqueue = kqueue()) == -1 ) {
		_free( n );
		return ret_error;
	}

	/* Return the object
	 */
	*fdp = nfd;
	return ret_ok;
}
_______________________________________________
Cherokee mailing list
[email protected]
http://www.alobbs.com/cgi-bin/mailman/listinfo/cherokee

Reply via email to