elemental-ircd/libcharybdis/poll.c

303 lines
7.2 KiB
C

/*
* ircd-ratbox: A slightly useful ircd.
* s_bsd_poll.c: POSIX poll() compatible network routines.
*
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
* Copyright (C) 1996-2002 Hybrid Development Team
* Copyright (C) 2001 Adrian Chadd <adrian@creative.net.au>
* Copyright (C) 2002-2005 ircd-ratbox development team
*
* 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* $Id: poll.c 3354 2007-04-03 09:21:31Z nenolod $
*/
#include "config.h"
#include "stdinc.h"
#include <sys/poll.h>
#include "libcharybdis.h"
/* I hate linux -- adrian */
#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif
#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif
struct _pollfd_list
{
struct pollfd *pollfds;
int maxindex; /* highest FD number */
int allocated;
};
typedef struct _pollfd_list pollfd_list_t;
pollfd_list_t pollfd_list;
static void poll_update_pollfds(int, short, PF *);
static unsigned long last_count = 0;
static unsigned long empty_count = 0;
/*
* init_netio
*
* This is a needed exported function which will be called to initialise
* the network loop code.
*/
void
init_netio(void)
{
int fd;
int maxconn = comm_get_maxconnections();
pollfd_list.pollfds = calloc(sizeof(struct pollfd), maxconn);
for (fd = 0; fd < maxconn; fd++)
pollfd_list.pollfds[fd].fd = -1;
pollfd_list.maxindex = 0;
pollfd_list.allocated = maxconn;
}
static inline void
resize_poll_array(int fd)
{
int i, old_value = pollfd_list.allocated;
if (fd < pollfd_list.allocated)
return;
pollfd_list.allocated += 1024;
pollfd_list.pollfds = MyRealloc(pollfd_list.pollfds, pollfd_list.allocated * sizeof(struct pollfd));
/* because realloced memory can contain junk, we have to zero it out. */
memset(&pollfd_list.pollfds[old_value+1], 0, sizeof(struct pollfd) * 1024);
for (i = old_value + 1; i <= pollfd_list.allocated; i++)
pollfd_list.pollfds[i].fd = -1;
}
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* Private functions */
/*
* find a spare slot in the fd list. We can optimise this out later!
* -- adrian
*/
static inline int
poll_findslot(void)
{
int i;
for (i = 0; i < pollfd_list.allocated; i++)
{
if(pollfd_list.pollfds[i].fd == -1)
{
/* MATCH!!#$*&$ */
return i;
}
}
s_assert(1 == 0);
/* NOTREACHED */
return -1;
}
/*
* set and clear entries in the pollfds[] array.
*/
static void
poll_update_pollfds(int fd, short event, PF * handler)
{
fde_t *F = comm_locate_fd(fd);
int comm_index;
resize_poll_array(fd);
if(F->comm_index < 0)
F->comm_index = poll_findslot();
comm_index = F->comm_index;
/* Update the events */
if(handler)
{
F->list = FDLIST_IDLECLIENT;
pollfd_list.pollfds[comm_index].events |= event;
pollfd_list.pollfds[comm_index].fd = fd;
/* update maxindex here */
if(comm_index > pollfd_list.maxindex)
pollfd_list.maxindex = comm_index;
}
else
{
if(comm_index >= 0)
{
pollfd_list.pollfds[comm_index].events &= ~event;
if(pollfd_list.pollfds[comm_index].events == 0)
{
pollfd_list.pollfds[comm_index].fd = -1;
pollfd_list.pollfds[comm_index].revents = 0;
F->comm_index = -1;
F->list = FDLIST_NONE;
/* update pollfd_list.maxindex here */
if(comm_index == pollfd_list.maxindex)
while (pollfd_list.maxindex >= 0 &&
pollfd_list.pollfds[pollfd_list.maxindex].fd == -1)
pollfd_list.maxindex--;
}
}
}
}
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* Public functions */
/*
* 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 = comm_locate_fd(fd);
s_assert(fd >= 0);
s_assert(F->flags.open);
if(type & COMM_SELECT_READ)
{
F->read_handler = handler;
F->read_data = client_data;
poll_update_pollfds(fd, POLLRDNORM, handler);
}
if(type & COMM_SELECT_WRITE)
{
F->write_handler = handler;
F->write_data = client_data;
poll_update_pollfds(fd, POLLWRNORM, handler);
}
if(timeout)
F->timeout = CurrentTime + (timeout / 1000);
}
static void
irc_sleep(unsigned long useconds)
{
#ifdef HAVE_NANOSLEEP
struct timespec t;
t.tv_sec = useconds / (unsigned long) 1000000;
t.tv_nsec = (useconds % (unsigned long) 1000000) * 1000;
nanosleep(&t, (struct timespec *) NULL);
#else
struct timeval t;
t.tv_sec = 0;
t.tv_usec = useconds;
select(0, NULL, NULL, NULL, &t);
#endif
return;
}
/* int comm_select_fdlist(unsigned long delay)
* Input: The maximum time to delay.
* Output: Returns -1 on error, 0 on success.
* Side-effects: Deregisters future interest in IO and calls the handlers
* if an event occurs for an FD.
* Comments: 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.
* Called to do the new-style IO, courtesy 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 num;
int fd;
int ci;
unsigned long ndelay;
PF *hdl;
if(last_count > 0)
{
empty_count = 0;
ndelay = 0;
}
else {
ndelay = ++empty_count * 15000 ;
if(ndelay > delay * 1000)
ndelay = delay * 1000;
}
for (;;)
{
/* XXX kill that +1 later ! -- adrian */
if(ndelay > 0)
irc_sleep(ndelay);
last_count = num = poll(pollfd_list.pollfds, pollfd_list.maxindex + 1, 0);
if(num >= 0)
break;
if(ignoreErrno(errno))
continue;
/* error! */
set_time();
return -1;
/* NOTREACHED */
}
/* update current time again, eww.. */
set_time();
if(num == 0)
return 0;
/* XXX we *could* optimise by falling out after doing num fds ... */
for (ci = 0; ci < pollfd_list.maxindex + 1; ci++)
{
fde_t *F;
int revents;
if(((revents = pollfd_list.pollfds[ci].revents) == 0) ||
(pollfd_list.pollfds[ci].fd) == -1)
continue;
fd = pollfd_list.pollfds[ci].fd;
F = comm_locate_fd(fd);
if(revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR))
{
hdl = F->read_handler;
F->read_handler = NULL;
poll_update_pollfds(fd, POLLRDNORM, NULL);
if(hdl)
hdl(fd, F->read_data);
}
if(revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR))
{
hdl = F->write_handler;
F->write_handler = NULL;
poll_update_pollfds(fd, POLLWRNORM, NULL);
if(hdl)
hdl(fd, F->write_data);
}
}
return 0;
}