/************************************************************************
* IRC - Internet Relay Chat, src/socketengine_epoll.c
* Copyright (C) 2004 David Parton
*
* engine functions for the /dev/epoll socket engine
*
*/
/* $Id: socketengine_epoll.c,v 1.3 2005/07/05 03:17:54 sheik Exp $ */
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "fds.h"
#include <stdint.h>
#include <errno.h>
#include <sys/epoll.h>
#ifdef NEED_EPOLL_DEFS
#include <asm/unistd.h>
_syscall1(int, epoll_create, int, size)
_syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event*, event)
_syscall4(int, epoll_wait, int, epfd, struct epoll_event*, pevents, int, maxevents, int, timeout)
#endif
static int epoll_id = -1, numfds = 0;
static struct epoll_fd
{
int fd;
unsigned int events;
} epoll_fds[MAXCONNECTIONS];
void engine_init()
{
epoll_id = epoll_create(MAXCONNECTIONS);
memset(epoll_fds, 0, sizeof(epoll_fds));
}
void engine_add_fd(int fd)
{
struct epoll_event ev;
if (numfds >= MAXCONNECTIONS)
abort();
ev.events = 0;
ev.data.ptr = &epoll_fds[numfds];
if (epoll_ctl(epoll_id, EPOLL_CTL_ADD, fd, &ev) < 0)
abort();
epoll_fds[numfds].fd = fd;
epoll_fds[numfds].events = 0;
set_fd_internal(fd, (void*)&epoll_fds[numfds]);
++numfds;
}
void engine_del_fd(int fd)
{
struct epoll_event ev;
struct epoll_fd *epfd = (struct epoll_fd*)get_fd_internal(fd);
if (epoll_ctl(epoll_id, EPOLL_CTL_DEL, fd, &ev) < 0)
abort();
if (epfd - epoll_fds != numfds - 1)
{
*epfd = epoll_fds[numfds-1];
set_fd_internal(epfd->fd, (void*)epfd);
/* update the epoll internal pointer as well */
ev.events = epfd->events;
ev.data.ptr = epfd;
if (epoll_ctl(epoll_id, EPOLL_CTL_MOD, epfd->fd, &ev) < 0)
abort();
}
--numfds;
}
void engine_change_fd_state(int fd, unsigned int stateplus)
{
struct epoll_event ev;
struct epoll_fd *epfd = (struct epoll_fd*)get_fd_internal(fd);
ev.events = 0;
ev.data.ptr = epfd;
if (stateplus & FDF_WANTWRITE) ev.events |= EPOLLOUT;
if (stateplus & FDF_WANTREAD) ev.events |= EPOLLIN|EPOLLHUP|EPOLLERR;
if (ev.events != epfd->events)
{
epfd->events = ev.events;
if (epoll_ctl(epoll_id, EPOLL_CTL_MOD, fd, &ev) < 0)
abort();
}
}
#define ENGINE_MAX_EVENTS 512
#define ENGINE_MAX_LOOPS (2 * (MAXCONNECTIONS / 512))
int engine_read_message(time_t delay)
{
struct epoll_event events[ENGINE_MAX_EVENTS], *pevent;
struct epoll_fd* epfd;
int nfds, i, numloops = 0, eventsfull;
unsigned int fdflags;
int fdtype;
void *fdvalue;
aClient *cptr;
aListener *lptr;
do
{
nfds = epoll_wait(epoll_id, events, ENGINE_MAX_EVENTS, delay * 1000);
if (nfds == -1)
{
if (errno == EINTR || errno == EAGAIN)
return -1;
report_error("epoll_wait: %s:%s", &me);
sleep(5);
return -1;
}
eventsfull = nfds == ENGINE_MAX_EVENTS;
if (delay || numloops)
NOW = timeofday = time(NULL);
numloops++;
for (i = 0, pevent = events; i < nfds; i++, pevent++)
{
epfd = pevent->data.ptr;
if (epfd->fd != -1)
{
int rr = pevent->events & (EPOLLIN|EPOLLHUP|EPOLLERR);
int rw = pevent->events & EPOLLOUT;
get_fd_info(epfd->fd, &fdtype, &fdflags, &fdvalue);
switch (fdtype)
{
case FDT_NONE:
break;
case FDT_AUTH:
cptr = (aClient*)fdvalue;
if (rr) read_authports(cptr);
if (rw && cptr->authfd >= 0) send_authports(cptr);
check_client_fd(cptr);
break;
case FDT_LISTENER:
lptr = (aListener*)fdvalue;
if (rr) accept_connection(lptr);
break;
case FDT_RESOLVER:
do_dns_async();
break;
case FDT_CLIENT:
cptr = (aClient*)fdvalue;
readwrite_client(cptr, rr, rw);
break;
case FDT_CALLBACKP:
{
struct fd_callbackp *fdcb = (struct fd_callbackp*)fdvalue;
fdcb->rdf = rr;
fdcb->wrf = rw;
(*fdcb->callback)(fdcb);
break;
}
default:
abort();
}
}
}
} while (eventsfull && numloops < ENGINE_MAX_LOOPS);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1