diff options
Diffstat (limited to 'src/io-epoll.c')
-rw-r--r-- | src/io-epoll.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/src/io-epoll.c b/src/io-epoll.c new file mode 100644 index 0000000..56d0743 --- /dev/null +++ b/src/io-epoll.c @@ -0,0 +1,107 @@ +/* io-epoll.c - epoll(7) based file descriptor monitoring + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/epoll.h> + +#include <libtf/io.h> +#include <libtf/fiber.h> + +#define TF_FD_AUTOCLOSE 1 +#define TF_FD_RESTORE_BLOCKING 2 +#define TF_FD_STREAM_ORIENTED 4 + +static int tf_fd_init(struct tf_fd *fd, int kfd, int flags) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + struct epoll_event ev; + int r; + + ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.data.ptr = fd; + r = epoll_ctl(pd->epoll_fd, EPOLL_CTL_ADD, kfd, &ev); + if (r < 0) { + TF_BUG_ON(errno == EEXIST); + return -errno; + } + + fd->fd = kfd; + fd->flags = flags; + fd->waiting_fiber = NULL; + + return 0; +} + +static void tf_fd_wait(struct tf_fd *fd, int events) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + + TF_BUG_ON(fd->waiting_fiber != NULL); + fd->events = events | EPOLLERR | EPOLLHUP; + fd->waiting_fiber = tf_get_fiber(); + pd->num_waiters++; +} + +static void tf_fd_release(struct tf_fd *fd) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + + fd->waiting_fiber = NULL; + fd->events = 0; + pd->num_waiters--; +} + +void tf_poll_init(void) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + + pd->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + pd->num_waiters = 0; + TF_BUG_ON(pd->epoll_fd < 0); +} + +int tf_poll(tf_mtime_diff_t timeout) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + struct epoll_event events[64]; + struct tf_fd *fd; + int ret = (timeout == 0) ? TF_WAKEUP_TIMEOUT : TF_WAKEUP_FD; + int r, i; + + if (timeout == 0 && pd->num_waiters == 0) + return ret; + + do { + r = epoll_wait(pd->epoll_fd, events, array_size(events), timeout); + for (i = 0; i < r; i++) { + fd = (struct tf_fd *) events[i].data.ptr; + if (likely(fd->events & events[i].events)) + tf_wakeup(fd->waiting_fiber, TF_WAKEUP_FD); + } + if (timeout != 0) + ret = TF_WAKEUP_FD; + timeout = 0; + } while (unlikely(r == array_size(events))); + + return ret; +} + +void tf_poll_close(void) +{ + struct tf_poll_data *pd = &tf_get_scheduler()->poll_data; + + close(pd->epoll_fd); +} + +#include "io-unix.c" |