From 02b9e09173fbc1c481fb7f9aeff7d268f1af99e9 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 22 Mar 2004 21:58:59 +0000 Subject: [PATCH] Get rid of fd_mode_t, use three handler pointers instead. This also makes epoll based fd handling easier. --- fd/fd.c | 54 +++++++++++++--- fd/fd.h | 13 ++-- fd/fd_epoll.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++ fd/fd_epoll.h | 50 +++++++++++++++ 4 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 fd/fd_epoll.c create mode 100644 fd/fd_epoll.h diff --git a/fd/fd.c b/fd/fd.c index 966fe357..b9692054 100644 --- a/fd/fd.c +++ b/fd/fd.c @@ -27,7 +27,7 @@ #include "fd/event.h" #include "fd/fd.h" -static fd_set fd_sets[FD_MODES]; +static fd_set readset, writeset, errorset; static int max_fd; static avl_tree_t *fds; @@ -40,8 +40,9 @@ int fd_compare(struct fd *a, struct fd *b) { bool fd_init(void) { int i; - for(i = 0; i < FD_MODES; i++) - FD_ZERO(&fd_sets[i]); + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); fds = avl_tree_new((avl_compare_t)fd_compare, NULL); @@ -58,7 +59,14 @@ bool fd_add(struct fd *fd) { if(!avl_add(fds, fd)) return false; - FD_SET(fd->fd, &fd_sets[fd->mode]); + if(fd->read) + FD_SET(fd->fd, &readset); + + if(fd->write) + FD_SET(fd->fd, &writeset); + + if(fd->error) + FD_SET(fd->fd, &errorset); if(fd->fd > max_fd) max_fd = fd->fd; @@ -67,7 +75,9 @@ bool fd_add(struct fd *fd) { }; bool fd_del(struct fd *fd) { - FD_CLR(fd->fd, &fd_sets[fd->mode]); + FD_CLR(fd->fd, &readset); + FD_CLR(fd->fd, &writeset); + FD_CLR(fd->fd, &errorset); if(fd->fd >= max_fd) max_fd = ((struct fd *)fds->tail)->fd; @@ -75,20 +85,40 @@ bool fd_del(struct fd *fd) { return avl_del(fds, fd); }; +bool fd_mod(struct fd *fd) { + if(fd->read) + FD_SET(fd->fd, &readset); + else + FD_CLR(fd->fd, &readset); + + if(fd->write) + FD_SET(fd->fd, &writeset); + else + FD_CLR(fd->fd, &writeset); + + if(fd->error) + FD_SET(fd->fd, &errorset); + else + FD_CLR(fd->fd, &errorset); +} + bool fd_run(void) { struct timeval tv; int result; - fd_set fd_cur[FD_MODES]; + fd_set readtmp, writetmp, errortmp; fd_running = true; logger(LOG_INFO, "fd: running"); while(fd_running) { - memcpy(fd_cur, fd_sets, sizeof(fd_cur)); + readtmp = readset; + writetmp = writeset; + errortmp = errorset; + tv = event_timeout(); - result = select(max_fd + 1, &fd_cur[0], &fd_cur[1], &fd_cur[2], tv.tv_sec >= 0 ? &tv : NULL); + result = select(max_fd + 1, &readtmp, &writetmp, &errortmp, tv.tv_sec >= 0 ? &tv : NULL); if(result < 0) { if(errno != EINTR && errno != EAGAIN) { @@ -103,8 +133,12 @@ bool fd_run(void) { struct fd *fd; avl_foreach(fds, fd, { - if(FD_ISSET(fd->fd, &fd_cur[fd->mode])) - fd->handler(fd); + if(fd->read && FD_ISSET(fd->fd, &readtmp)) + fd->read(fd); + if(fd->write && FD_ISSET(fd->fd, &writetmp)) + fd->write(fd); + if(fd->error && FD_ISSET(fd->fd, &errortmp)) + fd->error(fd); }); } else { event_handle(); diff --git a/fd/fd.h b/fd/fd.h index 13714a47..29cad35f 100644 --- a/fd/fd.h +++ b/fd/fd.h @@ -23,21 +23,15 @@ #ifndef __FD_H__ #define __FD_H__ -enum fd_mode { - FD_MODE_READ = 0, - FD_MODE_WRITE, - FD_MODE_EXCEPT, - FD_MODES, -} fd_mode_t; - struct fd; typedef bool (*fd_handler_t)(struct fd *); typedef struct fd { int fd; - enum fd_mode mode; - fd_handler_t handler; + fd_handler_t read; + fd_handler_t write; + fd_handler_t error; void *data; } fd_t; @@ -45,6 +39,7 @@ extern bool fd_init(void); extern bool fd_exit(void); extern bool fd_add(struct fd *fd); extern bool fd_del(struct fd *fd); +extern bool fd_mod(struct fd *fd); extern bool fd_run(void); extern void fd_stop(void); diff --git a/fd/fd_epoll.c b/fd/fd_epoll.c new file mode 100644 index 00000000..43bdc06e --- /dev/null +++ b/fd/fd_epoll.c @@ -0,0 +1,166 @@ +/* + fd_epoll.c -- I/O and event multiplexing using epoll + + Copyright (C) 2003-2004 Guus Sliepen , + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: fd.c 1375 2004-03-22 12:30:39Z guus $ +*/ + +#include "system.h" + +#include "support/avl.h" +#include "support/xalloc.h" +#include "fd/event.h" +#include "fd/fd.h" + +static int epollfd; +static avl_tree_t *fds; + +volatile bool fd_running = false; + +int fd_compare(struct fd *a, struct fd *b) { + return (a->fd - b->fd) ?: (a->mode - b->mode); +}; + +bool fd_init(void) { + int i; + + epollfd = epoll_create(32); + + if(epollfd == -1) { + logger(LOG_ERR, "fd: could not open an epoll file descriptor: %s", strerror(errno)); + return false; + } + + fds = avl_tree_new((avl_compare_t)fd_compare, NULL); + + event_init(); +} + +bool fd_exit(void) { + event_exit(); + + avl_tree_del(fds); + + close(epollfd); +} + +bool fd_add(struct fd *fd) { + if(!avl_add(fds, fd)) + return false; + + fd->event.events = 0; + + if(fd->read) + fd->event.events |= EPOLLIN; + + if(fd->write) + fd->event.events |= EPOLLOUT; + + if(fd->error) + fd->event.events |= EPOLLPRI | EPOLLERR | EPOLLHUP; + + fd->event.data.ptr = fd; + + if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd->fd, &fd->event) == -1) { + logger(LOG_ERR, "fd: failed to add file descriptor: %s", strerror(errno)); + return false; + } + + return true; +}; + +bool fd_del(struct fd *fd) { + if(epoll_ctl(epollfd, EPOLL_CTL_DEL, fd->fd, &fd->event) == -1) { + logger(LOG_ERR, "fd: failed to delete file descriptor: %s", strerror(errno)); + return false; + } + + return avl_del(fds, fd); +}; + +bool fd_mod(struct fd *fd) { + fd->event.events = 0; + + if(fd->read) + fd->event.events |= EPOLLIN; + + if(fd->write) + fd->event.events |= EPOLLOUT; + + if(fd->error) + fd->event.events |= EPOLLPRI | EPOLLERR | EPOLLHUP; + + if(epoll_ctl(epollfd, EPOLL_CTL_MOD, fd->fd, &fd->event) == -1) { + logger(LOG_ERR, "fd: failed to modify file descriptor: %s", strerror(errno)); + return false; + } + + return true; +} + +bool fd_run(void) { + struct timeval tv; + int result; + struct epoll_event *events[10]; + + fd_running = true; + + logger(LOG_INFO, "fd: running"); + + while(fd_running) { + tv = event_timeout(); + + result = epoll_wait(epollfd, events, sizeof events / sizeof *events, tv.tv_sec >= 0 ? tv.tv_sec * 1000 + tv.tv_usec / 1000: -1); + + if(result < 0) { + if(errno != EINTR && errno != EAGAIN) { + logger(LOG_ERR, _("fd: error while waiting for input: %s"), strerror(errno)); + return false; + } + + continue; + } + + if(result) { + struct fd *fd; + + while(result--) { + fd = events[result].data.ptr; + + if(events[result].events & EPOLLIN) + fd->read(fd); + + if(events[result].events & EPOLLOUT) + fd->write(fd); + + if(events[result].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)) + fd->error(fd); + } + } else { + event_handle(); + } + } + + logger(LOG_INFO, "fd: stopping"); + + return true; +} + +void fd_stop(void) { + fd_running = false; +} diff --git a/fd/fd_epoll.h b/fd/fd_epoll.h new file mode 100644 index 00000000..21e39a5b --- /dev/null +++ b/fd/fd_epoll.h @@ -0,0 +1,50 @@ +/* + fd_epoll.h -- I/O and event multiplexing using epoll + + Copyright (C) 2003-2004 Guus Sliepen , + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: fd.h 1375 2004-03-22 12:30:39Z guus $ +*/ + +#ifndef __FD_H__ +#define __FD_H__ + +struct fd; + +typedef bool (*fd_handler_t)(struct fd *); + +typedef struct fd { + int fd; + fd_handler_t read; + fd_handler_t write; + fd_handler_t error; + void *data; + + /* Private */ + + struct epoll_event event; +} fd_t; + +extern bool fd_init(void); +extern bool fd_exit(void); +extern bool fd_add(struct fd *fd); +extern bool fd_del(struct fd *fd); +extern bool fd_mod(struct fd *fd); +extern bool fd_run(void); +extern void fd_stop(void); + +#endif -- 2.20.1