/* io-epoll.c - epoll(7) based file descriptor monitoring * * Copyright (C) 2009 Timo Teräs * 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 #include #include #include #include #include struct tf_epoll_data { int epoll_fd; }; static void tf_epoll_main(void *ctx) { struct tf_epoll_data *pd = ctx; struct epoll_event events[64]; struct tf_fd *fd; int r, i; do { r = epoll_wait(pd->epoll_fd, events, array_size(events), 0); if (r == 0) { /* FIXME: yielding is bad */ struct tf_fiber *self = tf_vmach_get_current_fiber(); tf_list_add_tail(&self->queue_node, &self->wakeup_q); if (tf_fiber_schedule() == 0) continue; } for (i = 0; i < r; i++) { fd = (struct tf_fd *) events[i].data.ptr; tf_fiber_wakeup(fd->fiber); } } while (1); close(pd->epoll_fd); } static void *tf_epoll_create(void) { struct tf_epoll_data *d; d = tf_fiber_create(tf_epoll_main, sizeof(struct tf_epoll_data)); if (d == NULL) return NULL; d->epoll_fd = epoll_create1(EPOLL_CLOEXEC); TF_BUG_ON(d->epoll_fd < 0); tf_fiber_run(tf_fiber_get(d)); return d; } static int tf_epoll_fd_created(void *fiber, struct tf_fd *fd) { struct tf_epoll_data *d = fiber; struct epoll_event ev; int r; ev = (struct epoll_event) { .events = EPOLLET, .data.ptr = fd, }; if (fd->flags & TF_FD_READ) ev.events |= EPOLLIN; if (fd->flags & TF_FD_WRITE) ev.events |= EPOLLOUT; r = epoll_ctl(d->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); if (unlikely(r < 0)) { TF_BUG_ON(errno == EEXIST); r = -errno; return r; } return 0; } static int tf_epoll_fd_destroyed(void *fiber, struct tf_fd *fd) { struct tf_epoll_data *d = fiber; if (!(fd->flags & TF_FD_AUTOCLOSE)) epoll_ctl(d->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); return 0; } struct tf_poll_hooks tf_epoll_hooks = { .create = tf_epoll_create, .fd_created = tf_epoll_fd_created, .fd_destroyed = tf_epoll_fd_destroyed, };