summaryrefslogtreecommitdiffstats
path: root/src/io-epoll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/io-epoll.c')
-rw-r--r--src/io-epoll.c107
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"